diff options
Diffstat (limited to 'sql')
338 files changed, 30652 insertions, 10046 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 9c9f3919781..c7c4df279e7 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -36,7 +36,7 @@ ELSE() ENDIF() INCLUDE_DIRECTORIES( -${CMAKE_SOURCE_DIR}/include +${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql ${PCRE_INCLUDES} ${ZLIB_INCLUDE_DIR} @@ -77,7 +77,8 @@ ENDIF() SET (SQL_SOURCE ../sql-common/client.c compat56.cc derror.cc des_key_file.cc - discover.cc ../sql-common/errmsg.c field.cc field_conv.cc + discover.cc ../sql-common/errmsg.c + field.cc field_conv.cc field_comp.cc filesort_utils.cc filesort.cc gstream.cc signal_handler.cc @@ -109,7 +110,7 @@ SET (SQL_SOURCE debug_sync.cc sql_repl.cc sql_select.cc sql_show.cc sql_state.c group_by_handler.cc - sql_statistics.cc sql_string.cc + sql_statistics.cc sql_string.cc lex_string.h sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc sql_time.cc tztime.cc unireg.cc item_xmlfunc.cc @@ -121,7 +122,7 @@ SET (SQL_SOURCE rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc sql_connect.cc scheduler.cc sql_partition_admin.cc sql_profile.cc event_parse_data.cc sql_alter.cc - sql_signal.cc rpl_handler.cc mdl.cc sql_admin.cc + sql_signal.cc mdl.cc sql_admin.cc transaction.cc sys_vars.cc sql_truncate.cc datadict.cc sql_reload.cc item_inetfunc.cc @@ -137,15 +138,22 @@ SET (SQL_SOURCE my_apc.cc mf_iocache_encr.cc item_jsonfunc.cc my_json_writer.cc rpl_gtid.cc rpl_parallel.cc + semisync.cc semisync_master.cc semisync_slave.cc + semisync_master_ack_receiver.cc sql_type.cc item_windowfunc.cc sql_window.cc sql_cte.cc + item_vers.cc sql_sequence.cc sql_sequence.h ha_sequence.h + sql_tvc.cc sql_tvc.h + opt_split.cc ${WSREP_SOURCES} table_cache.cc encryption.cc temporary_tables.cc + proxy_protocol.cc ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE} + vtmd.cc ) IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR diff --git a/sql/authors.h b/sql/authors.h index 3a8f5497248..609b77059f4 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -75,6 +75,8 @@ struct show_table_authors_st show_table_authors[]= { "Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" }, { "Ian Gilfillan", "South Africa", "MariaDB documentation"}, { "Federico Razolli", "Italy", "MariaDB documentation Italian translation"}, + { "Vinchen", "Shenzhen, China", "Instant ADD Column for InnoDB, Spider engine optimization, from Tencent Game DBA Team" }, + { "Willhan", "Shenzhen, China", "Big Column Compression, Spider engine optimization, from Tencent Game DBA Team" }, /* People working on MySQL code base (not NDB) */ { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h index 3573c5ceb27..d7c28215631 100644 --- a/sql/bounded_queue.h +++ b/sql/bounded_queue.h @@ -16,9 +16,8 @@ #ifndef BOUNDED_QUEUE_INCLUDED #define BOUNDED_QUEUE_INCLUDED -#include "my_global.h" #include "my_base.h" -#include "my_sys.h" +#include <my_sys.h> #include "queues.h" #include <string.h> diff --git a/sql/compat56.cc b/sql/compat56.cc index 16701b1c4e7..16c25924d6e 100644 --- a/sql/compat56.cc +++ b/sql/compat56.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" +#include "mariadb.h" #include "compat56.h" #include "myisampack.h" #include "my_time.h" diff --git a/sql/create_options.cc b/sql/create_options.cc index 2aab99dc47a..7837beb516f 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -19,6 +19,7 @@ Engine defined options of tables/fields/keys in CREATE/ALTER TABLE. */ +#include "mariadb.h" #include "create_options.h" #include <my_getopt.h> #include "set_var.h" @@ -544,7 +545,7 @@ uint engine_option_value::frm_length() if value.str is NULL, this option is not written to frm (=DEFAULT) */ - return value.str ? 1 + name.length + 2 + value.length : 0; + return value.str ? (uint)(1 + name.length + 2 + value.length) : 0; } @@ -729,7 +730,7 @@ uchar *engine_option_value::frm_read(const uchar *buff, const uchar *buff_end, @retval FALSE OK */ -bool engine_table_options_frm_read(const uchar *buff, uint length, +bool engine_table_options_frm_read(const uchar *buff, size_t length, TABLE_SHARE *share) { const uchar *buff_end= buff + length; diff --git a/sql/create_options.h b/sql/create_options.h index 41e8abcb232..c82cb875743 100644 --- a/sql/create_options.h +++ b/sql/create_options.h @@ -87,7 +87,7 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct, engine_option_value **option_list, ha_create_table_option *rules, bool suppress_warning, MEM_ROOT *root); -bool engine_table_options_frm_read(const uchar *buff, uint length, +bool engine_table_options_frm_read(const uchar *buff, size_t length, TABLE_SHARE *share); engine_option_value *merge_engine_table_options(engine_option_value *source, engine_option_value *changes, diff --git a/sql/datadict.cc b/sql/datadict.cc index 5a4d8bbb8bf..4e18fe06cb6 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "datadict.h" #include "sql_priv.h" #include "sql_class.h" @@ -24,7 +24,7 @@ static int read_string(File file, uchar**to, size_t length) { DBUG_ENTER("read_string"); - my_free(*to); + /* This can't use MY_THREAD_SPECIFIC as it's used on server start */ if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) || mysql_file_read(file, *to, length, MYF(MY_NABP))) { @@ -51,7 +51,7 @@ static int read_string(File file, uchar**to, size_t length) @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise - @retval TABLE_TYPE_UNKNOWN error + @retval TABLE_TYPE_UNKNOWN error - file can't be opened @retval TABLE_TYPE_NORMAL table @retval TABLE_TYPE_SEQUENCE sequence table @retval TABLE_TYPE_VIEW view diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 6b09978d128..58a01a77849 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -15,7 +15,7 @@ /* see include/mysql/service_debug_sync.h for debug sync documentation */ -#include <my_global.h> +#include "mariadb.h" #include "debug_sync.h" #if defined(ENABLED_DEBUG_SYNC) @@ -465,13 +465,13 @@ static int debug_sync_qsort_cmp(const void* arg1, const void* arg2) static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr, int quantity, const char *dsp_name, - uint name_len) + size_t name_len) { st_debug_sync_action *action; int low ; int high ; int mid ; - int diff ; + ssize_t diff ; DBUG_ASSERT(actionarr); DBUG_ASSERT(dsp_name); DBUG_ASSERT(name_len); @@ -1457,7 +1457,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) ER_DEBUG_SYNC_TIMEOUT, ER_THD(thd, ER_DEBUG_SYNC_TIMEOUT)); thd->abort_on_warning= save_abort_on_warning; - DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ABORT();); + DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ASSERT(0);); break; } error= 0; diff --git a/sql/debug_sync.h b/sql/debug_sync.h index 999667b9efc..70d28cb982b 100644 --- a/sql/debug_sync.h +++ b/sql/debug_sync.h @@ -26,8 +26,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> - class THD; #if defined(ENABLED_DEBUG_SYNC) diff --git a/sql/derror.cc b/sql/derror.cc index 5a1bee23f4a..8011f8c4020 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -21,7 +21,7 @@ Read language depeneded messagefile */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "derror.h" @@ -213,8 +213,11 @@ static File open_error_msg_file(const char *file_name, const char *language, O_RDONLY | O_SHARE | O_BINARY, MYF(0))) < 0) goto err; - sql_print_warning("An old style --language or -lc-message-dir value with language specific part detected: %s", lc_messages_dir); - sql_print_warning("Use --lc-messages-dir without language specific part instead."); + if (global_system_variables.log_warnings > 2) + { + sql_print_warning("An old style --language or -lc-message-dir value with language specific part detected: %s", lc_messages_dir); + sql_print_warning("Use --lc-messages-dir without language specific part instead."); + } } error_pos=1; if (mysql_file_read(file, (uchar*) head, 32, MYF(MY_NABP))) diff --git a/sql/derror.h b/sql/derror.h index 9f2aee71c7e..a171a248190 100644 --- a/sql/derror.h +++ b/sql/derror.h @@ -16,8 +16,6 @@ #ifndef DERROR_INCLUDED #define DERROR_INCLUDED -#include "my_global.h" /* uint */ - bool init_errmessage(void); void free_error_messages(); bool read_texts(const char *file_name, const char *language, diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc index ede2e9fa9d4..1f81fb9fd3f 100644 --- a/sql/des_key_file.cc +++ b/sql/des_key_file.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> // HAVE_* +#include "mariadb.h" // HAVE_* #include "sql_priv.h" #include "des_key_file.h" // st_des_keyschedule, st_des_keyblock #include "log.h" // sql_print_error @@ -59,7 +59,7 @@ load_des_key_file(const char *file_name) char *start, *end; char buf[1024], offset; st_des_keyblock keyblock; - uint length; + size_t length; if (!(length=my_b_gets(&io,buf,sizeof(buf)-1))) break; // End of file diff --git a/sql/discover.cc b/sql/discover.cc index 62a0084e2e7..7184cde5e03 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -21,7 +21,7 @@ Functions for discover of frm file from handler */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "discover.h" @@ -136,7 +136,7 @@ int writefrm(const char *path, const char *db, const char *table, } else { - error= mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP)); + error= (int)mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP)); if (!error && !tmp_table && opt_sync_frm) error= mysql_file_sync(file, MYF(MY_WME)) || diff --git a/sql/discover.h b/sql/discover.h index e1508107235..c3127c3bff3 100644 --- a/sql/discover.h +++ b/sql/discover.h @@ -16,8 +16,6 @@ #ifndef DISCOVER_INCLUDED #define DISCOVER_INCLUDED -#include "my_global.h" /* uchar */ - int extension_based_table_discovery(MY_DIR *dirp, const char *ext, handlerton::discovered_list *tl); diff --git a/sql/encryption.cc b/sql/encryption.cc index 52eab262570..3174968f9b3 100644 --- a/sql/encryption.cc +++ b/sql/encryption.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <mysql/plugin_encryption.h> #include "log.h" #include "sql_plugin.h" diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index d42a503e9d2..59aa6bcabc6 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_parse.h" // parse_sql @@ -171,11 +171,11 @@ Event_creation_ctx::load_from_db(THD *thd, */ bool -Event_queue_element_for_exec::init(LEX_CSTRING db, LEX_CSTRING n) +Event_queue_element_for_exec::init(const LEX_CSTRING *db, const LEX_CSTRING *n) { - if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME)))) + if (!(dbname.str= my_strndup(db->str, dbname.length= db->length, MYF(MY_WME)))) return TRUE; - if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME)))) + if (!(name.str= my_strndup(n->str, name.length= n->length, MYF(MY_WME)))) { my_free(const_cast<char*>(dbname.str)); return TRUE; @@ -1196,7 +1196,7 @@ Event_timed::get_create_event(THD *thd, String *buf) buf->append(STRING_WITH_LEN("CREATE ")); append_definer(thd, buf, &definer_user, &definer_host); buf->append(STRING_WITH_LEN("EVENT ")); - append_identifier(thd, buf, name.str, name.length); + append_identifier(thd, buf, &name); if (expression) { @@ -1236,7 +1236,7 @@ Event_timed::get_create_event(THD *thd, String *buf) append_unescaped(buf, comment.str, comment.length); } buf->append(STRING_WITH_LEN(" DO ")); - buf->append(body.str, body.length); + buf->append(&body); DBUG_RETURN(0); } @@ -1266,15 +1266,15 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) sp_sql->length(0); - sp_sql->append(C_STRING_WITH_LEN("CREATE ")); - sp_sql->append(C_STRING_WITH_LEN("PROCEDURE ")); + sp_sql->append(STRING_WITH_LEN("CREATE ")); + sp_sql->append(STRING_WITH_LEN("PROCEDURE ")); /* Let's use the same name as the event name to perhaps produce a better error message in case it is a part of some parse error. We're using append_identifier here to successfully parse events with reserved names. */ - append_identifier(thd, sp_sql, name.str, name.length); + append_identifier(thd, sp_sql, &name); /* The default SQL security of a stored procedure is DEFINER. We @@ -1282,9 +1282,9 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) let's execute the procedure with the invoker rights to save on resets of security contexts. */ - sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER ")); + sp_sql->append(STRING_WITH_LEN("() SQL SECURITY INVOKER ")); - sp_sql->append(body.str, body.length); + sp_sql->append(&body); DBUG_RETURN(thd->is_fatal_error); } @@ -1310,10 +1310,10 @@ Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql) sp_sql->set(buffer.str, buffer.length, system_charset_info); sp_sql->length(0); - sp_sql->append(C_STRING_WITH_LEN("DROP EVENT ")); - append_identifier(thd, sp_sql, dbname.str, dbname.length); + sp_sql->append(STRING_WITH_LEN("DROP EVENT ")); + append_identifier(thd, sp_sql, &dbname); sp_sql->append('.'); - append_identifier(thd, sp_sql, name.str, name.length); + append_identifier(thd, sp_sql, &name); DBUG_RETURN(thd->is_fatal_error); } @@ -1355,7 +1355,7 @@ Event_job_data::execute(THD *thd, bool drop) mysql_change_db will be invoked anyway later, to activate the procedure database before it's executed. */ - thd->set_db(dbname.str, dbname.length); + thd->set_db(&dbname); lex_start(thd); diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index 884bbd7d701..8afed7df4c9 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -37,7 +37,7 @@ public: ~Event_queue_element_for_exec(); bool - init(LEX_CSTRING dbname, LEX_CSTRING name); + init(const LEX_CSTRING *dbname, const LEX_CSTRING *name); LEX_CSTRING dbname; LEX_CSTRING name; diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 20c2384ff04..61b1c2de15d 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" // close_thread_tables @@ -41,38 +41,38 @@ static const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] = { { - { C_STRING_WITH_LEN("db") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("name") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("name") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("body") }, - { C_STRING_WITH_LEN("longblob") }, + { STRING_WITH_LEN("body") }, + { STRING_WITH_LEN("longblob") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("definer") }, - { C_STRING_WITH_LEN("char(") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("definer") }, + { STRING_WITH_LEN("char(") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("execute_at") }, - { C_STRING_WITH_LEN("datetime") }, + { STRING_WITH_LEN("execute_at") }, + { STRING_WITH_LEN("datetime") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("interval_value") }, - { C_STRING_WITH_LEN("int(11)") }, + { STRING_WITH_LEN("interval_value") }, + { STRING_WITH_LEN("int(11)") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("interval_field") }, - { C_STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," + { STRING_WITH_LEN("interval_field") }, + { STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY'," "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR'," "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND'," "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND'," @@ -80,43 +80,43 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] = {NULL, 0} }, { - { C_STRING_WITH_LEN("created") }, - { C_STRING_WITH_LEN("timestamp") }, + { STRING_WITH_LEN("created") }, + { STRING_WITH_LEN("timestamp") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("modified") }, - { C_STRING_WITH_LEN("timestamp") }, + { STRING_WITH_LEN("modified") }, + { STRING_WITH_LEN("timestamp") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("last_executed") }, - { C_STRING_WITH_LEN("datetime") }, + { STRING_WITH_LEN("last_executed") }, + { STRING_WITH_LEN("datetime") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("starts") }, - { C_STRING_WITH_LEN("datetime") }, + { STRING_WITH_LEN("starts") }, + { STRING_WITH_LEN("datetime") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("ends") }, - { C_STRING_WITH_LEN("datetime") }, + { STRING_WITH_LEN("ends") }, + { STRING_WITH_LEN("datetime") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("status") }, - { C_STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") }, + { STRING_WITH_LEN("status") }, + { STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("on_completion") }, - { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") }, + { STRING_WITH_LEN("on_completion") }, + { STRING_WITH_LEN("enum('DROP','PRESERVE')") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("sql_mode") }, - { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," + { STRING_WITH_LEN("sql_mode") }, + { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY'," "'NO_UNSIGNED_SUBTRACTION'," "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," @@ -124,46 +124,49 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] = "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," - "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") }, + "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH'," + "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("comment") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("comment") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("originator") }, - { C_STRING_WITH_LEN("int(10)") }, + { STRING_WITH_LEN("originator") }, + { STRING_WITH_LEN("int(10)") }, {NULL, 0} }, { - { C_STRING_WITH_LEN("time_zone") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("latin1") } + { STRING_WITH_LEN("time_zone") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("latin1") } }, { - { C_STRING_WITH_LEN("character_set_client") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("character_set_client") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("collation_connection") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("collation_connection") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("db_collation") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db_collation") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("body_utf8") }, - { C_STRING_WITH_LEN("longblob") }, + { STRING_WITH_LEN("body_utf8") }, + { STRING_WITH_LEN("longblob") }, { NULL, 0 } } }; +static LEX_CSTRING MYSQL_EVENT_NAME= { STRING_WITH_LEN("event") }; + static const TABLE_FIELD_DEF event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0}; @@ -499,7 +502,7 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, */ do { - ret= read_record_info.read_record(&read_record_info); + ret= read_record_info.read_record(); if (ret == 0) ret= copy_event_to_schema_table(thd, schema_table, event_table); } while (ret == 0); @@ -538,7 +541,7 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table, DBUG_ENTER("Event_db_repository::fill_schema_events"); DBUG_PRINT("info",("db=%s", db? db:"(null)")); - event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ); + event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ); if (open_system_tables_for_read(thd, &event_table, &open_tables_backup)) DBUG_RETURN(TRUE); @@ -600,7 +603,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE_LIST tables; DBUG_ENTER("Event_db_repository::open_event_table"); - tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, lock_type); if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) DBUG_RETURN(TRUE); @@ -1008,7 +1011,7 @@ Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema) if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE)) goto end; - while (!ret && !(read_record_info.read_record(&read_record_info)) ) + while (!ret && !(read_record_info.read_record())) { char *et_field= get_field(thd->mem_root, table->field[field]); @@ -1066,7 +1069,7 @@ Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname, DBUG_PRINT("enter",("thd: %p name: %*s", thd, (int) name->length, name->str)); - event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ); + event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ); /* Reset sql_mode during data dictionary operations. */ thd->variables.sql_mode= 0; @@ -1183,48 +1186,11 @@ Event_db_repository::check_system_tables(THD *thd) { TABLE_LIST tables; int ret= FALSE; - const unsigned int event_priv_column_position= 29; - DBUG_ENTER("Event_db_repository::check_system_tables"); DBUG_PRINT("enter", ("thd: %p", thd)); - /* Check mysql.db */ - tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ); - - if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) - { - ret= 1; - sql_print_error("Cannot open mysql.db"); - } - else - { - if (table_intact.check(tables.table, &mysql_db_table_def)) - ret= 1; - - close_mysql_tables(thd); - } - /* Check mysql.user */ - tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); - - if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) - { - ret= 1; - sql_print_error("Cannot open mysql.user"); - } - else - { - if (tables.table->s->fields < event_priv_column_position || - strncmp(tables.table->field[event_priv_column_position]->field_name.str, - STRING_WITH_LEN("Event_priv"))) - { - sql_print_error("mysql.user has no `Event_priv` column at position %d", - event_priv_column_position); - ret= 1; - } - close_mysql_tables(thd); - } /* Check mysql.event */ - tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ); if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index e203712e229..b2ff80626db 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sp_head.h" diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h index e1aed36aa01..d2e14d74cf8 100644 --- a/sql/event_parse_data.h +++ b/sql/event_parse_data.h @@ -17,7 +17,7 @@ #ifndef _EVENT_PARSE_DATA_H_ #define _EVENT_PARSE_DATA_H_ -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" class Item; class THD; diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 208ad55c5f5..7cc3e50f235 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "event_queue.h" @@ -617,7 +617,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd, top= (Event_queue_element*) queue_top(&queue); - thd->set_current_time(); /* Get current time */ + thd->set_start_time(); /* Get current time */ next_activation_at= top->execute_at; if (next_activation_at > thd->query_start()) @@ -637,7 +637,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd, } if (!(*event_name= new Event_queue_element_for_exec()) || - (*event_name)->init(top->dbname, top->name)) + (*event_name)->init(&top->dbname, &top->name)) { ret= TRUE; break; diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 225a3172dc1..f459fd34aee 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "event_scheduler.h" @@ -49,11 +49,11 @@ Event_db_repository *Event_worker_thread::db_repository; static -const LEX_STRING scheduler_states_names[] = +const LEX_CSTRING scheduler_states_names[] = { - { C_STRING_WITH_LEN("INITIALIZED") }, - { C_STRING_WITH_LEN("RUNNING") }, - { C_STRING_WITH_LEN("STOPPING") } + { STRING_WITH_LEN("INITIALIZED") }, + { STRING_WITH_LEN("RUNNING") }, + { STRING_WITH_LEN("STOPPING") } }; struct scheduler_param { @@ -85,7 +85,7 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE]; String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); prefix.length(0); - prefix.append("Event Scheduler: ["); + prefix.append(STRING_WITH_LEN("Event Scheduler: [")); prefix.append(et->definer.str, et->definer.length, system_charset_info); prefix.append("][", 2); @@ -297,7 +297,6 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) DBUG_ENTER("Event_worker_thread::run"); DBUG_PRINT("info", ("Time is %u, THD: %p", (uint)my_time(0), thd)); - inc_thread_running(); if (res) goto end; @@ -326,7 +325,6 @@ end: event->name.str)); delete event; - dec_thread_running(); deinit_event_thread(thd); DBUG_VOID_RETURN; diff --git a/sql/events.cc b/sql/events.cc index 0830fa7c611..2fbb16861f6 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_parse.h" // check_access @@ -644,7 +644,7 @@ Events::drop_schema_events(THD *thd, const char *db) DBUG_ENTER("Events::drop_schema_events"); DBUG_PRINT("enter", ("dropping events from %s", db)); - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); /* Sic: no check if the scheduler is disabled or system tables @@ -826,12 +826,12 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) */ if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS) { - DBUG_ASSERT(thd->lex->select_lex.db); - if (!is_infoschema_db(thd->lex->select_lex.db) && // There is no events in I_S - check_access(thd, EVENT_ACL, thd->lex->select_lex.db, + DBUG_ASSERT(thd->lex->select_lex.db.str); + if (!is_infoschema_db(&thd->lex->select_lex.db) && // There is no events in I_S + check_access(thd, EVENT_ACL, thd->lex->select_lex.db.str, NULL, NULL, 0, 0)) DBUG_RETURN(1); - db= normalize_db_name(thd->lex->select_lex.db, db_tmp, sizeof(db_tmp)); + db= normalize_db_name(thd->lex->select_lex.db.str, db_tmp, sizeof(db_tmp)); } ret= db_repository->fill_schema_events(thd, tables, db); @@ -1175,7 +1175,7 @@ Events::load_events_from_db(THD *thd) DBUG_RETURN(TRUE); } - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { Event_queue_element *et; bool created, dropped; @@ -1252,7 +1252,7 @@ int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len) if (create_query_string(thd, &log_query)) { WSREP_WARN("events create string failed: schema: %s, query: %s", - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); return 1; } return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len); diff --git a/sql/field.cc b/sql/field.cc index d05b08fde74..7c88f230734 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -27,7 +27,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "rpl_rli.h" // Pull in Relay_log_info @@ -51,7 +51,7 @@ *****************************************************************************/ static const char *zero_timestamp="0000-00-00 00:00:00.000000"; -LEX_CSTRING temp_lex_str= {"temp", 4}; +LEX_CSTRING temp_lex_str= {STRING_WITH_LEN("temp")}; uchar Field_null::null[1]={1}; const char field_separator=','; @@ -1228,7 +1228,7 @@ bool Field::can_optimize_range(const Item_bool_func *cond, } -int Field::store_hex_hybrid(const char *str, uint length) +int Field::store_hex_hybrid(const char *str, size_t length) { DBUG_ASSERT(result_type() != STRING_RESULT); ulonglong nr; @@ -1467,8 +1467,7 @@ Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd, int Field_num::check_edom_and_important_data_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, - uint length, + const char *str, size_t length, const char *end) { /* Test if we get an empty string or garbage */ @@ -1490,7 +1489,7 @@ int Field_num::check_edom_and_important_data_truncation(const char *type, int Field_num::check_edom_and_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, uint length, + const char *str, size_t length, const char *end) { int rc= check_edom_and_important_data_truncation(type, edom, @@ -1524,7 +1523,7 @@ int Field_num::check_edom_and_truncation(const char *type, bool edom, 1 error */ -bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, +bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len, longlong *rnd, ulonglong unsigned_max, longlong signed_min, longlong signed_max) { @@ -1557,7 +1556,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, goto out_of_range; } } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) return 1; return 0; @@ -1568,7 +1567,7 @@ out_of_range: } -double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, +double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs, int *error) { char *end; @@ -1578,7 +1577,7 @@ double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); *error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_edom_and_truncation("double", str == end, cs, str, length, end)) *error= 1; @@ -1642,7 +1641,8 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg) - :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), + :ptr(ptr_arg), invisible(VISIBLE), + null_ptr(null_ptr_arg), table(0), orig_table(0), table_name(0), field_name(*field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), @@ -1753,7 +1753,7 @@ bool Field::compatible_field_size(uint field_metadata, } -int Field::store(const char *to, uint length, CHARSET_INFO *cs, +int Field::store(const char *to, size_t length, CHARSET_INFO *cs, enum_check_fields check_level) { int res; @@ -1912,7 +1912,7 @@ void Field::make_field(Send_field *field) if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; - if (orig_table->pos_in_table_list && + if (orig_table->pos_in_table_list && orig_table->pos_in_table_list->schema_table) field->org_table_name= (orig_table->pos_in_table_list-> schema_table->table_name); @@ -2040,6 +2040,48 @@ bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) } +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + DBUG_ASSERT(ltime); + if (!table || !table->s) + return true; + DBUG_ASSERT(table->versioned(VERS_TRX_ID) || + (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY)); + if (!trx_id) + return true; + + THD *thd= get_thd(); + DBUG_ASSERT(thd); + if (trx_id == ULONGLONG_MAX) + { + thd->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE); + ltime->second_part= TIME_MAX_SECOND_PART; + return false; + } + if (cached == trx_id) + { + *ltime= cache; + return false; + } + + TR_table trt(thd); + bool found= trt.query(trx_id); + if (found) + { + trt[TR_table::FLD_COMMIT_TS]->get_date(&cache, fuzzydate); + *ltime= cache; + cached= trx_id; + return false; + } + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_NO_TRX_ID, ER_THD(thd, ER_VERS_NO_TRX_ID), + (longlong) trx_id); + return true; +} + + Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const LEX_CSTRING *field_name_arg, @@ -2232,8 +2274,11 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, */ tmp->unireg_check= Field::NONE; tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | - ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); + ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG | + VERS_SYS_START_FLAG | VERS_SYS_END_FLAG | + VERS_UPDATE_UNVERSIONED_FLAG); tmp->reset_fields(); + tmp->invisible= VISIBLE; return tmp; } @@ -2420,7 +2465,7 @@ void Field_decimal::overflow(bool negative) } -int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) +int Field_decimal::store(const char *from_arg, size_t len, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -2564,7 +2609,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (get_thd()->count_cuted_fields) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; @@ -2734,7 +2779,8 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (get_thd()->count_cuted_fields && !is_cuted_fields_incr) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -2800,7 +2846,6 @@ int Field_decimal::store(double nr) return 1; } - reg4 uint i; size_t length; uchar fyllchar,*to; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; @@ -2816,7 +2861,7 @@ int Field_decimal::store(double nr) else { to=ptr; - for (i=field_length-length ; i-- > 0 ;) + for (size_t i=field_length-length ; i-- > 0 ;) *to++ = fyllchar; memcpy(to,buff,length); return 0; @@ -3102,7 +3147,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) } -int Field_new_decimal::store(const char *from, uint length, +int Field_new_decimal::store(const char *from, size_t length, CHARSET_INFO *charset_arg) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; @@ -3127,7 +3172,7 @@ int Field_new_decimal::store(const char *from, uint length, DBUG_RETURN(1); } - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (check_edom_and_important_data_truncation("decimal", err && err != E_DEC_TRUNCATED, @@ -3172,7 +3217,7 @@ int Field_new_decimal::store(const char *from, uint length, - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2) Also, we send a note if a string had some trailing spaces: '1.12 ' */ - if (thd->count_cuted_fields && + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && (err == E_DEC_TRUNCATED || err2 == E_DEC_TRUNCATED || end < from + length)) @@ -3309,7 +3354,7 @@ int Field_new_decimal::cmp(const uchar *a,const uchar*b) void Field_new_decimal::sort_string(uchar *buff, - uint length __attribute__((unused))) + uint) { memcpy(buff, ptr, bin_size); } @@ -3335,7 +3380,7 @@ void Field_new_decimal::sql_type(String &str) const @returns number of bytes written to metadata_ptr */ -int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr) +int Field_new_decimal::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= precision; *(metadata_ptr + 1)= decimals(); @@ -3493,7 +3538,7 @@ int Field_num::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg) ** tiny int ****************************************************************************/ -int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_tiny::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error; @@ -3669,7 +3714,7 @@ void Field_tiny::sql_type(String &res) const Field type short int (2 byte) ****************************************************************************/ -int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_short::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; @@ -3858,7 +3903,7 @@ void Field_short::sql_type(String &res) const Field type medium int (3 byte) ****************************************************************************/ -int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_medium::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; @@ -4049,7 +4094,7 @@ void Field_medium::sql_type(String &res) const ** long int ****************************************************************************/ -int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_long::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long store_tmp; @@ -4177,17 +4222,17 @@ String *Field_long::val_str(String *val_buffer, { ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_numeric; - uint length; - uint mlength=MY_MAX(field_length+1,12*cs->mbmaxlen); + size_t length; + size_t mlength=MY_MAX(field_length+1,12*cs->mbmaxlen); val_buffer->alloc(mlength); char *to=(char*) val_buffer->ptr(); int32 j; j=sint4korr(ptr); if (unsigned_flag) - length=cs->cset->long10_to_str(cs,to,mlength, 10,(long) (uint32)j); + length=cs->cset->long10_to_str(cs,to,mlength, 10,(uint32) j); else - length=cs->cset->long10_to_str(cs,to,mlength,-10,(long) j); + length=cs->cset->long10_to_str(cs,to,mlength,-10,j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); @@ -4236,7 +4281,7 @@ void Field_long::sql_type(String &res) const Field type longlong int (8 bytes) ****************************************************************************/ -int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; @@ -4249,7 +4294,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) error= 1; else @@ -4383,6 +4428,26 @@ void Field_longlong::sql_type(String &res) const add_zerofill_and_unsigned(res); } +void Field_longlong::set_max() +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + set_notnull(); + int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); +} + +bool Field_longlong::is_max() +{ + ASSERT_COLUMN_MARKED_FOR_READ; + if (unsigned_flag) + { + ulonglong j; + j= uint8korr(ptr); + return j == ULONGLONG_MAX; + } + longlong j; + j= sint8korr(ptr); + return j == LONGLONG_MAX; +} /* Floating-point numbers @@ -4392,7 +4457,7 @@ void Field_longlong::sql_type(String &res) const single precision float ****************************************************************************/ -int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; Field_float::store(get_double(from, len, cs, &error)); @@ -4544,7 +4609,7 @@ bool Field_float::send_binary(Protocol *protocol) @returns number of bytes written to metadata_ptr */ -int Field_float::do_save_field_metadata(uchar *metadata_ptr) +int Field_float::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -4571,7 +4636,7 @@ void Field_float::sql_type(String &res) const double precision floating point numbers ****************************************************************************/ -int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; Field_double::store(get_double(from, len, cs, &error)); @@ -4861,7 +4926,7 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused))) @returns number of bytes written to metadata_ptr */ -int Field_double::do_save_field_metadata(uchar *metadata_ptr) +int Field_double::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= pack_length(); return 1; @@ -5031,7 +5096,7 @@ int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) } -int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) { MYSQL_TIME l_time; MYSQL_TIME_STATUS status; @@ -5320,36 +5385,6 @@ static longlong read_lowendian(const uchar *from, uint bytes) } } -static void store_bigendian(ulonglong num, uchar *to, uint bytes) -{ - switch(bytes) { - case 1: mi_int1store(to, num); break; - case 2: mi_int2store(to, num); break; - case 3: mi_int3store(to, num); break; - case 4: mi_int4store(to, num); break; - case 5: mi_int5store(to, num); break; - case 6: mi_int6store(to, num); break; - case 7: mi_int7store(to, num); break; - case 8: mi_int8store(to, num); break; - default: DBUG_ASSERT(0); - } -} - -static longlong read_bigendian(const uchar *from, uint bytes) -{ - switch(bytes) { - case 1: return mi_uint1korr(from); - case 2: return mi_uint2korr(from); - case 3: return mi_uint3korr(from); - case 4: return mi_uint4korr(from); - case 5: return mi_uint5korr(from); - case 6: return mi_uint6korr(from); - case 7: return mi_uint7korr(from); - case 8: return mi_sint8korr(from); - default: DBUG_ASSERT(0); return 0; - } -} - void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) { mi_int4store(ptr, timestamp); @@ -5455,6 +5490,27 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) my_timestamp_to_binary(&tm, ptr, dec); } +void Field_timestampf::set_max() +{ + DBUG_ENTER("Field_timestampf::set_max"); + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS); + + set_notnull(); + mi_int4store(ptr, TIMESTAMP_MAX_VALUE); + mi_int3store(ptr + 4, TIME_MAX_SECOND_PART); + + DBUG_VOID_RETURN; +} + +bool Field_timestampf::is_max() +{ + DBUG_ENTER("Field_timestampf::is_max"); + ASSERT_COLUMN_MARKED_FOR_READ; + + DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE && + mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART); +} my_time_t Field_timestampf::get_timestamp(const uchar *pos, ulong *sec_part) const @@ -5541,7 +5597,7 @@ int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime, } -int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs) +int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs) { MYSQL_TIME ltime; MYSQL_TIME_STATUS status; @@ -5737,7 +5793,7 @@ void Field_time::store_TIME(const MYSQL_TIME *ltime) int3store(ptr,tmp); } -int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) +int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) { MYSQL_TIME ltime; MYSQL_TIME_STATUS status; @@ -6176,7 +6232,7 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) ** Can handle 2 byte or 4 byte years! ****************************************************************************/ -int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) +int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char *end; @@ -6190,7 +6246,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && (error= check_int(cs, from, len, end, error))) { if (error == 1) /* empty or incorrect string */ @@ -6898,7 +6954,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, bool count_spaces) { THD *thd= get_thd(); - if ((pstr < end) && thd->count_cuted_fields) + if ((pstr < end) && thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (test_if_important_data(field_charset, pstr, end)) { @@ -6920,7 +6976,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, /* Copy a string and fill with space */ -int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; @@ -6944,6 +7000,20 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) } +int Field_str::store(longlong nr, bool unsigned_val) +{ + char buff[64]; + uint length; + length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, + buff, + sizeof(buff), + (unsigned_val ? 10: + -10), + nr); + return store(buff, length, field_charset); +} + + /** Store double value in Field_string or Field_varstring. @@ -6956,7 +7026,8 @@ int Field_str::store(double nr) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; - uint local_char_length= field_length / charset()->mbmaxlen; + uint local_char_length= MY_MIN(sizeof(buff), + field_length / field_charset->mbmaxlen); size_t length= 0; my_bool error= (local_char_length == 0); @@ -6971,7 +7042,7 @@ int Field_str::store(double nr) else set_warning(WARN_DATA_TRUNCATED, 1); } - return store(buff, length, &my_charset_numeric); + return store(buff, (uint)length, &my_charset_numeric); } uint Field::is_equal(Create_field *new_field) @@ -6988,17 +7059,6 @@ uint Field_str::is_equal(Create_field *new_field) } -int Field_string::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - int l; - CHARSET_INFO *cs=charset(); - l= (cs->cset->longlong10_to_str)(cs,buff,sizeof(buff), - unsigned_val ? 10 : -10, nr); - return Field_string::store(buff,(uint)l,cs); -} - - int Field_longstr::store_decimal(const my_decimal *d) { char buff[DECIMAL_MAX_STR_LENGTH+1]; @@ -7114,7 +7174,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), ASSERT_COLUMN_MARKED_FOR_READ; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(!table || table->in_use == current_thd); - uint length; + size_t length; if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH) length= my_charpos(field_charset, ptr, ptr + field_length, @@ -7177,11 +7237,11 @@ Field_string::compatible_field_size(uint field_metadata, int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) { - uint a_len, b_len; + size_t a_len, b_len; if (field_charset->mbmaxlen != 1) { - uint char_len= field_length/field_charset->mbmaxlen; + size_t char_len= field_length/field_charset->mbmaxlen; a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len); b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len); } @@ -7199,7 +7259,7 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) void Field_string::sort_string(uchar *to,uint length) { - uint tmp __attribute__((unused))= + IF_DBUG(size_t tmp= ,) field_charset->coll->strnxfrm(field_charset, to, length, char_length() * @@ -7215,7 +7275,7 @@ void Field_string::sql_type(String &res) const { THD *thd= table->in_use; CHARSET_INFO *cs=res.charset(); - ulong length; + size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", @@ -7232,9 +7292,9 @@ void Field_string::sql_type(String &res) const uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) { - uint length= MY_MIN(field_length,max_length); - uint local_char_length= max_length/field_charset->mbmaxlen; - DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name.str, + size_t length= MY_MIN(field_length,max_length); + size_t local_char_length= max_length/field_charset->mbmaxlen; + DBUG_PRINT("debug", ("Packing field '%s' - length: %zu ", field_name.str, length)); if (length > local_char_length) @@ -7278,7 +7338,7 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) the master. @note For information about how the length is packed, see @c - Field_string::do_save_field_metadata + Field_string::save_field_metadata @param to Destination of the data @param from Source of the data @@ -7361,7 +7421,7 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end, @returns number of bytes written to metadata_ptr */ -int Field_string::do_save_field_metadata(uchar *metadata_ptr) +int Field_string::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length < 1024); DBUG_ASSERT((real_type() & 0xF0) == 0xF0); @@ -7389,14 +7449,14 @@ uint Field_string::max_packed_col_length(uint max_length) uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg) { - uint bytes = my_charpos(field_charset, (char*) ptr, + size_t bytes = my_charpos(field_charset, (char*) ptr, (char*) ptr + field_length, length / field_charset->mbmaxlen); memcpy(buff, ptr, bytes); if (bytes < length) field_charset->cset->fill(field_charset, (char*) buff + bytes, length - bytes, field_charset->pad_char); - return bytes; + return (uint)bytes; } @@ -7458,14 +7518,14 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16; @returns number of bytes written to metadata_ptr */ -int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) +int Field_varstring::save_field_metadata(uchar *metadata_ptr) { DBUG_ASSERT(field_length <= 65535); int2store((char*)metadata_ptr, field_length); return 2; } -int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; @@ -7485,20 +7545,6 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) } -int Field_varstring::store(longlong nr, bool unsigned_val) -{ - char buff[64]; - uint length; - length= (uint) (field_charset->cset->longlong10_to_str)(field_charset, - buff, - sizeof(buff), - (unsigned_val ? 10: - -10), - nr); - return Field_varstring::store(buff, length, field_charset); -} - - double Field_varstring::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -7580,8 +7626,8 @@ int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr, int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) { - uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - uint local_char_length= max_key_length / field_charset->mbmaxlen; + size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + size_t local_char_length= max_key_length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, ptr + length_bytes, ptr + length_bytes + length, local_char_length); @@ -7615,26 +7661,29 @@ int Field_varstring::key_cmp(const uchar *a,const uchar *b) void Field_varstring::sort_string(uchar *to,uint length) { - uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + String buf; + + val_str(&buf, &buf); if (field_charset == &my_charset_bin) { /* Store length last in high-byte order to sort longer strings first */ if (length_bytes == 1) - to[length-1]= tot_length; + to[length - 1]= buf.length(); else - mi_int2store(to+length-2, tot_length); + mi_int2store(to + length - 2, buf.length()); length-= length_bytes; } - - tot_length= field_charset->coll->strnxfrm(field_charset, - to, length, - char_length() * - field_charset->strxfrm_multiply, - ptr + length_bytes, tot_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(tot_length == length); + +#ifndef DBUG_OFF + size_t rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, + char_length() * field_charset->strxfrm_multiply, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); } @@ -7650,16 +7699,22 @@ enum ha_base_keytype Field_varstring::key_type() const } +/* + Compressed columns need one extra byte to store the compression method. + This byte is invisible to the end user, but not for the storage engine. +*/ + void Field_varstring::sql_type(String &res) const { THD *thd= table->in_use; CHARSET_INFO *cs=res.charset(); - ulong length; + size_t length; length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(), "%s(%d)", (has_charset() ? "varchar" : "varbinary"), - (int) field_length / charset()->mbmaxlen); + (int) field_length / charset()->mbmaxlen - + MY_TEST(compression_method())); res.length(length); if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_charset() && (charset()->state & MY_CS_BINSORT)) @@ -7761,32 +7816,36 @@ uint Field_varstring::max_packed_col_length(uint max_length) uint Field_varstring::get_key_image(uchar *buff, uint length, imagetype type_arg) { - uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - uint local_char_length= length / field_charset->mbmaxlen; - uchar *pos= ptr+length_bytes; - local_char_length= my_charpos(field_charset, pos, pos + f_length, - local_char_length); - set_if_smaller(f_length, local_char_length); + String val; + uint local_char_length; + my_bitmap_map *old_map; + + old_map= dbug_tmp_use_all_columns(table, table->read_set); + val_str(&val, &val); + dbug_tmp_restore_column_map(table->read_set, old_map); + + local_char_length= val.charpos(length / field_charset->mbmaxlen); + if (local_char_length < val.length()) + val.length(local_char_length); /* Key is always stored with 2 bytes */ - int2store(buff,f_length); - memcpy(buff+HA_KEY_BLOB_LENGTH, pos, f_length); - if (f_length < length) + int2store(buff, val.length()); + memcpy(buff + HA_KEY_BLOB_LENGTH, val.ptr(), val.length()); + if (val.length() < length) { /* Must clear this as we do a memcmp in opt_range.cc to detect identical keys */ - bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length)); + memset(buff + HA_KEY_BLOB_LENGTH + val.length(), 0, length - val.length()); } - return HA_KEY_BLOB_LENGTH+f_length; + return HA_KEY_BLOB_LENGTH + val.length(); } void Field_varstring::set_key_image(const uchar *buff,uint length) { length= uint2korr(buff); // Real length is here - (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length, - field_charset); + (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset); } @@ -7843,13 +7902,14 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_varstring::is_equal(Create_field *new_field) { if (new_field->type_handler() == type_handler() && - new_field->charset == field_charset) + new_field->charset == field_charset && + !new_field->compression_method() == !compression_method()) { - if (new_field->length == max_display_length()) + if (new_field->length == field_length) return IS_EQUAL_YES; - if (new_field->length > max_display_length() && - ((new_field->length <= 255 && max_display_length() <= 255) || - (new_field->length > 255 && max_display_length() > 255))) + if (new_field->length > field_length && + ((new_field->length <= 255 && field_length <= 255) || + (new_field->length > 255 && field_length > 255))) return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length } return IS_EQUAL_NO; @@ -7871,6 +7931,201 @@ void Field_varstring::hash(ulong *nr, ulong *nr2) } +/** + Compress field + + @param[out] to destination buffer for compressed data + @param[in,out] to_length in: size of to, out: compressed data length + @param[in] from data to compress + @param[in] length from length + @param[in] cs from character set + + In worst case (no compression performed) storage requirement is increased by + 1 byte to store header. If it exceeds field length, normal data truncation is + performed. + + Generic compressed header format (1 byte): + + Bits 1-4: method specific bits + Bits 5-8: compression method + + If compression method is 0 then header is immediately followed by + uncompressed data. + + If compression method is zlib: + + Bits 1-3: number of bytes occupied by original data length + Bits 4: true if zlib wrapper not present + Bits 5-8: store 8 (zlib) + + Header is immediately followed by original data length, + followed by compressed data. +*/ + +int Field_longstr::compress(char *to, uint *to_length, + const char *from, uint length, + CHARSET_INFO *cs) +{ + THD *thd= get_thd(); + char *buf= 0; + int rc= 0; + + if (length == 0) + { + *to_length= 0; + return 0; + } + + if (String::needs_conversion_on_storage(length, cs, field_charset) || + *to_length <= length) + { + String_copier copier; + const char *end= from + length; + + if (!(buf= (char*) my_malloc(*to_length - 1, MYF(MY_WME)))) + { + *to_length= 0; + return -1; + } + + length= copier.well_formed_copy(field_charset, buf, *to_length - 1, + cs, from, length, + (*to_length - 1) / field_charset->mbmaxlen); + rc= check_conversion_status(&copier, end, cs, true); + from= buf; + DBUG_ASSERT(length > 0); + } + + if (length >= thd->variables.column_compression_threshold && + (*to_length= compression_method()->compress(thd, to, from, length))) + status_var_increment(thd->status_var.column_compressions); + else + { + /* Store uncompressed */ + to[0]= 0; + memcpy(to + 1, from, length); + *to_length= length + 1; + } + + if (buf) + my_free(buf); + return rc; +} + + +/* + Memory is allocated only when original data was actually compressed. + Otherwise val_ptr points at data located immediately after header. + + Data can be stored uncompressed if data was shorter than threshold + or compressed data was longer than original data. +*/ + +String *Field_longstr::uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length) +{ + if (from_length) + { + uchar method= (*from & 0xF0) >> 4; + + /* Uncompressed data */ + if (!method) + { + val_ptr->set((const char*) from + 1, from_length - 1, field_charset); + return val_ptr; + } + + if (compression_methods[method].uncompress) + { + if (!compression_methods[method].uncompress(val_buffer, from, from_length, + field_length)) + { + val_buffer->set_charset(field_charset); + status_var_increment(get_thd()->status_var.column_decompressions); + return val_buffer; + } + } + } + + /* + It would be better to return 0 in case of errors, but to take the + safer route, let's return a zero string and let the general + handler catch the error. + */ + val_ptr->set("", 0, field_charset); + return val_ptr; +} + + +int Field_varstring_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint to_length= (uint)MY_MIN(field_length, field_charset->mbmaxlen * length + 1); + int rc= compress((char*) get_data(), &to_length, from, (uint)length, cs); + store_length(to_length); + return rc; +} + + +String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_data(), get_length()); +} + + +double Field_varstring_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_varstring_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +int Field_varstring_compressed::cmp_max(const uchar *a_ptr, const uchar *b_ptr, + uint max_len) +{ + String a, b; + uint a_length, b_length; + + if (length_bytes == 1) + { + a_length= (uint) *a_ptr; + b_length= (uint) *b_ptr; + } + else + { + a_length= uint2korr(a_ptr); + b_length= uint2korr(b_ptr); + } + + uncompress(&a, &a, a_ptr + length_bytes, a_length); + uncompress(&b, &b, b_ptr + length_bytes, b_length); + + if (a.length() > max_len) + a.length(max_len); + if (b.length() > max_len) + b.length(max_len); + + return sortcmp(&a, &b, field_charset); +} + + /**************************************************************************** ** blob type ** A blob is saved as a length and a pointer. The length is stored in the @@ -7913,6 +8168,7 @@ uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const int Field_blob::copy_value(Field_blob *from) { DBUG_ASSERT(field_charset == from->charset()); + DBUG_ASSERT(!compression_method() == !from->compression_method()); int rc= 0; uint32 length= from->get_length(); uchar *data= from->get_ptr(); @@ -7931,10 +8187,10 @@ int Field_blob::copy_value(Field_blob *from) } -int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - uint copy_length, new_length; + size_t copy_length, new_length; String_copier copier; char *tmp; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -7953,7 +8209,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) DBUG_ASSERT(length <= max_data_length()); new_length= length; - copy_length= (uint)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len); + copy_length= (size_t)MY_MIN(UINT_MAX,table->in_use->variables.group_concat_max_len); if (new_length > copy_length) { new_length= Well_formed_prefix(cs, @@ -8006,7 +8262,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) return 0; } copy_length= copier.well_formed_copy(field_charset, - (char*) value.ptr(), new_length, + (char*) value.ptr(), (uint)new_length, cs, from, length); Field_blob::store_length(copy_length); bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*)); @@ -8020,22 +8276,6 @@ oom_error: } -int Field_blob::store(double nr) -{ - CHARSET_INFO *cs=charset(); - value.set_real(nr, NOT_FIXED_DEC, cs); - return Field_blob::store(value.ptr(),(uint) value.length(), cs); -} - - -int Field_blob::store(longlong nr, bool unsigned_val) -{ - CHARSET_INFO *cs=charset(); - value.set_int(nr, unsigned_val, cs); - return Field_blob::store(value.ptr(), (uint) value.length(), cs); -} - - double Field_blob::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -8145,7 +8385,7 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr, uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) { - uint32 blob_length= get_length(ptr); + size_t blob_length= get_length(ptr); uchar *blob; #ifdef HAVE_SPATIAL @@ -8163,7 +8403,7 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) return image_length; } blob= get_ptr(); - gobj= Geometry::construct(&buffer, (char*) blob, blob_length); + gobj= Geometry::construct(&buffer, (char*) blob, (uint32)blob_length); if (!gobj || gobj->get_mbr(&mbr, &dummy)) bzero(buff, image_length); else @@ -8178,12 +8418,12 @@ uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg) #endif /*HAVE_SPATIAL*/ blob= get_ptr(); - uint local_char_length= length / field_charset->mbmaxlen; + size_t local_char_length= length / field_charset->mbmaxlen; local_char_length= my_charpos(field_charset, blob, blob + blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); - if ((uint32) length > blob_length) + if (length > blob_length) { /* Must clear this as we do a memcmp in opt_range.cc to detect @@ -8209,14 +8449,14 @@ void Field_blob::set_key_image(const uchar *buff,uint length) int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) { uchar *blob1; - uint blob_length=get_length(ptr); + size_t blob_length=get_length(ptr); memcpy(&blob1, ptr+packlength, sizeof(char*)); CHARSET_INFO *cs= charset(); - uint local_char_length= max_key_length / cs->mbmaxlen; + size_t local_char_length= max_key_length / cs->mbmaxlen; local_char_length= my_charpos(cs, blob1, blob1+blob_length, local_char_length); set_if_smaller(blob_length, local_char_length); - return Field_blob::cmp(blob1, blob_length, + return Field_blob::cmp(blob1, (uint32)blob_length, key_ptr+HA_KEY_BLOB_LENGTH, uint2korr(key_ptr)); } @@ -8252,9 +8492,9 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, @returns number of bytes written to metadata_ptr */ -int Field_blob::do_save_field_metadata(uchar *metadata_ptr) +int Field_blob::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_blob::do_save_field_metadata"); + DBUG_ENTER("Field_blob::save_field_metadata"); *metadata_ptr= pack_length_no_ptr(); DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr)); DBUG_RETURN(1); @@ -8270,33 +8510,30 @@ uint32 Field_blob::sort_length() const void Field_blob::sort_string(uchar *to,uint length) { - uchar *blob; - uint blob_length=get_length(); + String buf; - if (!blob_length && field_charset->pad_char == 0) + val_str(&buf, &buf); + if (!buf.length() && field_charset->pad_char == 0) bzero(to,length); else { if (field_charset == &my_charset_bin) { - uchar *pos; - /* Store length of blob last in blob to shorter blobs before longer blobs */ length-= packlength; - pos= to+length; - - store_bigendian(blob_length, pos, packlength); + store_bigendian(buf.length(), to + length, packlength); } - memcpy(&blob, ptr+packlength, sizeof(char*)); - - blob_length= field_charset->coll->strnxfrm(field_charset, - to, length, length, - blob, blob_length, - MY_STRXFRM_PAD_WITH_SPACE | - MY_STRXFRM_PAD_TO_MAXLEN); - DBUG_ASSERT(blob_length == length); + +#ifndef DBUG_OFF + size_t rc= +#endif + field_charset->coll->strnxfrm(field_charset, to, length, length, + (const uchar*) buf.ptr(), buf.length(), + MY_STRXFRM_PAD_WITH_SPACE | + MY_STRXFRM_PAD_TO_MAXLEN); + DBUG_ASSERT(rc == length); } } @@ -8392,11 +8629,9 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from, DBUG_RETURN(0); // Error in data uint32 const length= get_length(from, master_packlength); DBUG_DUMP("packed", from, length + master_packlength); - bitmap_set_bit(table->write_set, field_index); if (from + master_packlength + length > from_end) DBUG_RETURN(0); - store(reinterpret_cast<const char*>(from) + master_packlength, - length, field_charset); + set_ptr(length, const_cast<uchar*> (from) + master_packlength); DBUG_RETURN(from + master_packlength + length); } @@ -8415,11 +8650,68 @@ uint Field_blob::max_packed_col_length(uint max_length) } +/* + Blob fields are regarded equal if they have same character set, + same blob store length and if either both are compressed or both are + uncompressed. + The logic for compression is that we don't have to uncompress and compress + again an already compressed field just because compression method changes. +*/ + uint Field_blob::is_equal(Create_field *new_field) { return new_field->type_handler() == type_handler() && new_field->charset == field_charset && - new_field->pack_length == pack_length(); + new_field->pack_length == pack_length() && + !new_field->compression_method() == !compression_method(); +} + + +int Field_blob_compressed::store(const char *from, size_t length, + CHARSET_INFO *cs) +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + uint to_length= (uint)MY_MIN(max_data_length(), field_charset->mbmaxlen * length + 1); + int rc; + + if (value.alloc(to_length)) + { + set_ptr((uint32) 0, NULL); + return -1; + } + + rc= compress((char*) value.ptr(), &to_length, from, (uint)length, cs); + set_ptr(to_length, (uchar*) value.ptr()); + return rc; +} + + +String *Field_blob_compressed::val_str(String *val_buffer, String *val_ptr) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + return uncompress(val_buffer, val_ptr, get_ptr(), get_length()); +} + + +double Field_blob_compressed::val_real(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); +} + + +longlong Field_blob_compressed::val_int(void) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + THD *thd= get_thd(); + String buf; + val_str(&buf, &buf); + return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset, + buf.ptr(), buf.length()).result(); } @@ -8473,7 +8765,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) } -uint gis_field_options_read(const uchar *buf, uint buf_len, +uint gis_field_options_read(const uchar *buf, size_t buf_len, Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid) { const uchar *buf_end= buf + buf_len; @@ -8579,7 +8871,7 @@ int Field_geom::store_decimal(const my_decimal *) } -int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs) { if (!length) bzero(ptr, Field_blob::pack_length()); @@ -8596,7 +8888,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) wkb_type > (uint32) Geometry::wkb_last) goto err; - if (geom_type != Field::GEOM_GEOMETRY && + if (geom_type != Field::GEOM_GEOMETRY && geom_type != Field::GEOM_GEOMETRYCOLLECTION && (uint32) geom_type != wkb_type) { @@ -8704,7 +8996,7 @@ void Field_enum::store_type(ulonglong value) (if there isn't a empty value in the enum) */ -int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; @@ -8721,7 +9013,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) } /* Remove end space */ - length= field_charset->cset->lengthsp(field_charset, from, length); + length= (uint)field_charset->cset->lengthsp(field_charset, from, length); uint tmp=find_type2(typelib, from, length, field_charset); if (!tmp) { @@ -8736,7 +9028,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) set_warning(WARN_DATA_TRUNCATED, 1); err= 1; } - if (!get_thd()->count_cuted_fields && !length) + if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length) err= 0; } else @@ -8763,7 +9055,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(WARN_DATA_TRUNCATED, 1); - if (nr != 0 || get_thd()->count_cuted_fields) + if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { nr= 0; error= 1; @@ -8798,7 +9090,7 @@ longlong Field_enum::val_int(void) @returns number of bytes written to metadata_ptr */ -int Field_enum::do_save_field_metadata(uchar *metadata_ptr) +int Field_enum::save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= real_type(); *(metadata_ptr + 1)= pack_length(); @@ -8887,7 +9179,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table, */ -int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) +int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; bool got_warning= 0; @@ -9306,14 +9598,14 @@ uint Field_bit::is_equal(Create_field *new_field) } -int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; for (; length && !*from; from++, length--) // skip left 0's ; - delta= bytes_in_rec - length; + delta= (int)(bytes_in_rec - length); if (delta < -1 || (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) || @@ -9517,9 +9809,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg) @returns number of bytes written to metadata_ptr */ -int Field_bit::do_save_field_metadata(uchar *metadata_ptr) +int Field_bit::save_field_metadata(uchar *metadata_ptr) { - DBUG_ENTER("Field_bit::do_save_field_metadata"); + DBUG_ENTER("Field_bit::save_field_metadata"); DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d", bit_len, bytes_in_rec)); /* @@ -9589,9 +9881,9 @@ Field_bit::compatible_field_size(uint field_metadata, void Field_bit::sql_type(String &res) const { CHARSET_INFO *cs= res.charset(); - ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "bit(%d)", (int) field_length); - res.length((uint) length); + res.length(length); } @@ -9742,7 +10034,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, } -int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) +int Field_bit_as_char::store(const char *from, size_t length, CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; @@ -9750,7 +10042,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) for (; length && !*from; from++, length--) // skip left 0's ; - delta= bytes_in_rec - length; + delta= (int)(bytes_in_rec - length); if (delta < 0 || (delta == 0 && bits && (uint) (uchar) *from >= (uint) (1 << bits))) @@ -9773,9 +10065,9 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) void Field_bit_as_char::sql_type(String &res) const { CHARSET_INFO *cs= res.charset(); - ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + size_t length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), "bit(%d)", (int) field_length); - res.length((uint) length); + res.length(length); } @@ -9851,7 +10143,7 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root, } } interval->type_names[i]= value.str; - interval->type_lengths[i]= value.length; + interval->type_lengths[i]= (uint)value.length; } interval->type_names[interval->count]= 0; // End marker interval->type_lengths[interval->count]= 0; @@ -9989,6 +10281,8 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, uint filter= VCOL_IMPOSSIBLE; if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT) filter|= VCOL_NOT_STRICTLY_DETERMINISTIC; + if (type == VCOL_GENERATED_VIRTUAL) + filter|= VCOL_NOT_VIRTUAL; if (ret || (res.errors & filter)) { @@ -10190,8 +10484,8 @@ bool Column_definition::check(THD *thd) TIMESTAMP columns get implicit DEFAULT value when explicit_defaults_for_timestamp is not set. */ - if (opt_explicit_defaults_for_timestamp || - !is_timestamp_type()) + if ((opt_explicit_defaults_for_timestamp || + !is_timestamp_type()) && !vers_sys_field()) { flags|= NO_DEFAULT_VALUE_FLAG; } @@ -10246,7 +10540,8 @@ Field *make_field(TABLE_SHARE *share, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, - const LEX_CSTRING *field_name) + const LEX_CSTRING *field_name, + uint32 flags) { uchar *UNINIT_VAR(bit_ptr); uchar UNINIT_VAR(bit_offset); @@ -10301,6 +10596,16 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, field_charset); if (field_type == MYSQL_TYPE_VARCHAR) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_varstring_compressed( + ptr, field_length, + HA_VARCHAR_PACKLENGTH(field_length), + null_pos, null_bit, + unireg_check, field_name, + share, field_charset, zlib_compression_method); + return new (mem_root) Field_varstring(ptr,field_length, HA_VARCHAR_PACKLENGTH(field_length), @@ -10308,6 +10613,7 @@ Field *make_field(TABLE_SHARE *share, unireg_check, field_name, share, field_charset); + } return 0; // Error } @@ -10329,10 +10635,18 @@ Field *make_field(TABLE_SHARE *share, } #endif if (f_is_blob(pack_flag)) + { + if (unireg_check == Field::TMYSQL_COMPRESSED) + return new (mem_root) + Field_blob_compressed(ptr, null_pos, null_bit, + unireg_check, field_name, share, + pack_length, field_charset, zlib_compression_method); + return new (mem_root) Field_blob(ptr,null_pos,null_bit, unireg_check, field_name, share, pack_length, field_charset); + } if (interval) { if (f_is_enum(pack_flag)) @@ -10412,11 +10726,22 @@ Field *make_field(TABLE_SHARE *share, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: - return new (mem_root) - Field_longlong(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + { + return new (mem_root) + Field_vers_trx_id(ptr, field_length, null_pos, null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); + } + else + { + return new (mem_root) + Field_longlong(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); + } case MYSQL_TYPE_TIMESTAMP: { uint dec= field_length > MAX_DATETIME_WIDTH ? @@ -10493,6 +10818,11 @@ Field *make_field(TABLE_SHARE *share, return 0; } +bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const +{ + return item->type() == Item::DATE_ITEM; +} + /** Create a field suitable for create of table. */ @@ -10511,10 +10841,27 @@ Column_definition::Column_definition(THD *thd, Field *old_field, comment= old_field->comment; decimals= old_field->decimals(); vcol_info= old_field->vcol_info; - default_value= orig_field ? orig_field->default_value : 0; - check_constraint= orig_field ? orig_field->check_constraint : 0; option_list= old_field->option_list; pack_flag= 0; + compression_method_ptr= 0; + versioning= VERSIONING_NOT_SET; + invisible= old_field->invisible; + + if (orig_field) + { + default_value= orig_field->default_value; + check_constraint= orig_field->check_constraint; + if (orig_field->unireg_check == Field::TMYSQL_COMPRESSED) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + } + } + else + { + default_value= 0; + check_constraint= 0; + } switch (real_field_type()) { case MYSQL_TYPE_TINY_BLOB: @@ -10535,7 +10882,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field, case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: /* This is corrected in create_length_to_internal_length */ - length= (length+charset->mbmaxlen-1) / charset->mbmaxlen; + length= (length+charset->mbmaxlen-1) / charset->mbmaxlen - + MY_TEST(old_field->compression_method()); break; #ifdef HAVE_SPATIAL case MYSQL_TYPE_GEOMETRY: @@ -10635,6 +10983,7 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field, flags= dup_field->flags; interval= dup_field->interval; vcol_info= dup_field->vcol_info; + invisible= dup_field->invisible; } @@ -10699,6 +11048,29 @@ bool Column_definition::has_default_expression() (flags & BLOB_FLAG))); } + +bool Column_definition::set_compressed(const char *method) +{ + enum enum_field_types sql_type= real_field_type(); + /* We can't use f_is_blob here as pack_flag is not yet set */ + if (sql_type == MYSQL_TYPE_VARCHAR || sql_type == MYSQL_TYPE_TINY_BLOB || + sql_type == MYSQL_TYPE_BLOB || sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_type == MYSQL_TYPE_LONG_BLOB) + { + if (!method || !strcmp(method, zlib_compression_method->name)) + { + unireg_check= Field::TMYSQL_COMPRESSED; + compression_method_ptr= zlib_compression_method; + return false; + } + my_error(ER_UNKNOWN_COMPRESSION_METHOD, MYF(0), method); + } + else + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); + return true; +} + + /** maximum possible display length for blob. @@ -10737,11 +11109,12 @@ uint32 Field_blob::max_display_length() @param cut_increment - whenever we should increase cut fields count @note - This function won't produce warning and increase cut fields counter - if count_cuted_fields == CHECK_FIELD_IGNORE for current thread. + This function won't produce warning or notes or increase cut fields counter + if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION + for the current thread. - if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. - This allows us to avoid notes in optimisation, like convert_constant_item(). + This allows us to avoid notes in optimisation, like + convert_constant_item(). @retval 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE @@ -10758,7 +11131,7 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, will have table == NULL. */ THD *thd= get_thd(); - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { thd->cuted_fields+= cut_increment; push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, diff --git a/sql/field.h b/sql/field.h index bc56ba4db7b..364e937227c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -32,6 +32,7 @@ #include "sql_error.h" /* Sql_condition */ #include "compat56.h" #include "sql_type.h" /* Type_std_attributes */ +#include "field_comp.h" class Send_field; class Copy_field; @@ -45,12 +46,15 @@ class Item_func; class Item_bool_func; class Item_equal; class Virtual_tmp_table; +class Qualified_column_ident; +class Table_ident; enum enum_check_fields { CHECK_FIELD_IGNORE, + CHECK_FIELD_EXPRESSION, CHECK_FIELD_WARN, - CHECK_FIELD_ERROR_FOR_NULL + CHECK_FIELD_ERROR_FOR_NULL, }; /* @@ -214,7 +218,7 @@ protected: my_decimal *buf) { DBUG_ASSERT(length < UINT_MAX32); - m_error= str2my_decimal(mask, str, (uint) length, cs, + m_error= str2my_decimal(mask, str, length, cs, buf, (const char **) &m_end_of_num); // E_DEC_TRUNCATED means a very minor truncation: '1e-100' -> 0 m_edom= m_error && m_error != E_DEC_TRUNCATED; @@ -312,7 +316,7 @@ protected: return decimal_value; } - longlong longlong_from_hex_hybrid(const char *str, uint32 length) + longlong longlong_from_hex_hybrid(const char *str, size_t length) { const char *end= str + length; const char *ptr= end - MY_MIN(length, sizeof(longlong)); @@ -526,6 +530,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type) #define VCOL_TIME_FUNC 8 #define VCOL_AUTO_INC 16 #define VCOL_IMPOSSIBLE 32 +#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */ #define VCOL_NOT_STRICTLY_DETERMINISTIC \ (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC) @@ -641,7 +646,17 @@ public: static void operator delete(void *ptr, MEM_ROOT *mem_root) { DBUG_ASSERT(0); } + /** + Used by System Versioning. + */ + virtual void set_max() + { DBUG_ASSERT(0); } + virtual bool is_max() + { DBUG_ASSERT(0); return false; } + uchar *ptr; // Position to field in record + + field_visibility_t invisible; /** Byte where the @c NULL bit is stored inside a record. If this Field is a @c NOT @c NULL field, this member is @c NULL. @@ -682,7 +697,8 @@ public: TIMESTAMP_OLD_FIELD=18, // TIMESTAMP created before 4.1.3 TIMESTAMP_DN_FIELD=21, // TIMESTAMP DEFAULT NOW() TIMESTAMP_UN_FIELD=22, // TIMESTAMP ON UPDATE NOW() - TIMESTAMP_DNUN_FIELD=23 // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() + TIMESTAMP_DNUN_FIELD=23, // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() + TMYSQL_COMPRESSED= 24, // Compatibility with TMySQL }; enum geometry_type { @@ -779,8 +795,8 @@ public: @retval false - conversion is needed */ virtual bool memcpy_field_possible(const Field *from) const= 0; - virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0; - virtual int store_hex_hybrid(const char *str, uint length); + virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0; + virtual int store_hex_hybrid(const char *str, size_t length); virtual int store(double nr)=0; virtual int store(longlong nr, bool unsigned_val)=0; virtual int store_decimal(const my_decimal *d)=0; @@ -788,7 +804,7 @@ public: virtual int store_timestamp(my_time_t timestamp, ulong sec_part); int store_time(const MYSQL_TIME *ltime) { return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); } - int store(const char *to, uint length, CHARSET_INFO *cs, + int store(const char *to, size_t length, CHARSET_INFO *cs, enum_check_fields check_level); int store(const LEX_STRING *ls, CHARSET_INFO *cs) { @@ -886,8 +902,21 @@ public: DBUG_RETURN(field_metadata); } virtual uint row_pack_length() const { return 0; } + + + /** + Retrieve the field metadata for fields. + + This default implementation returns 0 and saves 0 in the first_byte value. + + @param first_byte First byte of field metadata + + @returns 0 no bytes written. + */ + virtual int save_field_metadata(uchar *first_byte) - { return do_save_field_metadata(first_byte); } + { return 0; } + /* data_length() return the "real size" of the data in memory. @@ -956,8 +985,19 @@ public: { return bitmap_is_set(&table->has_value_set, field_index); } + void clear_has_explicit_value() + { + bitmap_clear_bit(&table->has_value_set, field_index); + } bool set_explicit_default(Item *value); + virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const + { DBUG_ASSERT(0); return 0; } + my_time_t get_timestamp(ulong *sec_part) const + { + return get_timestamp(ptr, sec_part); + } + /** Evaluates the @c UPDATE default function, if one exists, and stores the result in the record buffer. If no such function exists for the column, @@ -1399,6 +1439,21 @@ public: FIELD_FLAGS_COLUMN_FORMAT; } + bool vers_sys_field() const + { + return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); + } + + bool vers_update_unversioned() const + { + return flags & VERS_UPDATE_UNVERSIONED_FLAG; + } + + virtual bool vers_trx_id() const + { + return false; + } + /* Validate a non-null field value stored in the given record according to the current thread settings, e.g. sql_mode. @@ -1486,6 +1541,8 @@ public: /* Mark field in read map. Updates also virtual fields */ void register_field_in_read_map(); + virtual Compression_method *compression_method() const { return 0; } + virtual Virtual_tmp_table **virtual_tmp_table_addr() { return NULL; @@ -1520,19 +1577,6 @@ private: */ virtual size_t do_last_null_byte() const; -/** - Retrieve the field metadata for fields. - - This default implementation returns 0 and saves 0 in the metadata_ptr - value. - - @param metadata_ptr First byte of field metadata - - @returns 0 no bytes written. -*/ - virtual int do_save_field_metadata(uchar *metadata_ptr) - { return 0; } - protected: uchar *pack_int(uchar *to, const uchar *from, size_t size) { @@ -1575,20 +1619,20 @@ class Field_num :public Field { protected: int check_edom_and_important_data_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, uint length, + const char *str, size_t length, const char *end_of_num); int check_edom_and_truncation(const char *type, bool edom, CHARSET_INFO *cs, - const char *str, uint length, + const char *str, size_t length, const char *end_of_num); - int check_int(CHARSET_INFO *cs, const char *str, uint length, + int check_int(CHARSET_INFO *cs, const char *str, size_t length, const char *int_end, int error) { return check_edom_and_truncation("integer", error == MY_ERRNO_EDOM || str == int_end, cs, str, length, int_end); } - bool get_int(CHARSET_INFO *cs, const char *from, uint len, + bool get_int(CHARSET_INFO *cs, const char *from, size_t len, longlong *rnd, ulonglong unsigned_max, longlong signed_min, longlong signed_max); void prepend_zeros(String *value) const; @@ -1673,10 +1717,10 @@ public: charset() == from->charset(); } int store(double nr); - int store(longlong nr, bool unsigned_val)=0; + int store(longlong nr, bool unsigned_val); int store_decimal(const my_decimal *); - int store(const char *to,uint length,CHARSET_INFO *cs)=0; - int store_hex_hybrid(const char *str, uint length) + int store(const char *to,size_t length,CHARSET_INFO *cs)=0; + int store_hex_hybrid(const char *str, size_t length) { return store(str, length, &my_charset_bin); } @@ -1721,6 +1765,11 @@ protected: const Item *item) const; bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond, const Item *item) const; + int compress(char *to, uint *to_length, + const char *from, uint length, + CHARSET_INFO *cs); + String *uncompress(String *val_buffer, String *val_ptr, + const uchar *from, uint from_length); public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -1754,7 +1803,7 @@ public: /* base class for float and double and decimal (old one) */ class Field_real :public Field_num { protected: - double get_double(const char *str, uint length, CHARSET_INFO *cs, int *err); + double get_double(const char *str, size_t length, CHARSET_INFO *cs, int *err); public: bool not_fixed; @@ -1810,7 +1859,7 @@ public: return eq_def(from) ? get_identical_copy_func() : do_field_string; } int reset(void); - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); double val_real(void); @@ -1831,7 +1880,7 @@ public: /* New decimal/numeric field which use fixed point arithmetic */ class Field_new_decimal :public Field_num { private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); public: /* The maximum number of decimal digits can be stored */ uint precision; @@ -1869,7 +1918,7 @@ public: bool store_value(const my_decimal *decimal_value); bool store_value(const my_decimal *decimal_value, int *native_error); void set_value_on_overflow(my_decimal *decimal_value, bool sign); - int store(const char *to, uint length, CHARSET_INFO *charset); + 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); @@ -1915,7 +1964,7 @@ public: const Type_handler *type_handler() const { return &type_handler_tiny; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { ptr[0]=0; return 0; } @@ -1965,7 +2014,7 @@ public: const Type_handler *type_handler() const { return &type_handler_short; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;} - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { ptr[0]=ptr[1]=0; return 0; } @@ -2000,7 +2049,7 @@ public: const Type_handler *type_handler() const { return &type_handler_int24; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } @@ -2040,7 +2089,7 @@ public: const Type_handler *type_handler() const { return &type_handler_long; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } @@ -2086,7 +2135,7 @@ public: const Type_handler *type_handler() const { return &type_handler_longlong; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) @@ -2113,6 +2162,57 @@ public: { return unpack_int64(to, from, from_end); } + + void set_max(); + bool is_max(); +}; + + +class Field_vers_trx_id :public Field_longlong { + MYSQL_TIME cache; + ulonglong cached; +public: + Field_vers_trx_id(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, bool zero_arg, + bool unsigned_arg) + : Field_longlong(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, zero_arg, + unsigned_arg), + cached(0) + {} + enum_field_types real_type() const { return MYSQL_TYPE_LONGLONG; } + enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} + 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) + { + return get_date(ltime, fuzzydate, (ulonglong) val_int()); + } + bool test_if_equality_guarantees_uniqueness(const Item *item) const; + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const + { + return true; + } + + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const + { + return true; + } + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const + { + return true; + } + /* cmp_type() cannot be TIME_RESULT, because we want to compare this field against + integers. But in all other cases we treat it as TIME_RESULT! */ + bool vers_trx_id() const + { + return true; + } }; @@ -2139,7 +2239,7 @@ public: } const Type_handler *type_handler() const { return &type_handler_float; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { bzero(ptr,sizeof(float)); return 0; } @@ -2153,7 +2253,7 @@ public: uint row_pack_length() const { return pack_length(); } void sql_type(String &str) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -2191,7 +2291,7 @@ public: } const Type_handler *type_handler() const { return &type_handler_double; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int reset(void) { bzero(ptr,sizeof(double)); return 0; } @@ -2206,7 +2306,7 @@ public: uint row_pack_length() const { return pack_length(); } void sql_type(String &str) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -2226,7 +2326,7 @@ public: { return do_field_string; } - int store(const char *to, uint length, CHARSET_INFO *cs) + int store(const char *to, size_t length, CHARSET_INFO *cs) { null[0]=1; return 0; } int store(double nr) { null[0]=1; return 0; } int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; } @@ -2248,13 +2348,11 @@ public: bool can_optimize_keypart_ref(const Item_bool_func *cond, const Item *item) const { - DBUG_ASSERT(0); return false; } bool can_optimize_group_min_max(const Item_bool_func *cond, const Item *const_item) const { - DBUG_ASSERT(0); return false; } }; @@ -2271,7 +2369,7 @@ public: :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { flags|= BINARY_FLAG; } - int store_hex_hybrid(const char *str, uint length) + int store_hex_hybrid(const char *str, size_t length) { return store(str, length, &my_charset_bin); } @@ -2348,7 +2446,7 @@ public: :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} - int store(const char *to, uint length, CHARSET_INFO *charset); + 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); @@ -2370,7 +2468,7 @@ public: const Type_handler *type_handler() const { return &type_handler_timestamp; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } Copy_func *get_copy_func(const Field *from) const; - int store(const char *to,uint length,CHARSET_INFO *charset); + 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); @@ -2395,7 +2493,7 @@ public: return res; } /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ - virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; my_time_t get_timestamp(ulong *sec_part) const { return get_timestamp(ptr, sec_part); @@ -2494,7 +2592,7 @@ public: TIMESTAMP(0..6) - MySQL56 version */ class Field_timestampf :public Field_timestamp_with_dec { - int do_save_field_metadata(uchar *metadata_ptr) + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -2525,8 +2623,14 @@ public: { return memcmp(a_ptr, b_ptr, pack_length()); } + void set_max(); + bool is_max(); void store_TIME(my_time_t timestamp, ulong sec_part); my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + my_time_t get_timestamp(ulong *sec_part) const + { + return get_timestamp(ptr, sec_part); + } uint size_of() const { return sizeof(*this); } }; @@ -2567,7 +2671,7 @@ public: } return do_field_int; } - int store(const char *to,uint length,CHARSET_INFO *charset); + 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); @@ -2686,7 +2790,7 @@ public: decimals() == from->decimals(); } int store_time_dec(const MYSQL_TIME *ltime, uint dec); - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int store_decimal(const my_decimal *); @@ -2766,8 +2870,8 @@ public: TIME(0..6) - MySQL56 version */ class Field_timef :public Field_time_with_dec { - void store_TIME(const MYSQL_TIME *); - int do_save_field_metadata(uchar *metadata_ptr) + void store_TIME(const MYSQL_TIME *ltime); + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -2929,7 +3033,7 @@ public: class Field_datetimef :public Field_datetime_with_dec { void store_TIME(MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; - int do_save_field_metadata(uchar *metadata_ptr) + int save_field_metadata(uchar *metadata_ptr) { *metadata_ptr= (uchar) decimals(); return 1; @@ -3059,9 +3163,8 @@ public: (has_charset() ? ' ' : 0)); return 0; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + int store(const char *to,size_t length,CHARSET_INFO *charset); + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3094,7 +3197,7 @@ public: Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); virtual uint get_key_image(uchar *buff,uint length, imagetype type); private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -3108,6 +3211,15 @@ public: { return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); } +protected: + void store_length(uint32 number) + { + if (length_bytes == 1) + *ptr= (uchar) number; + else + int2store(ptr, number); + } +public: /* The maximum space available in a Field_varstring, in bytes. See length_bytes. @@ -3152,11 +3264,11 @@ public: bool memcpy_field_possible(const Field *from) const { return Field_str::memcpy_field_possible(from) && + !compression_method() == !from->compression_method() && length_bytes == ((Field_varstring*) from)->length_bytes; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + int store(const char *to,size_t length,CHARSET_INFO *charset); + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3190,10 +3302,91 @@ public: void hash(ulong *nr, ulong *nr2); uint length_size() { return length_bytes; } private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); +}; + + +class Field_varstring_compressed: public Field_varstring { +public: + Field_varstring_compressed(uchar *ptr_arg, + uint32 len_arg, uint length_bytes_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, const DTCollation &collation, + Compression_method *compression_method_arg): + Field_varstring(ptr_arg, len_arg, length_bytes_arg, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, + share, collation), + compression_method_ptr(compression_method_arg) { DBUG_ASSERT(len_arg > 0); } + Compression_method *compression_method() const + { return compression_method_ptr; } +private: + Compression_method *compression_method_ptr; + int store(const char *to, size_t length, CHARSET_INFO *charset); + using Field_str::store; + String *val_str(String *, String *); + double val_real(void); + longlong val_int(void); + uint size_of() const { return sizeof(*this); } + enum_field_types binlog_type() const { return MYSQL_TYPE_VARCHAR_COMPRESSED; } + void sql_type(String &str) const + { + Field_varstring::sql_type(str); + str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/")); + } + uint32 max_display_length() { return field_length - 1; } + int cmp_max(const uchar *a_ptr, const uchar *b_ptr, uint max_len); + + /* + Compressed fields can't have keys as two rows may have different + compression methods or compression levels. + */ + + int key_cmp(const uchar *str, uint length) + { DBUG_ASSERT(0); return 0; } + using Field_varstring::key_cmp; }; +static inline uint8 number_storage_requirement(uint32 n) +{ + return n < 256 ? 1 : n < 65536 ? 2 : n < 16777216 ? 3 : 4; +} + + +static inline void store_bigendian(ulonglong num, uchar *to, uint bytes) +{ + switch(bytes) { + case 1: mi_int1store(to, num); break; + case 2: mi_int2store(to, num); break; + case 3: mi_int3store(to, num); break; + case 4: mi_int4store(to, num); break; + case 5: mi_int5store(to, num); break; + case 6: mi_int6store(to, num); break; + case 7: mi_int7store(to, num); break; + case 8: mi_int8store(to, num); break; + default: DBUG_ASSERT(0); + } +} + + +static inline longlong read_bigendian(const uchar *from, uint bytes) +{ + switch(bytes) { + case 1: return mi_uint1korr(from); + case 2: return mi_uint2korr(from); + case 3: return mi_uint3korr(from); + case 4: return mi_uint4korr(from); + case 5: return mi_uint5korr(from); + case 6: return mi_uint6korr(from); + case 7: return mi_uint7korr(from); + case 8: return mi_sint8korr(from); + default: DBUG_ASSERT(0); return 0; + } +} + + extern LEX_CSTRING temp_lex_str; class Field_blob :public Field_longstr { @@ -3236,13 +3429,7 @@ public: NONE, field_name_arg, collation) { flags|= BLOB_FLAG; - packlength= 4; - if (set_packlength) - { - packlength= len_arg <= 255 ? 1 : - len_arg <= 65535 ? 2 : - len_arg <= 16777215 ? 3 : 4; - } + packlength= set_packlength ? number_storage_requirement(len_arg) : 4; } Field_blob(uint32 packlength_arg) :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, &temp_lex_str, @@ -3275,7 +3462,8 @@ public: if (from->type() == MYSQL_TYPE_BIT) return do_field_int; */ - if (!(from->flags & BLOB_FLAG) || from->charset() != charset()) + if (!(from->flags & BLOB_FLAG) || from->charset() != charset() || + !from->compression_method() != !compression_method()) return do_conv_blob; if (from->pack_length() != Field_blob::pack_length()) return do_copy_blob; @@ -3292,11 +3480,11 @@ public: bool memcpy_field_possible(const Field *from) const { return Field_str::memcpy_field_possible(from) && + !compression_method() == !from->compression_method() && !table->copy_blobs; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); + int store(const char *to, size_t length, CHARSET_INFO *charset); + using Field_str::store; double val_real(void); longlong val_int(void); String *val_str(String*,String *); @@ -3338,9 +3526,10 @@ public: void reset_fields() { bzero((uchar*) &value,sizeof(value)); bzero((uchar*) &read_value,sizeof(read_value)); } uint32 get_field_buffer_size(void) { return value.alloced_length(); } void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number); - inline void store_length(uint32 number) + inline void store_length(size_t number) { - store_length(ptr, packlength, number); + DBUG_ASSERT(number < UINT_MAX32); + store_length(ptr, packlength, (uint32)number); } inline uint32 get_length(uint row_offset= 0) const { return get_length(ptr+row_offset, this->packlength); } @@ -3430,7 +3619,54 @@ public: uint32 char_length() const; uint is_equal(Create_field *new_field); private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); +}; + + +class Field_blob_compressed: public Field_blob { +public: + Field_blob_compressed(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, + uint blob_pack_length, const DTCollation &collation, + Compression_method *compression_method_arg): + Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, share, blob_pack_length, collation), + compression_method_ptr(compression_method_arg) {} + Compression_method *compression_method() const + { return compression_method_ptr; } +private: + Compression_method *compression_method_ptr; + int store(const char *to, size_t length, CHARSET_INFO *charset); + using Field_str::store; + String *val_str(String *, String *); + double val_real(void); + longlong val_int(void); + uint size_of() const { return sizeof(*this); } + enum_field_types binlog_type() const { return MYSQL_TYPE_BLOB_COMPRESSED; } + void sql_type(String &str) const + { + Field_blob::sql_type(str); + str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/")); + } + + /* + Compressed fields can't have keys as two rows may have different + compression methods or compression levels. + */ + + uint get_key_image(uchar *buff, uint length, imagetype type_arg) + { DBUG_ASSERT(0); return 0; } + void set_key_image(const uchar *buff, uint length) + { DBUG_ASSERT(0); } + int key_cmp(const uchar *a, const uchar *b) + { DBUG_ASSERT(0); return 0; } + int key_cmp(const uchar *str, uint length) + { DBUG_ASSERT(0); return 0; } + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit) + { DBUG_ASSERT(0); return 0; } }; @@ -3468,7 +3704,7 @@ public: bool is_eq_func) const; void sql_type(String &str) const; uint is_equal(Create_field *new_field); - int store(const char *to, uint length, CHARSET_INFO *charset); + int store(const char *to, size_t length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int store_decimal(const my_decimal *); @@ -3495,7 +3731,7 @@ public: }; uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields); -uint gis_field_options_read(const uchar *buf, uint buf_len, +uint gis_field_options_read(const uchar *buf, size_t buf_len, Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid); #endif /*HAVE_SPATIAL*/ @@ -3549,7 +3785,7 @@ public: return save_in_field_str(to); } bool memcpy_field_possible(const Field *from) const { return false; } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); double val_real(void); @@ -3595,7 +3831,7 @@ public: const Item *item, bool is_eq_func) const; private: - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); uint is_equal(Create_field *new_field); }; @@ -3616,7 +3852,7 @@ public: flags=(flags & ~ENUM_FLAG) | SET_FLAG; } int store_field(Field *from) { return from->save_in_field(this); } - int store(const char *to,uint length,CHARSET_INFO *charset); + int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr) { return Field_set::store((longlong) nr, FALSE); } int store(longlong nr, bool unsigned_val); @@ -3672,7 +3908,7 @@ public: } int save_in_field(Field *to) { return to->store(val_int(), true); } bool memcpy_field_possible(const Field *from) const { return false; } - int store(const char *to, uint length, CHARSET_INFO *charset); + int store(const char *to, size_t length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); int store_decimal(const my_decimal *); @@ -3773,7 +4009,7 @@ public: private: virtual size_t do_last_null_byte() const; - int do_save_field_metadata(uchar *first_byte); + int save_field_metadata(uchar *first_byte); }; @@ -3791,7 +4027,7 @@ public: enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg); enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } uint size_of() const { return sizeof(*this); } - int store(const char *to, uint length, CHARSET_INFO *charset); + int store(const char *to, size_t length, CHARSET_INFO *charset); int store(double nr) { return Field_bit::store(nr); } int store(longlong nr, bool unsigned_val) { return Field_bit::store(nr, unsigned_val); } @@ -3822,7 +4058,8 @@ Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, CHARSET_INFO *cs, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, - TYPELIB *interval, const LEX_CSTRING *field_name); + TYPELIB *interval, const LEX_CSTRING *field_name, + uint32 flags); /* Create field class for CREATE TABLE @@ -3872,15 +4109,23 @@ class Column_definition: public Sql_alloc, bool prepare_stage1_check_typelib_default(); bool prepare_stage1_convert_default(THD *, MEM_ROOT *, CHARSET_INFO *to); const Type_handler *field_type() const; // Prevent using this + Compression_method *compression_method_ptr; public: LEX_CSTRING field_name; LEX_CSTRING comment; // Comment for field + enum enum_column_versioning + { + VERSIONING_NOT_SET, + WITH_VERSIONING, + WITHOUT_VERSIONING + }; Item *on_update; // ON UPDATE NOW() /* At various stages in execution this can be length of field in bytes or max number of characters. */ ulonglong length; + field_visibility_t invisible; /* The value of `length' as set by parser: is the number of characters for most of the types, or of bytes for BLOBs or numeric types. @@ -3907,18 +4152,23 @@ public: *default_value, // Default value *check_constraint; // Check constraint + enum_column_versioning versioning; + Column_definition() :Type_handler_hybrid_field_type(&type_handler_null), + compression_method_ptr(0), comment(null_clex_str), - on_update(NULL), length(0), decimals(0), + on_update(NULL), length(0), invisible(VISIBLE), decimals(0), flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), interval(0), charset(&my_charset_bin), srid(0), geom_type(Field::GEOM_GEOMETRY), option_list(NULL), pack_flag(0), - vcol_info(0), default_value(0), check_constraint(0) + vcol_info(0), default_value(0), check_constraint(0), + versioning(VERSIONING_NOT_SET) { interval_list.empty(); } + Column_definition(THD *thd, Field *field, Field *orig_field); void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); void create_length_to_internal_length_null() @@ -3933,6 +4183,8 @@ public: void create_length_to_internal_length_string() { length*= charset->mbmaxlen; + if (real_field_type() == MYSQL_TYPE_VARCHAR && compression_method()) + length++; DBUG_ASSERT(length <= UINT_MAX32); key_length= (uint) length; pack_length= type_handler()->calc_pack_length((uint32) length); @@ -3943,6 +4195,10 @@ public: length*= charset->mbmaxlen; key_length= pack_length; } + bool vers_sys_field() const + { + return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); + } void create_length_to_internal_length_bit(); void create_length_to_internal_length_newdecimal(); @@ -4043,7 +4299,7 @@ public: (uint32)length, null_pos, null_bit, pack_flag, type_handler(), charset, geom_type, srid, unireg_check, interval, - field_name_arg); + field_name_arg, flags); } Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, const LEX_CSTRING *field_name_arg) const @@ -4082,6 +4338,11 @@ public: { *this= *def; } + bool set_compressed(const char *method); + void set_compression_method(Compression_method *compression_method_arg) + { compression_method_ptr= compression_method_arg; } + Compression_method *compression_method() const + { return compression_method_ptr; } }; @@ -4118,7 +4379,6 @@ public: bool resolve_type_refs(THD *); }; - /** This class is used during a stored routine or a trigger execution, at sp_rcontext::create() time. @@ -4140,8 +4400,8 @@ public: */ class Spvar_definition: public Column_definition { - class Qualified_column_ident *m_column_type_ref; // for %TYPE - class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE + Qualified_column_ident *m_column_type_ref; // for %TYPE + Table_ident *m_table_rowtype_ref; // for table%ROWTYPE bool m_cursor_rowtype_ref; // for cursor%ROWTYPE uint m_cursor_rowtype_offset; // for cursor%ROWTYPE Row_definition_list *m_row_field_definitions; // for ROW @@ -4168,20 +4428,20 @@ public: bool is_column_type_ref() const { return m_column_type_ref != 0; } bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; } bool is_cursor_rowtype_ref() const { return m_cursor_rowtype_ref; } - class Qualified_column_ident *column_type_ref() const + Qualified_column_ident *column_type_ref() const { return m_column_type_ref; } - void set_column_type_ref(class Qualified_column_ident *ref) + void set_column_type_ref(Qualified_column_ident *ref) { m_column_type_ref= ref; } - class Table_ident *table_rowtype_ref() const + Table_ident *table_rowtype_ref() const { return m_table_rowtype_ref; } - void set_table_rowtype_ref(class Table_ident *ref) + void set_table_rowtype_ref(Table_ident *ref) { DBUG_ASSERT(ref); set_handler(&type_handler_row); @@ -4380,5 +4640,21 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, #define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) #define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) +#define f_visibility(x) (static_cast<field_visibility_t> ((x) & INVISIBLE_MAX_BITS)) + +inline +ulonglong TABLE::vers_end_id() const +{ + DBUG_ASSERT(versioned(VERS_TRX_ID)); + return static_cast<ulonglong>(vers_end_field()->val_int()); +} + +inline +ulonglong TABLE::vers_start_id() const +{ + DBUG_ASSERT(versioned(VERS_TRX_ID)); + return static_cast<ulonglong>(vers_start_field()->val_int()); +} + #endif /* FIELD_INCLUDED */ diff --git a/sql/field_comp.cc b/sql/field_comp.cc new file mode 100644 index 00000000000..9a7b3a7c7e0 --- /dev/null +++ b/sql/field_comp.cc @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 MariaDB Foundation + + 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 */ + + +#include <my_global.h> +#include "sql_string.h" +#include "sql_class.h" +#include "field_comp.h" +#include <zlib.h> + + +static uint compress_zlib(THD *thd, char *to, const char *from, uint length) +{ + uint level= thd->variables.column_compression_zlib_level; + + if (level > 0) + { + z_stream stream; + int wbits= thd->variables.column_compression_zlib_wrap ? MAX_WBITS : + -MAX_WBITS; + uint strategy= thd->variables.column_compression_zlib_strategy; + /* Store only meaningful bytes of original data length. */ + uchar original_pack_length= number_storage_requirement(length); + + *to= 0x80 + original_pack_length + (wbits < 0 ? 8 : 0); + store_bigendian(length, (uchar*) to + 1, original_pack_length); + + stream.avail_in= length; + stream.next_in= (Bytef*) from; + + stream.avail_out= length - original_pack_length - 1; + stream.next_out= (Bytef*) to + original_pack_length + 1; + + stream.zalloc= 0; + stream.zfree= 0; + stream.opaque= 0; + + if (deflateInit2(&stream, level, Z_DEFLATED, wbits, 8, strategy) == Z_OK && + deflate(&stream, Z_FINISH) == Z_STREAM_END && + deflateEnd(&stream) == Z_OK) + return (uint) (stream.next_out - (Bytef*) to); + } + return 0; +} + + +static int uncompress_zlib(String *to, const uchar *from, uint from_length, + uint field_length) +{ + z_stream stream; + uchar original_pack_length; + int wbits; + ulonglong avail_out; + + original_pack_length= *from & 0x07; + wbits= *from & 8 ? -MAX_WBITS : MAX_WBITS; + + from++; + from_length--; + + if (from_length < original_pack_length) + { + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; + } + + avail_out= (ulonglong)read_bigendian(from, original_pack_length); + + if (avail_out > field_length) + { + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; + } + + stream.avail_out= (uint)avail_out; + if (to->alloc(stream.avail_out)) + return 1; + + stream.next_out= (Bytef*) to->ptr(); + + stream.avail_in= from_length - original_pack_length; + stream.next_in= (Bytef*) from + original_pack_length; + + stream.zalloc= 0; + stream.zfree= 0; + stream.opaque= 0; + + if (inflateInit2(&stream, wbits) == Z_OK && + inflate(&stream, Z_FINISH) == Z_STREAM_END && + inflateEnd(&stream) == Z_OK) + { + to->length(stream.total_out); + return 0; + } + my_error(ER_ZLIB_Z_DATA_ERROR, MYF(0)); + return 1; +} + + +Compression_method compression_methods[MAX_COMPRESSION_METHODS]= +{ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { "zlib", compress_zlib, uncompress_zlib }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; diff --git a/sql/field_comp.h b/sql/field_comp.h new file mode 100644 index 00000000000..7eb8ab1e75e --- /dev/null +++ b/sql/field_comp.h @@ -0,0 +1,33 @@ +#ifndef FIELD_COMP_H_INCLUDED +#define FIELD_COMP_H_INCLUDED +/* Copyright (C) 2017 MariaDB Foundation + + 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 */ + + +#define MAX_COMPRESSION_METHODS 16 + +struct Compression_method +{ + const char *name; + uint (*compress)(THD *thd, char *to, const char *from, uint length); + int (*uncompress)(String *to, const uchar *from, uint from_length, + uint field_length); +}; + + +extern Compression_method compression_methods[MAX_COMPRESSION_METHODS]; +#define zlib_compression_method (&compression_methods[8]) + +#endif diff --git a/sql/field_conv.cc b/sql/field_conv.cc index a3c488a637d..d648c90e114 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -25,7 +25,7 @@ gives much more speed. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // THD #include <m_ctype.h> @@ -122,6 +122,7 @@ static int set_bad_null_error(Field *field, int err) field->set_warning(Sql_condition::WARN_LEVEL_WARN, err, 1); /* fall through */ case CHECK_FIELD_IGNORE: + case CHECK_FIELD_EXPRESSION: return 0; case CHECK_FIELD_ERROR_FOR_NULL: if (!field->table->in_use->no_errors) @@ -388,7 +389,7 @@ static void do_field_varbinary_pre50(Copy_field *copy) copy->from_field->val_str(©->tmp); /* Use the same function as in 4.1 to trim trailing spaces */ - uint length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(), + size_t length= my_lengthsp_8bit(&my_charset_bin, copy->tmp.c_ptr_quick(), copy->from_field->field_length); copy->to_field->store(copy->tmp.c_ptr_quick(), length, @@ -480,7 +481,7 @@ static void do_cut_string_complex(Copy_field *copy) (char*) copy->from_ptr, (char*) from_end, copy->to_length / cs->mbmaxlen); - uint copy_length= prefix.length(); + size_t copy_length= prefix.length(); if (copy->to_length < copy_length) copy_length= copy->to_length; memcpy(copy->to_ptr, copy->from_ptr, copy_length); @@ -528,7 +529,8 @@ static void do_varstring1(Copy_field *copy) if (length > copy->to_length- 1) { length=copy->to_length - 1; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -547,7 +549,7 @@ static void do_varstring1_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_ptr, from_length, to_char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -562,7 +564,8 @@ static void do_varstring2(Copy_field *copy) if (length > copy->to_length- HA_KEY_BLOB_LENGTH) { length=copy->to_length-HA_KEY_BLOB_LENGTH; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -582,7 +585,7 @@ static void do_varstring2_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_beg, from_length, char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -754,7 +757,8 @@ Field::Copy_func *Field_varstring::get_copy_func(const Field *from) const return do_field_varbinary_pre50; if (Field_varstring::real_type() != from->real_type() || Field_varstring::charset() != from->charset() || - length_bytes != ((const Field_varstring*) from)->length_bytes) + length_bytes != ((const Field_varstring*) from)->length_bytes || + !compression_method() != !from->compression_method()) return do_field_string; return length_bytes == 1 ? (from->charset()->mbmaxlen == 1 ? do_varstring1 : do_varstring1_mb) : diff --git a/sql/filesort.cc b/sql/filesort.cc index 476b0bfb7dd..00dfa08bba8 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -22,12 +22,9 @@ Sorts a database */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "filesort.h" -#ifdef HAVE_STDDEF_H -#include <stddef.h> /* for macro offsetof */ -#endif #include <m_ctype.h> #include "sql_sort.h" #include "probes_mysql.h" @@ -37,7 +34,6 @@ #include "bounded_queue.h" #include "filesort_utils.h" #include "sql_select.h" -#include "log_slow.h" #include "debug_sync.h" /// How to write record_ref. @@ -72,7 +68,7 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff, uchar *buff_end); static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info, TABLE *table, - ha_rows records, ulong memory_available); + ha_rows records, size_t memory_available); void Sort_param::init_for_filesort(uint sortlen, TABLE *table, ulong max_length_for_sort_data, @@ -93,7 +89,10 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, table->field, sort_length, &addon_buf); } if (addon_field) - res_length= addon_buf.length; + { + DBUG_ASSERT(addon_buf.length < UINT_MAX32); + res_length= (uint)addon_buf.length; + } else { res_length= ref_length; @@ -103,7 +102,7 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table, */ sort_length+= ref_length; } - rec_length= sort_length + addon_buf.length; + rec_length= sort_length + (uint)addon_buf.length; max_rows= maxrows; } @@ -1030,8 +1029,8 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item, if (use_strnxfrm(cs)) { - uint tmp_length __attribute__((unused)); - tmp_length= cs->coll->strnxfrm(cs, to, sort_field->length, + IF_DBUG(size_t tmp_length= ,) + cs->coll->strnxfrm(cs, to, sort_field->length, item->max_char_length() * cs->strxfrm_multiply, (uchar*) res->ptr(), res->length(), @@ -1350,10 +1349,10 @@ static bool save_index(Sort_param *param, uint count, false - PQ will be slower than merge-sort, or there is not enough memory. */ -bool check_if_pq_applicable(Sort_param *param, +static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *filesort_info, TABLE *table, ha_rows num_rows, - ulong memory_available) + size_t memory_available) { DBUG_ENTER("check_if_pq_applicable"); @@ -1375,7 +1374,7 @@ bool check_if_pq_applicable(Sort_param *param, DBUG_RETURN(false); } - ulong num_available_keys= + size_t num_available_keys= memory_available / (param->rec_length + sizeof(char*)); // We need 1 extra record in the buffer, when using PQ. param->max_keys_per_buffer= (uint) param->max_rows + 1; @@ -1405,7 +1404,7 @@ bool check_if_pq_applicable(Sort_param *param, // Try to strip off addon fields. if (param->addon_field) { - const ulong row_length= + const size_t row_length= param->sort_length + param->ref_length + sizeof(char*); num_available_keys= memory_available / row_length; @@ -1415,7 +1414,7 @@ bool check_if_pq_applicable(Sort_param *param, const double sort_merge_cost= get_merge_many_buffs_cost_fast(num_rows, num_available_keys, - row_length); + (uint)row_length); /* PQ has cost: (insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID + @@ -1887,7 +1886,7 @@ Type_handler_string_result::sortlength(THD *thd, set_if_smaller(sortorder->length, thd->variables.max_sort_length); if (use_strnxfrm((cs= item->collation.collation))) { - sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length); + sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length); } else if (cs == &my_charset_bin) { @@ -1970,7 +1969,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, if (use_strnxfrm((cs=sortorder->field->sort_charset()))) { *multi_byte_charset= true; - sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length); + sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length); } if (sortorder->field->maybe_null()) length++; // Place for NULL marker diff --git a/sql/filesort.h b/sql/filesort.h index 2b4f7ac2654..bd1d81f91ef 100644 --- a/sql/filesort.h +++ b/sql/filesort.h @@ -17,7 +17,7 @@ #define FILESORT_INCLUDED #include "my_base.h" /* ha_rows */ -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" #include "filesort_utils.h" class SQL_SELECT; diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc index cb0b2d52b6f..b39bb880c15 100644 --- a/sql/filesort_utils.cc +++ b/sql/filesort_utils.cc @@ -13,11 +13,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "filesort_utils.h" #include "sql_const.h" #include "sql_sort.h" #include "table.h" -#include "my_sys.h" namespace { diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h index d537b602edf..544cd3e0f2b 100644 --- a/sql/filesort_utils.h +++ b/sql/filesort_utils.h @@ -16,7 +16,6 @@ #ifndef FILESORT_UTILS_INCLUDED #define FILESORT_UTILS_INCLUDED -#include "my_global.h" #include "my_base.h" #include "sql_array.h" diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc index ab48542add6..ce1f4394ebd 100644 --- a/sql/gcalc_slicescan.cc +++ b/sql/gcalc_slicescan.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #include <m_string.h> @@ -137,13 +137,13 @@ static void GCALC_DBUG_PRINT_PI(const Gcalc_heap::Info *pi) static void GCALC_DBUG_PRINT_SLICE(const char *header, const Gcalc_scan_iterator::point *slice) { - int nbuf; + size_t nbuf; char buf[1024]; nbuf= strlen(header); strcpy(buf, header); for (; slice; slice= slice->get_next()) { - int lnbuf= nbuf; + size_t lnbuf= nbuf; lnbuf+= sprintf(buf + lnbuf, "%d\t", slice->thread); lnbuf+= sprintf(buf + lnbuf, "%s\t", gcalc_ev_name(slice->event)); @@ -170,7 +170,7 @@ static void GCALC_DBUG_PRINT_SLICE(const char *header, Gcalc_dyn_list::Gcalc_dyn_list(size_t blk_size, size_t sizeof_item): m_blk_size(blk_size - ALLOC_ROOT_MIN_BLOCK_SIZE), m_sizeof_item(ALIGN_SIZE(sizeof_item)), - m_points_per_blk((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item), + m_points_per_blk((uint)((m_blk_size - PH_DATA_OFFSET) / m_sizeof_item)), m_blk_hook(&m_first_blk), m_free(NULL), m_keep(NULL) diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc index 71118ae1c9f..2aeaafc1a76 100644 --- a/sql/gcalc_tools.cc +++ b/sql/gcalc_tools.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #ifdef HAVE_SPATIAL diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 3a3273d279b..b2bc1aee091 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -78,11 +78,9 @@ So, we can read full search-structure as 32-bit word */ #define NO_YACC_SYMBOLS -#include <my_global.h> +#include "mariadb.h" #include "mysql_version.h" #include "lex.h" -#include <stdlib.h> -#include <stdio.h> #include <string.h> #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc index bd2b9728177..b36df5e94be 100644 --- a/sql/gen_lex_token.cc +++ b/sql/gen_lex_token.cc @@ -14,9 +14,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> -#include <stdlib.h> -#include <stdio.h> +#include "mariadb.h" #include <string.h> /* We only need the tokens here */ @@ -132,6 +130,10 @@ void compute_tokens() set_token(WITH_CUBE_SYM, "WITH CUBE"); set_token(WITH_ROLLUP_SYM, "WITH ROLLUP"); + set_token(WITH_SYSTEM_SYM, "WITH SYSTEM"); + set_token(FOR_SYSTEM_TIME_SYM, "FOR SYSTEM_TIME"); + set_token(VALUES_IN_SYM, "VALUES IN"); + set_token(VALUES_LESS_SYM, "VALUES LESS"); set_token(NOT2_SYM, "!"); set_token(OR2_SYM, "|"); set_token(PARAM_MARKER, "?"); diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc index c1b5cfbe254..e75800d8986 100644 --- a/sql/group_by_handler.cc +++ b/sql/group_by_handler.cc @@ -21,6 +21,7 @@ upper level. */ +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" diff --git a/sql/gstream.cc b/sql/gstream.cc index adb46083621..ff3604ac7a4 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -18,7 +18,7 @@ NOTE: These functions assumes that the string is end \0 terminated! */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "gstream.h" #include "m_string.h" // LEX_STRING diff --git a/sql/gstream.h b/sql/gstream.h index f10b7e9b830..b9310b716ba 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -17,8 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NULL, NullS */ -#include "my_sys.h" /* MY_ALLOW_ZERO_PTR */ +#include <my_sys.h> /* MY_ALLOW_ZERO_PTR */ #include "m_ctype.h" /* my_charset_latin1, my_charset_bin */ class Gis_read_stream diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index f0cb7fead66..4ec6f3dfa38 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -35,7 +35,7 @@ Partitioning lays the foundation for more manageable databases that are extremely large. It does also lay the foundation for more parallelism in the execution of queries. This functionality will grow with later - versions of MySQL. + versions of MySQL/MariaDB. The partition is setup to use table locks. It implements an partition "SHARE" that is inserted into a hash by table name. You can use this to store @@ -46,7 +46,7 @@ if this file. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // append_file_to_dir #include "create_options.h" @@ -58,6 +58,7 @@ #include "sql_plugin.h" #include "sql_show.h" // append_identifier #include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE +#include "sql_select.h" #include "debug_sync.h" @@ -73,9 +74,7 @@ HA_REC_NOT_IN_SEQ | \ HA_CAN_REPAIR) #define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \ - HA_CAN_FULLTEXT | \ HA_DUPLICATE_POS | \ - HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED | \ HA_READ_BEFORE_WRITE_REMOVAL |\ HA_CAN_TABLES_WITHOUT_ROLLBACK) @@ -90,10 +89,7 @@ static handler *partition_create_handler(handlerton *hton, TABLE_SHARE *share, MEM_ROOT *mem_root); static uint partition_flags(); -static uint alter_table_flags(uint flags); - -extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); -extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); +static ulonglong alter_table_flags(ulonglong flags); /* If frm_error() is called then we will use this to to find out what file @@ -126,7 +122,6 @@ static void init_partition_psi_keys(void) static int partition_initialize(void *p) { - handlerton *partition_hton; partition_hton= (handlerton *)p; @@ -160,18 +155,11 @@ static int partition_initialize(void *p) bool Partition_share::init(uint num_parts) { DBUG_ENTER("Partition_share::init"); - mysql_mutex_init(key_partition_auto_inc_mutex, - &auto_inc_mutex, - MY_MUTEX_INIT_FAST); auto_inc_initialized= false; partition_name_hash_initialized= false; next_auto_inc_val= 0; - partitions_share_refs= new Parts_share_refs; - if (!partitions_share_refs) - DBUG_RETURN(true); - if (partitions_share_refs->init(num_parts)) + if (partitions_share_refs.init(num_parts)) { - delete partitions_share_refs; DBUG_RETURN(true); } DBUG_RETURN(false); @@ -189,7 +177,7 @@ bool Partition_share::init(uint num_parts) New partition object */ -static handler *partition_create_handler(handlerton *hton, +static handler *partition_create_handler(handlerton *hton, TABLE_SHARE *share, MEM_ROOT *mem_root) { @@ -226,14 +214,12 @@ static uint partition_flags() return HA_CAN_PARTITION; } -static uint alter_table_flags(uint flags __attribute__((unused))) +static ulonglong alter_table_flags(ulonglong /* flags */) { return (HA_PARTITION_FUNCTION_SUPPORTED | HA_FAST_CHANGE_PARTITION); } -const uint32 ha_partition::NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; - /* Constructor method @@ -249,12 +235,19 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(table)"); - init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); DBUG_VOID_RETURN; } +/* Initialize all partition variables */ + +void ha_partition::ha_partition_init() +{ + init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0)); + init_handler_variables(); +} + /* Constructor method @@ -271,8 +264,7 @@ ha_partition::ha_partition(handlerton *hton, partition_info *part_info) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); DBUG_ASSERT(part_info); - init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); m_part_info= part_info; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); @@ -298,8 +290,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(clone)"); - init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0)); - init_handler_variables(); + ha_partition_init(); m_part_info= part_info_arg; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); @@ -362,6 +353,7 @@ void ha_partition::init_handler_variables() m_curr_key_info[0]= NULL; m_curr_key_info[1]= NULL; m_part_func_monotonicity_info= NON_MONOTONIC; + m_key_not_found= FALSE; auto_increment_lock= FALSE; auto_increment_safe_stmt_log_lock= FALSE; /* @@ -376,6 +368,30 @@ void ha_partition::init_handler_variables() part_share= NULL; m_new_partitions_share_refs.empty(); m_part_ids_sorted_by_num_of_records= NULL; + m_partitions_to_open= NULL; + + m_range_info= NULL; + m_mrr_full_buffer_size= 0; + m_mrr_new_full_buffer_size= 0; + m_mrr_full_buffer= NULL; + m_mrr_range_first= NULL; + + m_pre_calling= FALSE; + m_pre_call_use_parallel= FALSE; + + ft_first= ft_current= NULL; + bulk_access_executing= FALSE; // For future + + /* + Clear bitmaps to allow on one to call my_bitmap_free() on them at any time + */ + my_bitmap_clear(&m_bulk_insert_started); + my_bitmap_clear(&m_locked_partitions); + my_bitmap_clear(&m_partitions_to_reset); + my_bitmap_clear(&m_key_not_found_partitions); + my_bitmap_clear(&m_mrr_used_partitions); + my_bitmap_clear(&m_opened_partitions); + m_file_sample= NULL; #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; @@ -385,9 +401,9 @@ void ha_partition::init_handler_variables() const char *ha_partition::table_type() const -{ +{ // we can do this since we only support a single engine type - return m_file[0]->table_type(); + return m_file[0]->table_type(); } @@ -688,8 +704,9 @@ int ha_partition::create(const char *name, TABLE *table_arg, partition_element *part_elem; handler **file, **abort_file; DBUG_ENTER("ha_partition::create"); + DBUG_PRINT("enter", ("name: '%s'", name)); - DBUG_ASSERT(*fn_rext((char*)name) == '\0'); + DBUG_ASSERT(!fn_frm_ext(name)); /* Not allowed to create temporary partitioned tables */ if (create_info && create_info->tmp_table()) @@ -701,7 +718,6 @@ int ha_partition::create(const char *name, TABLE *table_arg, if (get_from_handler_file(name, ha_thd()->mem_root, false)) DBUG_RETURN(TRUE); DBUG_ASSERT(m_file_buffer); - DBUG_PRINT("enter", ("name: (%s)", name)); name_buffer_ptr= m_name_buffer_ptr; file= m_file; /* @@ -964,7 +980,7 @@ int ha_partition::rename_partitions(const char *path) When state is PART_IS_CHANGED it means that we have created a new TEMP partition that is to be renamed to normal partition name and we are to delete the old partition with currently the normal name. - + We perform this operation by 1) Delete old partition with normal partition name 2) Signal this in table log entry @@ -1199,7 +1215,7 @@ int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt) DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS)); } - + /* Handle optimize/analyze/check/repair of one partition @@ -1221,7 +1237,7 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, int error; handler *file= m_file[part_id]; DBUG_ENTER("handle_opt_part"); - DBUG_PRINT("enter", ("flag = %u", flag)); + DBUG_PRINT("enter", ("flag: %u", flag)); if (flag == OPTIMIZE_PARTS) error= file->ha_optimize(thd, check_opt); @@ -1265,24 +1281,24 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, /* - print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE + print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE (modelled after mi_check_print_msg) TODO: move this into the handler, or rewrite mysql_admin_table. */ -static bool print_admin_msg(THD* thd, uint len, +bool print_admin_msg(THD* thd, uint len, const char* msg_type, const char* db_name, String &table_name, const char* op_name, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 7, 8); -static bool print_admin_msg(THD* thd, uint len, +bool print_admin_msg(THD* thd, uint len, const char* msg_type, const char* db_name, String &table_name, const char* op_name, const char *fmt, ...) { va_list args; Protocol *protocol= thd->protocol; - uint length; - uint msg_length; + size_t length; + size_t msg_length; char name[NAME_LEN*2+2]; char *msgbuf; bool error= true; @@ -1294,7 +1310,7 @@ static bool print_admin_msg(THD* thd, uint len, va_end(args); if (msg_length >= (len - 1)) goto err; - msgbuf[len - 1] = 0; // healthy paranoia + msgbuf[len - 1]= 0; // healthy paranoia if (!thd->vio_ok()) @@ -1303,7 +1319,7 @@ static bool print_admin_msg(THD* thd, uint len, goto err; } - length=(uint) (strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name); + length=(size_t)(strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name); /* TODO: switch from protocol to push_warning here. The main reason we didn't it yet is parallel repair, which threads have no THD object accessible via @@ -1388,7 +1404,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, opt_op_name[flag], - "Subpartition %s returned error", + "Subpartition %s returned error", sub_elem->partition_name); } /* reset part_state for the remaining partitions */ @@ -1414,7 +1430,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, { print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, - opt_op_name[flag], "Partition %s returned error", + opt_op_name[flag], "Partition %s returned error", part_elem->partition_name); } /* reset part_state for the remaining partitions */ @@ -1456,7 +1472,7 @@ bool ha_partition::check_and_repair(THD *thd) } while (*(++file)); DBUG_RETURN(FALSE); } - + /** @breif Check if the table can be automatically repaired @@ -1496,7 +1512,7 @@ bool ha_partition::is_crashed() const } while (*(++file)); DBUG_RETURN(FALSE); } - + /* Prepare by creating a new partition @@ -1541,7 +1557,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl, if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem))) goto error_create; - tbl->s->connect_string = p_elem->connect_string; + if (!(file->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + tbl->s->connect_string= p_elem->connect_string; if ((error= file->ha_create(part_name, tbl, create_info))) { /* @@ -1876,7 +1893,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, in the partitions. */ - uint disable_non_uniq_indexes = indexes_are_disabled(); + uint disable_non_uniq_indexes= indexes_are_disabled(); i= 0; part_count= 0; @@ -2104,7 +2121,7 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) HA_STATUS_AUTO is optimized so it will not always be forwarded to all partitions, but HA_STATUS_VARIABLE will. */ - info(HA_STATUS_VARIABLE); + info(HA_STATUS_VARIABLE | HA_STATUS_OPEN); info(HA_STATUS_AUTO); @@ -2115,10 +2132,11 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) DATA DIRECTORY and INDEX DIRECTORY are never applied to the whole partitioned table, only its parts. */ - my_bool from_alter = (create_info->data_file_name == (const char*) -1); - create_info->data_file_name= create_info->index_file_name = NULL; + my_bool from_alter= (create_info->data_file_name == (const char*) -1); + create_info->data_file_name= create_info->index_file_name= NULL; - create_info->connect_string= null_clex_str; + if (!(m_file[0]->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + create_info->connect_string= null_clex_str; /* We do not need to update the individual partition DATA DIRECTORY settings @@ -2136,8 +2154,8 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) List_iterator<partition_element> part_it(m_part_info->partitions); partition_element *part_elem, *sub_elem; uint num_subparts= m_part_info->num_subparts; - uint num_parts = num_subparts ? m_file_tot_parts / num_subparts - : m_file_tot_parts; + uint num_parts= (num_subparts ? m_file_tot_parts / num_subparts : + m_file_tot_parts); HA_CREATE_INFO dummy_info; memset(&dummy_info, 0, sizeof(dummy_info)); @@ -2253,7 +2271,7 @@ void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) comment Original comment RETURN VALUE - new comment + new comment DESCRIPTION No comment changes so far @@ -2380,7 +2398,7 @@ uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type) /* Here we rely on the fact that all tables are of the same type */ uint8 type= m_file[0]->table_cache_type(); (*tables_type)|= type; - DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts)); + DBUG_PRINT("enter", ("cnt: %u", (uint) m_tot_parts)); /* We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT: HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table @@ -2421,7 +2439,7 @@ reg_query_cache_dependant_table(THD *thd, (++(*block_table))->n= ++(*n); if (!cache->insert_table(thd, cache_key_len, cache_key, (*block_table), - table_share->db.length, + (uint32) table_share->db.length, (uint8) (cache_key_len - table_share->table_cache_key.length), type, @@ -2535,7 +2553,7 @@ register_query_cache_dependant_tables(THD *thd, @return status @retval TRUE Error @retval FALSE Success - + @details Set up 1) Comment on partition @@ -2545,12 +2563,12 @@ register_query_cache_dependant_tables(THD *thd, */ int ha_partition::set_up_table_before_create(TABLE *tbl, - const char *partition_name_with_path, + const char *partition_name_with_path, HA_CREATE_INFO *info, partition_element *part_elem) { int error= 0; - const char *partition_name; + LEX_CSTRING part_name; THD *thd= ha_thd(); DBUG_ENTER("set_up_table_before_create"); @@ -2560,15 +2578,16 @@ int ha_partition::set_up_table_before_create(TABLE *tbl, DBUG_RETURN(1); tbl->s->max_rows= part_elem->part_max_rows; tbl->s->min_rows= part_elem->part_min_rows; - partition_name= strrchr(partition_name_with_path, FN_LIBCHAR); + part_name.str= strrchr(partition_name_with_path, FN_LIBCHAR)+1; + part_name.length= strlen(part_name.str); if ((part_elem->index_file_name && (error= append_file_to_dir(thd, (const char**)&part_elem->index_file_name, - partition_name+1))) || + &part_name))) || (part_elem->data_file_name && (error= append_file_to_dir(thd, (const char**)&part_elem->data_file_name, - partition_name+1)))) + &part_name)))) { DBUG_RETURN(error); } @@ -2625,10 +2644,10 @@ static uint name_add(char *dest, const char *first_name, const char *sec_name) bool ha_partition::create_handler_file(const char *name) { partition_element *part_elem, *subpart_elem; - uint i, j, part_name_len, subpart_name_len; - uint tot_partition_words, tot_name_len, num_parts; - uint tot_parts= 0; - uint tot_len_words, tot_len_byte, chksum, tot_name_words; + size_t i, j, part_name_len, subpart_name_len; + size_t tot_partition_words, tot_name_len, num_parts; + size_t tot_parts= 0; + size_t tot_len_words, tot_len_byte, chksum, tot_name_words; char *name_buffer_ptr; uchar *file_buffer, *engine_array; bool result= TRUE; @@ -2640,8 +2659,7 @@ bool ha_partition::create_handler_file(const char *name) DBUG_ENTER("create_handler_file"); num_parts= m_part_info->partitions.elements; - DBUG_PRINT("info", ("table name = %s, num_parts = %u", name, - num_parts)); + DBUG_PRINT("enter", ("table name: %s num_parts: %zu", name, num_parts)); tot_name_len= 0; for (i= 0; i < num_parts; i++) { @@ -2760,7 +2778,7 @@ bool ha_partition::create_handler_file(const char *name) { uchar buffer[4]; part_elem= part_it++; - uint length = part_elem->connect_string.length; + size_t length= part_elem->connect_string.length; int4store(buffer, length); if (my_write(file, buffer, 4, MYF(MY_WME | MY_NABP)) || my_write(file, (uchar *) part_elem->connect_string.str, length, @@ -2958,7 +2976,7 @@ bool ha_partition::read_par_file(const char *name) if (chksum) goto err2; m_tot_parts= uint4korr((file_buffer) + PAR_NUM_PARTS_OFFSET); - DBUG_PRINT("info", ("No of parts = %u", m_tot_parts)); + DBUG_PRINT("info", ("No of parts: %u", m_tot_parts)); tot_partition_words= (m_tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; tot_name_len_offset= file_buffer + PAR_ENGINES_OFFSET + @@ -3064,7 +3082,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root) } my_afree(engine_array); - + if (create_handlers(mem_root)) { clear_handler_file(); @@ -3151,7 +3169,7 @@ bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id, { PART_NAME_DEF *part_def; uchar *part_name; - uint part_name_length; + size_t part_name_length; DBUG_ENTER("ha_partition::insert_partition_name_in_hash"); /* Calculate and store the length here, to avoid doing it when @@ -3171,7 +3189,7 @@ bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id, DBUG_RETURN(true); memcpy(part_name, name, part_name_length + 1); part_def->partition_name= part_name; - part_def->length= part_name_length; + part_def->length= (uint)part_name_length; part_def->part_id= part_id; part_def->is_subpart= is_subpart; if (my_hash_insert(&part_share->partition_name_hash, (uchar *) part_def)) @@ -3286,9 +3304,8 @@ bool ha_partition::set_ha_share_ref(Handler_share **ha_share_arg) DBUG_RETURN(true); if (!(part_share= get_share())) DBUG_RETURN(true); - DBUG_ASSERT(part_share->partitions_share_refs); - DBUG_ASSERT(part_share->partitions_share_refs->num_parts >= m_tot_parts); - ha_shares= part_share->partitions_share_refs->ha_shares; + DBUG_ASSERT(part_share->partitions_share_refs.num_parts >= m_tot_parts); + ha_shares= part_share->partitions_share_refs.ha_shares; for (i= 0; i < m_tot_parts; i++) { if (m_file[i]->set_ha_share_ref(&ha_shares[i])) @@ -3347,63 +3364,59 @@ void ha_partition::free_partition_bitmaps() my_bitmap_free(&m_locked_partitions); my_bitmap_free(&m_partitions_to_reset); my_bitmap_free(&m_key_not_found_partitions); + my_bitmap_free(&m_opened_partitions); + my_bitmap_free(&m_mrr_used_partitions); } /** Helper function for initializing all internal bitmaps. + + Note: + All bitmaps, including partially allocated, are freed in + free_partion_bitmaps() */ bool ha_partition::init_partition_bitmaps() { DBUG_ENTER("ha_partition::init_partition_bitmaps"); + /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) DBUG_RETURN(true); - bitmap_clear_all(&m_bulk_insert_started); /* Initialize the bitmap we use to keep track of locked partitions */ if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); DBUG_RETURN(true); - } - bitmap_clear_all(&m_locked_partitions); /* Initialize the bitmap we use to keep track of partitions which may have something to reset in ha_reset(). */ if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); - my_bitmap_free(&m_locked_partitions); DBUG_RETURN(true); - } - bitmap_clear_all(&m_partitions_to_reset); /* Initialize the bitmap we use to keep track of partitions which returned HA_ERR_KEY_NOT_FOUND from index_read_map. */ if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) - { - my_bitmap_free(&m_bulk_insert_started); - my_bitmap_free(&m_locked_partitions); - my_bitmap_free(&m_partitions_to_reset); DBUG_RETURN(true); - } - bitmap_clear_all(&m_key_not_found_partitions); - m_key_not_found= false; + + if (bitmap_init(&m_mrr_used_partitions, NULL, m_tot_parts, TRUE)) + DBUG_RETURN(true); + + if (my_bitmap_init(&m_opened_partitions, NULL, m_tot_parts, FALSE)) + DBUG_RETURN(true); + + m_file_sample= NULL; + /* Initialize the bitmap for read/lock_partitions */ if (!m_is_clone_of) { DBUG_ASSERT(!m_clone_mem_root); if (m_part_info->set_partition_bitmaps(NULL)) - { - free_partition_bitmaps(); DBUG_RETURN(true); - } } DBUG_RETURN(false); } @@ -3434,7 +3447,6 @@ bool ha_partition::init_partition_bitmaps() int ha_partition::open(const char *name, int mode, uint test_if_locked) { - char *name_buffer_ptr; int error= HA_ERR_INITIALIZATION; handler **file; char name_buff[FN_REFLEN + 1]; @@ -3448,7 +3460,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_part_field_array= m_part_info->full_part_field_array; if (get_from_handler_file(name, &table->mem_root, MY_TEST(m_is_clone_of))) DBUG_RETURN(error); - name_buffer_ptr= m_name_buffer_ptr; if (populate_partition_name_hash()) { DBUG_RETURN(HA_ERR_INITIALIZATION); @@ -3468,13 +3479,37 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) } if (init_partition_bitmaps()) - DBUG_RETURN(error); - - DBUG_ASSERT(m_part_info); + goto err_alloc; + + if ((error= m_part_info->set_partition_bitmaps(m_partitions_to_open))) + goto err_alloc; + + /* Allocate memory used with MMR */ + if (!(m_range_info= (void **) + my_multi_malloc(MYF(MY_WME), + &m_range_info, sizeof(range_id_t) * m_tot_parts, + &m_stock_range_seq, sizeof(uint) * m_tot_parts, + &m_mrr_buffer, sizeof(HANDLER_BUFFER) * m_tot_parts, + &m_mrr_buffer_size, sizeof(uint) * m_tot_parts, + &m_part_mrr_range_length, sizeof(uint) * m_tot_parts, + &m_part_mrr_range_first, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts, + &m_part_mrr_range_current, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts, + &m_partition_part_key_multi_range_hld, + sizeof(PARTITION_PART_KEY_MULTI_RANGE_HLD) * + m_tot_parts, + NullS))) + goto err_alloc; + + bzero(m_mrr_buffer, m_tot_parts * sizeof(HANDLER_BUFFER)); + bzero(m_part_mrr_range_first, + sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts); if (m_is_clone_of) { uint i, alloc_len; + char *name_buffer_ptr; DBUG_ASSERT(m_clone_mem_root); /* Allocate an array of handler pointers for the partitions handlers. */ alloc_len= (m_tot_parts + 1) * sizeof(handler*); @@ -3484,6 +3519,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) goto err_alloc; } memset(m_file, 0, alloc_len); + name_buffer_ptr= m_name_buffer_ptr; /* Populate them by cloning the original partitions. This also opens them. Note that file->ref is allocated too. @@ -3491,6 +3527,9 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) file= m_is_clone_of->m_file; for (i= 0; i < m_tot_parts; i++) { + if (!bitmap_is_set(&m_is_clone_of->m_opened_partitions, i)) + continue; + if ((error= create_partition_name(name_buff, sizeof(name_buff), name, name_buffer_ptr, NORMAL_PART_NAME, FALSE))) goto err_handler; @@ -3501,36 +3540,35 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) file= &m_file[i]; goto err_handler; } + if (!m_file_sample) + m_file_sample= m_file[i]; name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + bitmap_set_bit(&m_opened_partitions, i); } } else { - file= m_file; - do - { - if ((error= create_partition_name(name_buff, sizeof(name_buff), name, - name_buffer_ptr, NORMAL_PART_NAME, FALSE))) - goto err_handler; - table->s->connect_string = m_connect_string[(uint)(file-m_file)]; - if ((error= (*file)->ha_open(table, name_buff, mode, - test_if_locked | HA_OPEN_NO_PSI_CALL))) - goto err_handler; - bzero(&table->s->connect_string, sizeof(LEX_STRING)); - if (m_file == file) - m_num_locks= (*file)->lock_count(); - DBUG_ASSERT(m_num_locks == (*file)->lock_count()); - name_buffer_ptr+= strlen(name_buffer_ptr) + 1; - } while (*(++file)); + if ((error= open_read_partitions(name_buff, sizeof(name_buff)))) + goto err_handler; + m_num_locks= m_file_sample->lock_count(); } - + /* + We want to know the upper bound for locks, to allocate enough memory. + There is no performance lost if we simply return in lock_count() the + maximum number locks needed, only some minor over allocation of memory + in get_lock_data(). + */ + m_num_locks*= m_tot_parts; + file= m_file; - ref_length= (*file)->ref_length; - check_table_flags= (((*file)->ha_table_flags() & + ref_length= get_open_file_sample()->ref_length; + check_table_flags= ((get_open_file_sample()->ha_table_flags() & ~(PARTITION_DISABLED_TABLE_FLAGS)) | (PARTITION_ENABLED_TABLE_FLAGS)); while (*(++file)) { + if (!bitmap_is_set(&m_opened_partitions, (uint)(file - m_file))) + continue; /* MyISAM can have smaller ref_length for partitions with MAX_ROWS set */ set_if_bigger(ref_length, ((*file)->ref_length)); /* @@ -3543,12 +3581,12 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { error= HA_ERR_INITIALIZATION; /* set file to last handler, so all of them are closed */ - file = &m_file[m_tot_parts - 1]; + file= &m_file[m_tot_parts - 1]; goto err_handler; } } - key_used_on_scan= m_file[0]->key_used_on_scan; - implicit_emptied= m_file[0]->implicit_emptied; + key_used_on_scan= get_open_file_sample()->key_used_on_scan; + implicit_emptied= get_open_file_sample()->implicit_emptied; /* Add 2 bytes for partition id in position ref length. ref_length=max_in_all_partitions(ref_length) + PARTITION_BYTES_IN_POS @@ -3574,15 +3612,21 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_part_info->part_expr->get_monotonicity_info(); else if (m_part_info->list_of_part_fields) m_part_func_monotonicity_info= MONOTONIC_STRICT_INCREASING; - info(HA_STATUS_VARIABLE | HA_STATUS_CONST); + info(HA_STATUS_VARIABLE | HA_STATUS_CONST | HA_STATUS_OPEN); DBUG_RETURN(0); err_handler: DEBUG_SYNC(ha_thd(), "partition_open_error"); + file= &m_file[m_tot_parts - 1]; while (file-- != m_file) - (*file)->ha_close(); + { + if (bitmap_is_set(&m_opened_partitions, (uint)(file - m_file))) + (*file)->ha_close(); + } err_alloc: free_partition_bitmaps(); + my_free(m_range_info); + m_range_info= 0; DBUG_RETURN(error); } @@ -3660,7 +3704,7 @@ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) /* Allocate new_handler->ref here because otherwise ha_open will allocate it - on this->table->mem_root and we will not be able to reclaim that memory + on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ if (!(new_handler->ref= (uchar*) alloc_root(mem_root, @@ -3702,20 +3746,69 @@ int ha_partition::close(void) { bool first= TRUE; handler **file; + uint i; + st_partition_ft_info *tmp_ft_info; DBUG_ENTER("ha_partition::close"); - DBUG_ASSERT(table->s == table_share); - destroy_record_priority_queue(); - free_partition_bitmaps(); DBUG_ASSERT(m_part_info); + + destroy_record_priority_queue(); + + for (; ft_first ; ft_first= tmp_ft_info) + { + tmp_ft_info= ft_first->next; + my_free(ft_first); + } + + /* Free active mrr_ranges */ + for (i= 0; i < m_tot_parts; i++) + { + if (m_part_mrr_range_first[i]) + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_first= + m_part_mrr_range_first[i]; + do + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_mrr_range_current; + tmp_mrr_range_current= tmp_mrr_range_first; + tmp_mrr_range_first= tmp_mrr_range_first->next; + my_free(tmp_mrr_range_current); + } while (tmp_mrr_range_first); + } + } + if (m_mrr_range_first) + { + do + { + m_mrr_range_current= m_mrr_range_first; + m_mrr_range_first= m_mrr_range_first->next; + if (m_mrr_range_current->key[0]) + my_free(m_mrr_range_current->key[0]); + if (m_mrr_range_current->key[1]) + my_free(m_mrr_range_current->key[1]); + my_free(m_mrr_range_current); + } while (m_mrr_range_first); + } + my_free(m_range_info); + m_range_info= NULL; // Safety + + if (m_mrr_full_buffer) + { + my_free(m_mrr_full_buffer); + m_mrr_full_buffer= NULL; + m_mrr_full_buffer_size= 0; + } file= m_file; repeat: do { - (*file)->ha_close(); + if (!first || bitmap_is_set(&m_opened_partitions, (uint)(file - m_file))) + (*file)->ha_close(); } while (*(++file)); + free_partition_bitmaps(); + if (first && m_added_file && m_added_file[0]) { file= m_added_file; @@ -3769,7 +3862,7 @@ repeat: int ha_partition::external_lock(THD *thd, int lock_type) { - uint error; + int error; uint i, first_used_partition; MY_BITMAP *used_partitions; DBUG_ENTER("ha_partition::external_lock"); @@ -3787,7 +3880,7 @@ int ha_partition::external_lock(THD *thd, int lock_type) i < m_tot_parts; i= bitmap_get_next_set(used_partitions, i)) { - DBUG_PRINT("info", ("external_lock(thd, %d) part %d", lock_type, i)); + DBUG_PRINT("info", ("external_lock(thd, %d) part %u", lock_type, i)); if ((error= m_file[i]->ha_external_lock(thd, lock_type))) { if (lock_type != F_UNLCK) @@ -3903,7 +3996,7 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) { - DBUG_PRINT("info", ("store lock %d iteration", i)); + DBUG_PRINT("info", ("store lock %u iteration", i)); to= m_file[i]->store_lock(thd, to, lock_type); } } @@ -3964,25 +4057,14 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type) @returns Number of locks returned in call to store_lock @desc - Returns the number of store locks needed in call to store lock. - We return number of partitions we will lock multiplied with number of - locks needed by each partition. Assists the above functions in allocating - sufficient space for lock structures. + Returns the maxinum possible number of store locks needed in call to + store lock. */ uint ha_partition::lock_count() const { DBUG_ENTER("ha_partition::lock_count"); - /* - The caller want to know the upper bound, to allocate enough memory. - There is no performance lost if we simply return maximum number locks - needed, only some minor over allocation of memory in get_lock_data(). - - Also notice that this may be called for another thread != table->in_use, - when mysql_lock_abort_for_thread() is called. So this is more safe, then - using number of partitions after pruning. - */ - DBUG_RETURN(m_tot_parts * m_num_locks); + DBUG_RETURN(m_num_locks); } @@ -4061,7 +4143,7 @@ void ha_partition::try_semi_consistent_read(bool yes) { uint i; DBUG_ENTER("ha_partition::try_semi_consistent_read"); - + i= bitmap_get_first_set(&(m_part_info->read_partitions)); DBUG_ASSERT(i != MY_BIT_NONE); for (; @@ -4126,7 +4208,7 @@ int ha_partition::write_row(uchar * buf) sql_mode_t saved_sql_mode= thd->variables.sql_mode; bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null; DBUG_ENTER("ha_partition::write_row"); - DBUG_ASSERT(buf == m_rec0); + DBUG_PRINT("enter", ("partition this: %p", this)); /* If we have an auto_increment column and we are writing a changed row @@ -4134,15 +4216,8 @@ int ha_partition::write_row(uchar * buf) */ if (have_auto_increment) { - if (!part_share->auto_inc_initialized && - !table_share->next_number_keypart) - { - /* - If auto_increment in table_share is not initialized, start by - initializing it. - */ - info(HA_STATUS_AUTO); - } + if (!table_share->next_number_keypart) + update_next_auto_inc_val(); error= update_auto_increment(); /* @@ -4186,7 +4261,7 @@ int ha_partition::write_row(uchar * buf) goto exit; } m_last_part= part_id; - DBUG_PRINT("info", ("Insert in partition %d", part_id)); + DBUG_PRINT("info", ("Insert in partition %u", part_id)); start_part_bulk_insert(thd, part_id); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ @@ -4278,7 +4353,7 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) start_part_bulk_insert(thd, new_part_id); if (new_part_id == old_part_id) { - DBUG_PRINT("info", ("Update in partition %d", new_part_id)); + DBUG_PRINT("info", ("Update in partition %u", (uint) new_part_id)); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[new_part_id]->ha_update_row(old_data, new_data); reenable_binlog(thd); @@ -4298,8 +4373,8 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) This gives the same behavior for partitioned vs non partitioned tables. */ table->next_number_field= NULL; - DBUG_PRINT("info", ("Update from partition %d to partition %d", - old_part_id, new_part_id)); + DBUG_PRINT("info", ("Update from partition %u to partition %u", + (uint) old_part_id, (uint) new_part_id)); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[new_part_id]->ha_write_row((uchar*) new_data); reenable_binlog(thd); @@ -4307,6 +4382,15 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) if (error) goto exit; + if (m_part_info->part_type == VERSIONING_PARTITION) + { + uint sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + DBUG_ASSERT(m_tot_parts == m_part_info->num_parts * sub_factor); + uint lpart_id= new_part_id / sub_factor; + // lpart_id is HISTORY partition because new_part_id != old_part_id + m_part_info->vers_update_stats(thd, lpart_id); + } + tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[old_part_id]->ha_delete_row(old_data); reenable_binlog(thd); @@ -4335,8 +4419,11 @@ exit: bitmap_is_set(table->write_set, table->found_next_number_field->field_index)) { - if (!part_share->auto_inc_initialized) - info(HA_STATUS_AUTO); + update_next_auto_inc_val(); + /* + The following call is safe as part_share->auto_inc_initialized + (tested in the call) is guaranteed to be set for update statements. + */ set_auto_increment_if_higher(table->found_next_number_field); } DBUG_RETURN(error); @@ -4623,9 +4710,9 @@ void ha_partition::start_part_bulk_insert(THD *thd, uint part_id) DESCRIPTION If the estimated number of rows to insert is less than 10 (but not 0) the new buffer size is same as original buffer size. - In case of first partition of when partition function is monotonic + In case of first partition of when partition function is monotonic new buffer size is same as the original buffer size. - For rest of the partition total buffer of 10*original_size is divided + For rest of the partition total buffer of 10*original_size is divided equally if number of partition is more than 10 other wise each partition will be allowed to use original buffer size. */ @@ -4663,7 +4750,7 @@ long ha_partition::estimate_read_buffer_size(long original_size) If monotonic partitioning function was used guess that 50 % of the inserts goes to the first partition For all other cases, guess on equal distribution between the partitions -*/ +*/ ha_rows ha_partition::guess_bulk_insert_rows() { DBUG_ENTER("guess_bulk_insert_rows"); @@ -4672,7 +4759,7 @@ ha_rows ha_partition::guess_bulk_insert_rows() DBUG_RETURN(estimation_rows_to_insert); /* If first insert/partition and monotonic partition function, guess 50%. */ - if (!m_bulk_inserted_rows && + if (!m_bulk_inserted_rows && m_part_func_monotonicity_info != NON_MONOTONIC && m_tot_parts > 1) DBUG_RETURN(estimation_rows_to_insert / 2); @@ -4738,7 +4825,7 @@ int ha_partition::end_bulk_insert() >0 Error code 0 Success - DESCRIPTION + DESCRIPTION rnd_init() is called when the server wants the storage engine to do a table scan or when the server wants to access data through rnd_pos. @@ -4774,7 +4861,10 @@ int ha_partition::rnd_init(bool scan) */ if (bitmap_is_overlapping(&m_part_info->full_part_field_set, table->write_set)) + { + DBUG_PRINT("info", ("partition set full bitmap")); bitmap_set_all(table->read_set); + } else { /* @@ -4783,6 +4873,7 @@ int ha_partition::rnd_init(bool scan) fields of the partition functions are read such that we can calculate the partition id to place updated and deleted records. */ + DBUG_PRINT("info", ("partition set part_field bitmap")); bitmap_union(table->read_set, &m_part_info->full_part_field_set); } } @@ -4791,9 +4882,9 @@ int ha_partition::rnd_init(bool scan) DBUG_PRINT("info", ("m_part_info->read_partitions: %p", m_part_info->read_partitions.bitmap)); part_id= bitmap_get_first_set(&(m_part_info->read_partitions)); - DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id)); + DBUG_PRINT("info", ("m_part_spec.start_part: %u", (uint) part_id)); - if (MY_BIT_NONE == part_id) + if (part_id == MY_BIT_NONE) { error= 0; goto err1; @@ -4803,7 +4894,7 @@ int ha_partition::rnd_init(bool scan) We have a partition and we are scanning with rnd_next so we bump our cache */ - DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); + DBUG_PRINT("info", ("rnd_init on partition: %u", (uint) part_id)); if (scan) { /* @@ -4812,26 +4903,29 @@ int ha_partition::rnd_init(bool scan) */ rnd_end(); late_extra_cache(part_id); - if ((error= m_file[part_id]->ha_rnd_init(scan))) - goto err; + + m_index_scan_type= partition_no_index_scan; } - else + + for (i= part_id; + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - for (i= part_id; - i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) - { - if ((error= m_file[i]->ha_rnd_init(scan))) - goto err; - } + if ((error= m_file[i]->ha_rnd_init(scan))) + goto err; } + m_scan_value= scan; m_part_spec.start_part= part_id; m_part_spec.end_part= m_tot_parts - 1; - DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value)); + m_rnd_init_and_first= TRUE; + DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value)); DBUG_RETURN(0); err: + if (scan) + late_extra_no_cache(part_id); + /* Call rnd_end for all previously inited partitions. */ for (; part_id < i; @@ -4863,13 +4957,10 @@ int ha_partition::rnd_end() switch (m_scan_value) { case 2: // Error break; - case 1: - if (NO_CURRENT_PART_ID != m_part_spec.start_part) // Table scan - { + case 1: // Table scan + if (m_part_spec.start_part != NO_CURRENT_PART_ID) late_extra_no_cache(m_part_spec.start_part); - m_file[m_part_spec.start_part]->ha_rnd_end(); - } - break; + /* fall through */ case 0: uint i; for (i= bitmap_get_first_set(&m_part_info->read_partitions); @@ -4885,6 +4976,7 @@ int ha_partition::rnd_end() DBUG_RETURN(0); } + /* read next row during full table scan (scan in random row order) @@ -4909,14 +5001,15 @@ int ha_partition::rnd_end() int ha_partition::rnd_next(uchar *buf) { handler *file; - int result= HA_ERR_END_OF_FILE; + int result= HA_ERR_END_OF_FILE, error; uint part_id= m_part_spec.start_part; DBUG_ENTER("ha_partition::rnd_next"); + DBUG_PRINT("enter", ("partition this: %p", this)); /* upper level will increment this once again at end of call */ decrement_statistics(&SSV::ha_read_rnd_next_count); - if (NO_CURRENT_PART_ID == part_id) + if (part_id == NO_CURRENT_PART_ID) { /* The original set of partitions to scan was empty and thus we report @@ -4924,16 +5017,26 @@ int ha_partition::rnd_next(uchar *buf) */ goto end; } - + DBUG_ASSERT(m_scan_value == 1); + + if (m_rnd_init_and_first) + { + m_rnd_init_and_first= FALSE; + error= handle_pre_scan(FALSE, check_parallel_search()); + if (m_pre_calling || error) + DBUG_RETURN(error); + } + file= m_file[part_id]; - + while (TRUE) { result= file->ha_rnd_next(buf); if (!result) { m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part)); m_part_spec.start_part= part_id; table->status= 0; DBUG_RETURN(0); @@ -4950,10 +5053,6 @@ int ha_partition::rnd_next(uchar *buf) /* End current partition */ late_extra_no_cache(part_id); - DBUG_PRINT("info", ("rnd_end on partition %d", part_id)); - if ((result= file->ha_rnd_end())) - break; - /* Shift to next partition */ part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id); if (part_id >= m_tot_parts) @@ -4962,15 +5061,14 @@ int ha_partition::rnd_next(uchar *buf) break; } m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", (uint) m_last_part)); m_part_spec.start_part= part_id; file= m_file[part_id]; - DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); - if ((result= file->ha_rnd_init(1))) - break; late_extra_cache(part_id); } end: + DBUG_PRINT("exit", ("reset start_part")); m_part_spec.start_part= NO_CURRENT_PART_ID; end_dont_reset_start_part: DBUG_RETURN(result); @@ -5005,7 +5103,7 @@ end_dont_reset_start_part: void ha_partition::position(const uchar *record) { handler *file= m_file[m_last_part]; - uint pad_length; + size_t pad_length; DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part)); DBUG_ENTER("ha_partition::position"); @@ -5120,12 +5218,13 @@ bool ha_partition::init_record_priority_queue() */ if (!m_ordered_rec_buffer) { - uint alloc_len; + size_t alloc_len; uint used_parts= bitmap_bits_set(&m_part_info->read_partitions); + DBUG_ASSERT(used_parts > 0); /* Allocate record buffer for each used partition. */ m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS; if (!m_using_extended_keys) - m_priority_queue_rec_len += m_file[0]->ref_length; + m_priority_queue_rec_len += get_open_file_sample()->ref_length; alloc_len= used_parts * m_priority_queue_rec_len; /* Allocate a key for temporary use when setting up the scan. */ alloc_len+= table_share->max_key_length; @@ -5151,20 +5250,15 @@ bool ha_partition::init_record_priority_queue() ptr+= m_priority_queue_rec_len; } m_start_key.key= (const uchar*)ptr; - + /* Initialize priority queue, initialized to reading forward. */ int (*cmp_func)(void *, uchar *, uchar *); - void *cmp_arg; - if (!m_using_extended_keys) - { + void *cmp_arg= (void*) this; + if (!m_using_extended_keys && !(table_flags() & HA_CMP_REF_IS_EXPENSIVE)) cmp_func= cmp_key_rowid_part_id; - cmp_arg= (void*)this; - } else - { cmp_func= cmp_key_part_id; - cmp_arg= (void*)m_curr_key_info; - } + DBUG_PRINT("info", ("partition queue_init(1) used_parts: %u", used_parts)); if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0)) { my_free(m_ordered_rec_buffer); @@ -5215,8 +5309,8 @@ int ha_partition::index_init(uint inx, bool sorted) int error= 0; uint i; DBUG_ENTER("ha_partition::index_init"); + DBUG_PRINT("enter", ("partition this: %p inx: %u sorted: %u", this, inx, sorted)); - DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted)); active_index= inx; m_part_spec.start_part= NO_CURRENT_PART_ID; m_start_key.length= 0; @@ -5251,11 +5345,14 @@ int ha_partition::index_init(uint inx, bool sorted) But this is required for operations that may need to change data only. */ if (get_lock_type() == F_WRLCK) + { + DBUG_PRINT("info", ("partition set part_field bitmap")); bitmap_union(table->read_set, &m_part_info->full_part_field_set); + } if (sorted) { /* - An ordered scan is requested. We must make sure all fields of the + An ordered scan is requested. We must make sure all fields of the used index are in the read set, as partitioning requires them for sorting (see ha_partition::handle_ordered_index_scan). @@ -5322,19 +5419,21 @@ err: int ha_partition::index_end() { int error= 0; - uint i; + handler **file; DBUG_ENTER("ha_partition::index_end"); active_index= MAX_KEY; m_part_spec.start_part= NO_CURRENT_PART_ID; - for (i= bitmap_get_first_set(&m_part_info->read_partitions); - i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + file= m_file; + do { - int tmp; - if ((tmp= m_file[i]->ha_index_end())) - error= tmp; - } + if ((*file)->inited == INDEX) + { + int tmp; + if ((tmp= (*file)->ha_index_end())) + error= tmp; + } + } while (*(++file)); destroy_record_priority_queue(); DBUG_RETURN(error); } @@ -5383,34 +5482,26 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key, /* Compare two part_no partition numbers */ static int cmp_part_ids(uchar *ref1, uchar *ref2) { - /* The following was taken from ha_partition::cmp_ref */ - my_ptrdiff_t diff1= ref2[1] - ref1[1]; - my_ptrdiff_t diff2= ref2[0] - ref1[0]; - if (!diff1 && !diff2) - return 0; - - if (diff1 > 0) - return(-1); - - if (diff1 < 0) - return(+1); - - if (diff2 > 0) - return(-1); - - return(+1); + uint32 diff2= uint2korr(ref2); + uint32 diff1= uint2korr(ref1); + if (diff2 > diff1) + return -1; + if (diff2 < diff1) + return 1; + return 0; } /* @brief - Provide ordering by (key_value, part_no). + Provide ordering by (key_value, part_no). */ -extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2) +extern "C" int cmp_key_part_id(void *ptr, uchar *ref1, uchar *ref2) { + ha_partition *file= (ha_partition*)ptr; int res; - if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS, + if ((res= key_rec_cmp(file->m_curr_key_info, ref1 + PARTITION_BYTES_IN_POS, ref2 + PARTITION_BYTES_IN_POS))) { return res; @@ -5420,7 +5511,7 @@ extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2) /* @brief - Provide ordering by (key_value, underying_table_rowid, part_no). + Provide ordering by (key_value, underying_table_rowid, part_no). */ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2) { @@ -5445,26 +5536,26 @@ extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2) Common routine for a number of index_read variants @param buf Buffer where the record should be returned. - @param have_start_key TRUE <=> the left endpoint is available, i.e. + @param have_start_key TRUE <=> the left endpoint is available, i.e. we're in index_read call or in read_range_first call and the range has left endpoint. FALSE <=> there is no left endpoint (we're in read_range_first() call and the range has no left endpoint). - + @return Operation status - @retval 0 OK + @retval 0 OK @retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record. @retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned. @retval other error code. @details - Start scanning the range (when invoked from read_range_first()) or doing + Start scanning the range (when invoked from read_range_first()) or doing an index lookup (when invoked from index_read_XXX): - If possible, perform partition selection - Find the set of partitions we're going to use - Depending on whether we need ordering: - NO: Get the first record from first used partition (see + NO: Get the first record from first used partition (see handle_unordered_scan_next_partition) YES: Fill the priority queue and get the record that is the first in the ordering @@ -5482,7 +5573,7 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) if (have_start_key) { - m_start_key.length= key_len= calculate_key_len(table, active_index, + m_start_key.length= key_len= calculate_key_len(table, active_index, m_start_key.key, m_start_key.keypart_map); DBUG_PRINT("info", ("have_start_key map %lu find_flag %u len %u", @@ -5494,7 +5585,7 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) DBUG_RETURN(error); } - if (have_start_key && + if (have_start_key && (m_start_key.flag == HA_READ_PREFIX_LAST || m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV || m_start_key.flag == HA_READ_BEFORE_KEY)) @@ -5514,7 +5605,9 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key) The unordered index scan will use the partition set created. */ DBUG_PRINT("info", ("doing unordered scan")); - error= handle_unordered_scan_next_partition(buf); + error= handle_pre_scan(FALSE, FALSE); + if (!error) + error= handle_unordered_scan_next_partition(buf); } else { @@ -5562,7 +5655,7 @@ int ha_partition::index_first(uchar * buf) /* Start an index scan from rightmost record and return first record - + SYNOPSIS index_last() buf Read row in MySQL Row Format @@ -5595,7 +5688,7 @@ int ha_partition::index_last(uchar * buf) SYNOPSIS ha_partition::common_first_last() - + see index_first for rest */ @@ -5607,7 +5700,11 @@ int ha_partition::common_first_last(uchar *buf) return error; if (!m_ordered_scan_ongoing && m_index_scan_type != partition_index_last) - return handle_unordered_scan_next_partition(buf); + { + if ((error= handle_pre_scan(FALSE, check_parallel_search()))) + return error; + return handle_unordered_scan_next_partition(buf); + } return handle_ordered_index_scan(buf, FALSE); } @@ -5635,7 +5732,7 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index, get_partition_set(table, buf, index, &m_start_key, &m_part_spec); - /* + /* We have either found exactly 1 partition (in which case start_part == end_part) or no matching partitions (start_part > end_part) @@ -5700,7 +5797,8 @@ int ha_partition::index_next(uchar * buf) and if direction changes, we must step back those partitions in the record queue so we don't return a value from the wrong direction. */ - DBUG_ASSERT(m_index_scan_type != partition_index_last); + if (m_index_scan_type == partition_index_last) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); if (!m_ordered_scan_ongoing) { DBUG_RETURN(handle_unordered_next(buf, FALSE)); @@ -5733,13 +5831,30 @@ int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen) decrement_statistics(&SSV::ha_read_next_count); DBUG_ASSERT(keylen == m_start_key.length); - DBUG_ASSERT(m_index_scan_type != partition_index_last); + if (m_index_scan_type == partition_index_last) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); if (!m_ordered_scan_ongoing) DBUG_RETURN(handle_unordered_next(buf, TRUE)); DBUG_RETURN(handle_ordered_next(buf, TRUE)); } +int ha_partition::index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map) +{ + DBUG_ENTER("ha_partition::index_read_last_map"); + + m_ordered= true; // Safety measure + end_range= NULL; + m_index_scan_type= partition_index_read_last; + m_start_key.key= key; + m_start_key.keypart_map= keypart_map; + m_start_key.flag= HA_READ_PREFIX_LAST; + DBUG_RETURN(common_index_read(buf, true)); +} + + /* Read next record when performing index scan backwards @@ -5761,7 +5876,8 @@ int ha_partition::index_prev(uchar * buf) decrement_statistics(&SSV::ha_read_prev_count); /* TODO: read comment in index_next */ - DBUG_ASSERT(m_index_scan_type != partition_index_first); + if (m_index_scan_type == partition_index_first) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); DBUG_RETURN(handle_ordered_prev(buf)); } @@ -5832,6 +5948,989 @@ int ha_partition::read_range_next() DBUG_RETURN(handle_unordered_next(table->record[0], eq_range)); } +/** + Create a copy of all keys used by multi_range_read() + + @retval 0 ok + @retval HA_ERR_END_OF_FILE no keys in range + @retval other value: error + + TODO to save memory: + - If (mrr_mode & HA_MRR_MATERIALIZED_KEYS) is set then the keys data is + stable and we don't have to copy the keys, only store a pointer to the + key. + - When allocating key data, store things in a MEM_ROOT buffer instead of + a malloc() per key. This will simplify and speed up the current code + and use less memory. +*/ + +int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq, + range_seq_t seq_it) +{ + uint i, length; + key_range *start_key, *end_key; + KEY_MULTI_RANGE *range; + DBUG_ENTER("ha_partition::multi_range_key_create_key"); + + bitmap_clear_all(&m_mrr_used_partitions); + m_mrr_range_length= 0; + bzero(m_part_mrr_range_length, + sizeof(*m_part_mrr_range_length) * m_tot_parts); + if (!m_mrr_range_first) + { + if (!(m_mrr_range_first= (PARTITION_KEY_MULTI_RANGE *) + my_multi_malloc(MYF(MY_WME), + &m_mrr_range_current, + sizeof(PARTITION_KEY_MULTI_RANGE), + NullS))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + m_mrr_range_first->id= 1; + m_mrr_range_first->key[0]= NULL; + m_mrr_range_first->key[1]= NULL; + m_mrr_range_first->next= NULL; + } + else + m_mrr_range_current= m_mrr_range_first; + + for (i= 0; i < m_tot_parts; i++) + { + if (!m_part_mrr_range_first[i]) + { + if (!(m_part_mrr_range_first[i]= (PARTITION_PART_KEY_MULTI_RANGE *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &m_part_mrr_range_current[i], + sizeof(PARTITION_PART_KEY_MULTI_RANGE), + NullS))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + else + { + m_part_mrr_range_current[i]= m_part_mrr_range_first[i]; + m_part_mrr_range_current[i]->partition_key_multi_range= NULL; + } + } + m_mrr_range_current->key_multi_range.start_key.key= NULL; + m_mrr_range_current->key_multi_range.end_key.key= NULL; + + while (!seq->next(seq_it, &m_mrr_range_current->key_multi_range)) + { + m_mrr_range_length++; + range= &m_mrr_range_current->key_multi_range; + + /* Copy start key */ + start_key= &range->start_key; + DBUG_PRINT("info",("partition range->range_flag: %u", range->range_flag)); + DBUG_PRINT("info",("partition start_key->key: %p", start_key->key)); + DBUG_PRINT("info",("partition start_key->length: %u", start_key->length)); + DBUG_PRINT("info",("partition start_key->keypart_map: %lu", + start_key->keypart_map)); + DBUG_PRINT("info",("partition start_key->flag: %u", start_key->flag)); + + if (start_key->key) + { + length= start_key->length; + if (!m_mrr_range_current->key[0] || + m_mrr_range_current->length[0] < length) + { + if (m_mrr_range_current->key[0]) + my_free(m_mrr_range_current->key[0]); + if (!(m_mrr_range_current->key[0]= + (uchar *) my_malloc(length, MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + m_mrr_range_current->length[0]= length; + } + memcpy(m_mrr_range_current->key[0], start_key->key, length); + start_key->key= m_mrr_range_current->key[0]; + } + + /* Copy end key */ + end_key= &range->end_key; + DBUG_PRINT("info",("partition end_key->key: %p", end_key->key)); + DBUG_PRINT("info",("partition end_key->length: %u", end_key->length)); + DBUG_PRINT("info",("partition end_key->keypart_map: %lu", + end_key->keypart_map)); + DBUG_PRINT("info",("partition end_key->flag: %u", end_key->flag)); + if (end_key->key) + { + length= end_key->length; + if (!m_mrr_range_current->key[1] || + m_mrr_range_current->length[1] < length) + { + if (m_mrr_range_current->key[1]) + my_free(m_mrr_range_current->key[1]); + if (!(m_mrr_range_current->key[1]= + (uchar *) my_malloc(length, MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + m_mrr_range_current->length[1]= length; + } + memcpy(m_mrr_range_current->key[1], end_key->key, length); + end_key->key= m_mrr_range_current->key[1]; + } + + m_mrr_range_current->ptr= m_mrr_range_current->key_multi_range.ptr; + m_mrr_range_current->key_multi_range.ptr= m_mrr_range_current; + + if (start_key->key && (start_key->flag & HA_READ_KEY_EXACT)) + get_partition_set(table, table->record[0], active_index, + start_key, &m_part_spec); + else + { + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + } + + /* Copy key to those partitions that needs it */ + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + bitmap_set_bit(&m_mrr_used_partitions, i); + m_part_mrr_range_length[i]++; + m_part_mrr_range_current[i]->partition_key_multi_range= + m_mrr_range_current; + + if (!m_part_mrr_range_current[i]->next) + { + PARTITION_PART_KEY_MULTI_RANGE *tmp_part_mrr_range; + if (!(tmp_part_mrr_range= (PARTITION_PART_KEY_MULTI_RANGE *) + my_malloc(sizeof(PARTITION_PART_KEY_MULTI_RANGE), + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + m_part_mrr_range_current[i]->next= tmp_part_mrr_range; + m_part_mrr_range_current[i]= tmp_part_mrr_range; + } + else + { + m_part_mrr_range_current[i]= m_part_mrr_range_current[i]->next; + m_part_mrr_range_current[i]->partition_key_multi_range= NULL; + } + } + } + + if (!m_mrr_range_current->next) + { + /* Add end of range sentinel */ + PARTITION_KEY_MULTI_RANGE *tmp_mrr_range; + if (!(tmp_mrr_range= (PARTITION_KEY_MULTI_RANGE *) + my_malloc(sizeof(PARTITION_KEY_MULTI_RANGE), MYF(MY_WME)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + tmp_mrr_range->id= m_mrr_range_current->id + 1; + tmp_mrr_range->key[0]= NULL; + tmp_mrr_range->key[1]= NULL; + tmp_mrr_range->next= NULL; + m_mrr_range_current->next= tmp_mrr_range; + } + m_mrr_range_current= m_mrr_range_current->next; + } + + if (!m_mrr_range_length) + { + DBUG_PRINT("Warning",("No keys to use for mrr")); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + /* set start and end part */ + m_part_spec.start_part= bitmap_get_first_set(&m_mrr_used_partitions); + + for (i= m_tot_parts; i-- > 0;) + { + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + m_part_spec.end_part= i; + break; + } + } + for (i= 0; i < m_tot_parts; i++) + { + m_partition_part_key_multi_range_hld[i].partition= this; + m_partition_part_key_multi_range_hld[i].part_id= i; + m_partition_part_key_multi_range_hld[i].partition_part_key_multi_range= + m_part_mrr_range_first[i]; + } + DBUG_PRINT("return",("OK")); + DBUG_RETURN(0); +} + + +static void partition_multi_range_key_get_key_info(void *init_params, + uint *length, + key_part_map *map) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params; + ha_partition *partition= hld->partition; + key_range *start_key= (&partition->m_mrr_range_first-> + key_multi_range.start_key); + DBUG_ENTER("partition_multi_range_key_get_key_info"); + *length= start_key->length; + *map= start_key->keypart_map; + DBUG_VOID_RETURN; +} + + +static range_seq_t partition_multi_range_key_init(void *init_params, + uint n_ranges, + uint flags) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)init_params; + ha_partition *partition= hld->partition; + uint i= hld->part_id; + DBUG_ENTER("partition_multi_range_key_init"); + partition->m_mrr_range_init_flags= flags; + hld->partition_part_key_multi_range= partition->m_part_mrr_range_first[i]; + DBUG_RETURN(init_params); +} + + +static bool partition_multi_range_key_next(range_seq_t seq, + KEY_MULTI_RANGE *range) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + PARTITION_KEY_MULTI_RANGE *partition_key_multi_range= + hld->partition_part_key_multi_range->partition_key_multi_range; + DBUG_ENTER("partition_multi_range_key_next"); + if (!partition_key_multi_range) + DBUG_RETURN(TRUE); + *range= partition_key_multi_range->key_multi_range; + hld->partition_part_key_multi_range= + hld->partition_part_key_multi_range->next; + DBUG_RETURN(FALSE); +} + + +static bool partition_multi_range_key_skip_record(range_seq_t seq, + range_id_t range_info, + uchar *rowid) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + DBUG_ENTER("partition_multi_range_key_skip_record"); + DBUG_RETURN(hld->partition->m_seq_if->skip_record(hld->partition->m_seq, + range_info, rowid)); +} + + +static bool partition_multi_range_key_skip_index_tuple(range_seq_t seq, + range_id_t range_info) +{ + PARTITION_PART_KEY_MULTI_RANGE_HLD *hld= + (PARTITION_PART_KEY_MULTI_RANGE_HLD *)seq; + DBUG_ENTER("partition_multi_range_key_skip_index_tuple"); + DBUG_RETURN(hld->partition->m_seq_if->skip_index_tuple(hld->partition->m_seq, + range_info)); +} + +ha_rows ha_partition::multi_range_read_info_const(uint keyno, + RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost) +{ + int error; + uint i; + handler **file; + ha_rows rows= 0; + uint ret_mrr_mode= 0; + range_seq_t seq_it; + part_id_range save_part_spec; + DBUG_ENTER("ha_partition::multi_range_read_info_const"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_mrr_new_full_buffer_size= 0; + save_part_spec= m_part_spec; + + seq_it= seq->init(seq_init_param, n_ranges, *mrr_mode); + if ((error= multi_range_key_create_key(seq, seq_it))) + { + if (error == HA_ERR_END_OF_FILE) // No keys in range + { + rows= 0; + goto calc_cost; + } + /* + This error means that we can't do multi_range_read for the moment + (probably running out of memory) and we need to fallback to + normal reads + */ + m_part_spec= save_part_spec; + DBUG_RETURN(HA_POS_ERROR); + } + m_part_seq_if.get_key_info= + seq->get_key_info ? partition_multi_range_key_get_key_info : NULL; + m_part_seq_if.init= partition_multi_range_key_init; + m_part_seq_if.next= partition_multi_range_key_next; + m_part_seq_if.skip_record= (seq->skip_record ? + partition_multi_range_key_skip_record : NULL); + m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ? + partition_multi_range_key_skip_index_tuple : + NULL); + file= m_file; + do + { + i= (uint)(file - m_file); + DBUG_PRINT("info",("partition part_id: %u", i)); + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + ha_rows tmp_rows; + uint tmp_mrr_mode; + m_mrr_buffer_size[i]= 0; + tmp_mrr_mode= *mrr_mode; + tmp_rows= (*file)-> + multi_range_read_info_const(keyno, &m_part_seq_if, + &m_partition_part_key_multi_range_hld[i], + m_part_mrr_range_length[i], + &m_mrr_buffer_size[i], + &tmp_mrr_mode, cost); + if (tmp_rows == HA_POS_ERROR) + { + m_part_spec= save_part_spec; + DBUG_RETURN(HA_POS_ERROR); + } + rows+= tmp_rows; + ret_mrr_mode|= tmp_mrr_mode; + m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i]; + } + } while (*(++file)); + *mrr_mode= ret_mrr_mode; + +calc_cost: + m_part_spec= save_part_spec; + cost->reset(); + cost->avg_io_cost= 1; + if ((*mrr_mode & HA_MRR_INDEX_ONLY) && rows > 2) + cost->io_count= keyread_time(keyno, n_ranges, (uint) rows); + else + cost->io_count= read_time(keyno, n_ranges, rows); + cost->cpu_cost= (double) rows / TIME_FOR_COMPARE + 0.01; + DBUG_RETURN(rows); +} + + +ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges, + uint keys, + uint key_parts, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost) +{ + uint i; + handler **file; + ha_rows rows; + DBUG_ENTER("ha_partition::multi_range_read_info"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_mrr_new_full_buffer_size= 0; + file= m_file; + do + { + i= (uint)(file - m_file); + if (bitmap_is_set(&(m_part_info->read_partitions), (i))) + { + m_mrr_buffer_size[i]= 0; + if ((rows= (*file)->multi_range_read_info(keyno, n_ranges, keys, + key_parts, + &m_mrr_buffer_size[i], + mrr_mode, cost))) + DBUG_RETURN(rows); + m_mrr_new_full_buffer_size+= m_mrr_buffer_size[i]; + } + } while (*(++file)); + + cost->reset(); + cost->avg_io_cost= 1; + if (*mrr_mode & HA_MRR_INDEX_ONLY) + cost->io_count= keyread_time(keyno, n_ranges, (uint) rows); + else + cost->io_count= read_time(keyno, n_ranges, rows); + DBUG_RETURN(0); +} + + +int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint mrr_mode, + HANDLER_BUFFER *buf) +{ + int error; + uint i; + handler **file; + uchar *tmp_buffer; + DBUG_ENTER("ha_partition::multi_range_read_init"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + m_seq_if= seq; + m_seq= seq->init(seq_init_param, n_ranges, mrr_mode); + if ((error= multi_range_key_create_key(seq, m_seq))) + DBUG_RETURN(0); + + m_part_seq_if.get_key_info= (seq->get_key_info ? + partition_multi_range_key_get_key_info : + NULL); + m_part_seq_if.init= partition_multi_range_key_init; + m_part_seq_if.next= partition_multi_range_key_next; + m_part_seq_if.skip_record= (seq->skip_record ? + partition_multi_range_key_skip_record : + NULL); + m_part_seq_if.skip_index_tuple= (seq->skip_index_tuple ? + partition_multi_range_key_skip_index_tuple : + NULL); + + /* m_mrr_new_full_buffer_size was calculated in multi_range_read_info */ + if (m_mrr_full_buffer_size < m_mrr_new_full_buffer_size) + { + if (m_mrr_full_buffer) + my_free(m_mrr_full_buffer); + if (!(m_mrr_full_buffer= + (uchar *) my_malloc(m_mrr_new_full_buffer_size, MYF(MY_WME)))) + { + m_mrr_full_buffer_size= 0; + error= HA_ERR_OUT_OF_MEM; + goto error; + } + m_mrr_full_buffer_size= m_mrr_new_full_buffer_size; + } + + tmp_buffer= m_mrr_full_buffer; + file= m_file; + do + { + i= (uint)(file - m_file); + DBUG_PRINT("info",("partition part_id: %u", i)); + if (bitmap_is_set(&m_mrr_used_partitions, i)) + { + if (m_mrr_new_full_buffer_size) + { + if (m_mrr_buffer_size[i]) + { + m_mrr_buffer[i].buffer= tmp_buffer; + m_mrr_buffer[i].end_of_used_area= tmp_buffer; + tmp_buffer+= m_mrr_buffer_size[i]; + m_mrr_buffer[i].buffer_end= tmp_buffer; + } + } + else + m_mrr_buffer[i]= *buf; + + if ((error= (*file)-> + multi_range_read_init(&m_part_seq_if, + &m_partition_part_key_multi_range_hld[i], + m_part_mrr_range_length[i], + mrr_mode, + &m_mrr_buffer[i]))) + goto error; + m_stock_range_seq[i]= 0; + } + } while (*(++file)); + + m_multi_range_read_first= TRUE; + m_mrr_range_current= m_mrr_range_first; + m_index_scan_type= partition_read_multi_range; + m_mrr_mode= mrr_mode; + m_mrr_n_ranges= n_ranges; + DBUG_RETURN(0); + +error: + DBUG_RETURN(error); +} + + +int ha_partition::multi_range_read_next(range_id_t *range_info) +{ + int error; + DBUG_ENTER("ha_partition::multi_range_read_next"); + DBUG_PRINT("enter", ("partition this: %p partition m_mrr_mode: %u", + this, m_mrr_mode)); + + if ((m_mrr_mode & HA_MRR_SORTED)) + { + if (m_multi_range_read_first) + { + if ((error= handle_ordered_index_scan(table->record[0], FALSE))) + DBUG_RETURN(error); + if (!m_pre_calling) + m_multi_range_read_first= FALSE; + } + else if ((error= handle_ordered_next(table->record[0], eq_range))) + DBUG_RETURN(error); + *range_info= m_mrr_range_current->ptr; + } + else + { + if (m_multi_range_read_first) + { + if ((error= handle_unordered_scan_next_partition(table->record[0]))) + DBUG_RETURN(error); + if (!m_pre_calling) + m_multi_range_read_first= FALSE; + } + else if ((error= handle_unordered_next(table->record[0], FALSE))) + DBUG_RETURN(error); + + *range_info= + ((PARTITION_KEY_MULTI_RANGE *) m_range_info[m_last_part])->ptr; + } + DBUG_RETURN(0); +} + + +int ha_partition::multi_range_read_explain_info(uint mrr_mode, char *str, + size_t size) +{ + DBUG_ENTER("ha_partition::multi_range_read_explain_info"); + DBUG_RETURN(get_open_file_sample()-> + multi_range_read_explain_info(mrr_mode, str, size)); +} + + +/** + Find and retrieve the Full Text Search relevance ranking for a search string + in a full text index. + + @param handler Full Text Search handler + @param record Search string + @param length Length of the search string + + @retval Relevance value +*/ + +float partition_ft_find_relevance(FT_INFO *handler, + uchar *record, uint length) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + uint m_last_part= ((ha_partition*) info->file)->last_part(); + FT_INFO *m_handler= info->part_ft_info[m_last_part]; + DBUG_ENTER("partition_ft_find_relevance"); + if (!m_handler) + DBUG_RETURN((float)-1.0); + DBUG_RETURN(m_handler->please->find_relevance(m_handler, record, length)); +} + + +/** + Retrieve the Full Text Search relevance ranking for the current + full text search. + + @param handler Full Text Search handler + + @retval Relevance value +*/ + +float partition_ft_get_relevance(FT_INFO *handler) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + uint m_last_part= ((ha_partition*) info->file)->last_part(); + FT_INFO *m_handler= info->part_ft_info[m_last_part]; + DBUG_ENTER("partition_ft_get_relevance"); + if (!m_handler) + DBUG_RETURN((float)-1.0); + DBUG_RETURN(m_handler->please->get_relevance(m_handler)); +} + + +/** + Free the memory for a full text search handler. + + @param handler Full Text Search handler +*/ + +void partition_ft_close_search(FT_INFO *handler) +{ + st_partition_ft_info *info= (st_partition_ft_info *)handler; + info->file->ft_close_search(handler); +} + + +/** + Free the memory for a full text search handler. + + @param handler Full Text Search handler +*/ + +void ha_partition::ft_close_search(FT_INFO *handler) +{ + uint i; + st_partition_ft_info *info= (st_partition_ft_info *)handler; + DBUG_ENTER("ha_partition::ft_close_search"); + + for (i= 0; i < m_tot_parts; i++) + { + FT_INFO *m_handler= info->part_ft_info[i]; + DBUG_ASSERT(!m_handler || + (m_handler->please && m_handler->please->close_search)); + if (m_handler && + m_handler->please && + m_handler->please->close_search) + m_handler->please->close_search(m_handler); + } + DBUG_VOID_RETURN; +} + + +/* Partition Full Text search function table */ +_ft_vft partition_ft_vft = +{ + NULL, // partition_ft_read_next + partition_ft_find_relevance, + partition_ft_close_search, + partition_ft_get_relevance, + NULL // partition_ft_reinit_search +}; + + +/** + Initialize a full text search. +*/ + +int ha_partition::ft_init() +{ + int error; + uint i= 0; + uint32 part_id; + DBUG_ENTER("ha_partition::ft_init"); + DBUG_PRINT("info", ("partition this: %p", this)); + + /* + For operations that may need to change data, we may need to extend + read_set. + */ + if (get_lock_type() == F_WRLCK) + { + /* + If write_set contains any of the fields used in partition and + subpartition expression, we need to set all bits in read_set because + the row may need to be inserted in a different [sub]partition. In + other words update_row() can be converted into write_row(), which + requires a complete record. + */ + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + bitmap_set_all(table->read_set); + else + { + /* + Some handlers only read fields as specified by the bitmap for the + read set. For partitioned handlers we always require that the + fields of the partition functions are read such that we can + calculate the partition id to place updated and deleted records. + */ + bitmap_union(table->read_set, &m_part_info->full_part_field_set); + } + } + + /* Now we see what the index of our first important partition is */ + DBUG_PRINT("info", ("m_part_info->read_partitions: %p", + (void *) m_part_info->read_partitions.bitmap)); + part_id= bitmap_get_first_set(&(m_part_info->read_partitions)); + DBUG_PRINT("info", ("m_part_spec.start_part %u", (uint) part_id)); + + if (part_id == MY_BIT_NONE) + { + error= 0; + goto err1; + } + + DBUG_PRINT("info", ("ft_init on partition %u", (uint) part_id)); + /* + ft_end() is needed for partitioning to reset internal data if scan + is already in use + */ + if (m_pre_calling) + { + if ((error= pre_ft_end())) + goto err1; + } + else + ft_end(); + m_index_scan_type= partition_ft_read; + for (i= part_id; i < m_tot_parts; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + error= m_pre_calling ? m_file[i]->pre_ft_init() : m_file[i]->ft_init(); + if (error) + goto err2; + } + } + m_scan_value= 1; + m_part_spec.start_part= part_id; + m_part_spec.end_part= m_tot_parts - 1; + m_ft_init_and_first= TRUE; + DBUG_PRINT("info", ("m_scan_value: %u", m_scan_value)); + DBUG_RETURN(0); + +err2: + late_extra_no_cache(part_id); + while ((int)--i >= (int)part_id) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i)) + { + if (m_pre_calling) + m_file[i]->pre_ft_end(); + else + m_file[i]->ft_end(); + } + } +err1: + m_scan_value= 2; + m_part_spec.start_part= NO_CURRENT_PART_ID; + DBUG_RETURN(error); +} + + +/** + Initialize a full text search during a bulk access request. +*/ + +int ha_partition::pre_ft_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_ft_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= ft_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Terminate a full text search. +*/ + +void ha_partition::ft_end() +{ + handler **file; + DBUG_ENTER("ha_partition::ft_end"); + DBUG_PRINT("info", ("partition this: %p", this)); + + switch (m_scan_value) { + case 2: // Error + break; + case 1: // Table scan + if (NO_CURRENT_PART_ID != m_part_spec.start_part) + late_extra_no_cache(m_part_spec.start_part); + file= m_file; + do + { + if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file))) + { + if (m_pre_calling) + (*file)->pre_ft_end(); + else + (*file)->ft_end(); + } + } while (*(++file)); + break; + } + m_scan_value= 2; + m_part_spec.start_part= NO_CURRENT_PART_ID; + ft_current= 0; + DBUG_VOID_RETURN; +} + + +/** + Terminate a full text search during a bulk access request. +*/ + +int ha_partition::pre_ft_end() +{ + bool save_m_pre_calling; + DBUG_ENTER("ha_partition::pre_ft_end"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + ft_end(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(0); +} + + +/** + Initialize a full text search using the extended API. + + @param flags Search flags + @param inx Key number + @param key Key value + + @return FT_INFO structure if successful + NULL otherwise +*/ + +FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key) +{ + FT_INFO *ft_handler; + handler **file; + st_partition_ft_info *ft_target, **parent; + DBUG_ENTER("ha_partition::ft_init_ext"); + + if (ft_current) + parent= &ft_current->next; + else + parent= &ft_first; + + if (!(ft_target= *parent)) + { + FT_INFO **tmp_ft_info; + if (!(ft_target= (st_partition_ft_info *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &ft_target, + sizeof(st_partition_ft_info), + &tmp_ft_info, + sizeof(FT_INFO *) * m_tot_parts, + NullS))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); + DBUG_RETURN(NULL); + } + ft_target->part_ft_info= tmp_ft_info; + (*parent)= ft_target; + } + + ft_current= ft_target; + file= m_file; + do + { + if (bitmap_is_set(&(m_part_info->read_partitions), (uint)(file - m_file))) + { + if ((ft_handler= (*file)->ft_init_ext(flags, inx, key))) + (*file)->ft_handler= ft_handler; + else + (*file)->ft_handler= NULL; + ft_target->part_ft_info[file - m_file]= ft_handler; + } + else + { + (*file)->ft_handler= NULL; + ft_target->part_ft_info[file - m_file]= NULL; + } + } while (*(++file)); + + ft_target->please= &partition_ft_vft; + ft_target->file= this; + DBUG_RETURN((FT_INFO*)ft_target); +} + + +/** + Return the next record from the FT result set during an ordered index + pre-scan + + @param use_parallel Is it a parallel search + + @return >0 Error code + 0 Success +*/ + +int ha_partition::pre_ft_read(bool use_parallel) +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_ft_read"); + DBUG_PRINT("info", ("partition this: %p", this)); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + m_pre_call_use_parallel= use_parallel; + error= ft_read(table->record[0]); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Return the first or next record in a full text search. + + @param buf Buffer where the record should be returned + + @return >0 Error code + 0 Success +*/ + +int ha_partition::ft_read(uchar *buf) +{ + handler *file; + int result= HA_ERR_END_OF_FILE, error; + uint part_id= m_part_spec.start_part; + DBUG_ENTER("ha_partition::ft_read"); + DBUG_PRINT("info", ("partition this: %p", this)); + DBUG_PRINT("info", ("part_id: %u", part_id)); + + if (part_id == NO_CURRENT_PART_ID) + { + /* + The original set of partitions to scan was empty and thus we report + the result here. + */ + DBUG_PRINT("info", ("NO_CURRENT_PART_ID")); + goto end; + } + + DBUG_ASSERT(m_scan_value == 1); + + if (m_ft_init_and_first) // First call to ft_read() + { + m_ft_init_and_first= FALSE; + if (!bulk_access_executing) + { + error= handle_pre_scan(FALSE, check_parallel_search()); + if (m_pre_calling || error) + DBUG_RETURN(error); + } + late_extra_cache(part_id); + } + + file= m_file[part_id]; + + while (TRUE) + { + if (!(result= file->ft_read(buf))) + { + /* Found row: remember position and return it. */ + m_part_spec.start_part= m_last_part= part_id; + table->status= 0; + DBUG_RETURN(0); + } + + /* + if we get here, then the current partition ft_next returned failure + */ + if (result == HA_ERR_RECORD_DELETED) + continue; // Probably MyISAM + + if (result != HA_ERR_END_OF_FILE) + goto end_dont_reset_start_part; // Return error + + /* End current partition */ + late_extra_no_cache(part_id); + DBUG_PRINT("info", ("stopping using partition %u", (uint) part_id)); + + /* Shift to next partition */ + while (++part_id < m_tot_parts && + !bitmap_is_set(&(m_part_info->read_partitions), part_id)) + ; + if (part_id >= m_tot_parts) + { + result= HA_ERR_END_OF_FILE; + break; + } + m_part_spec.start_part= m_last_part= part_id; + file= m_file[part_id]; + DBUG_PRINT("info", ("now using partition %u", (uint) part_id)); + late_extra_cache(part_id); + } + +end: + m_part_spec.start_part= NO_CURRENT_PART_ID; +end_dont_reset_start_part: + table->status= STATUS_NOT_FOUND; + DBUG_RETURN(result); +} + /* Common routine to set up index scans @@ -5842,7 +6941,7 @@ int ha_partition::read_range_next() needs it to calculcate partitioning function values) - idx_read_flag TRUE <=> m_start_key has range start endpoint which + idx_read_flag TRUE <=> m_start_key has range start endpoint which probably can be used to determine the set of partitions to scan. FALSE <=> there is no start endpoint. @@ -5864,7 +6963,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) DBUG_ENTER("ha_partition::partition_scan_set_up"); if (idx_read_flag) - get_partition_set(table,buf,active_index,&m_start_key,&m_part_spec); + get_partition_set(table, buf, active_index, &m_start_key, &m_part_spec); else { m_part_spec.start_part= 0; @@ -5885,8 +6984,8 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) We discovered a single partition to scan, this never needs to be performed using the ordered index scan. */ - DBUG_PRINT("info", ("index scan using the single partition %d", - m_part_spec.start_part)); + DBUG_PRINT("info", ("index scan using the single partition %u", + (uint) m_part_spec.start_part)); m_ordered_scan_ongoing= FALSE; } else @@ -5913,6 +7012,206 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) DBUG_RETURN(0); } +/** + Check if we can search partitions in parallel + + @retval TRUE yes + @retval FALSE no +*/ + +bool ha_partition::check_parallel_search() +{ + TABLE_LIST *table_list= table->pos_in_table_list; + st_select_lex *select_lex; + JOIN *join; + DBUG_ENTER("ha_partition::check_parallel_search"); + if (!table_list) + goto not_parallel; + + while (table_list->parent_l) + table_list= table_list->parent_l; + + select_lex= table_list->select_lex; + DBUG_PRINT("info",("partition select_lex: %p", select_lex)); + if (!select_lex) + goto not_parallel; + if (!select_lex->explicit_limit) + { + DBUG_PRINT("info",("partition not using explicit_limit")); + goto parallel; + } + + join= select_lex->join; + DBUG_PRINT("info",("partition join: %p", join)); + if (join && join->skip_sort_order) + { + DBUG_PRINT("info",("partition order_list.elements: %u", + select_lex->order_list.elements)); + if (select_lex->order_list.elements) + { + Item *item= *select_lex->order_list.first->item; + DBUG_PRINT("info",("partition item: %p", item)); + DBUG_PRINT("info",("partition item->type(): %u", item->type())); + DBUG_PRINT("info",("partition m_part_info->part_type: %u", + m_part_info->part_type)); + DBUG_PRINT("info",("partition m_is_sub_partitioned: %s", + m_is_sub_partitioned ? "TRUE" : "FALSE")); + DBUG_PRINT("info",("partition m_part_info->part_expr: %p", + m_part_info->part_expr)); + if (item->type() == Item::FIELD_ITEM && + m_part_info->part_type == RANGE_PARTITION && + !m_is_sub_partitioned && + (!m_part_info->part_expr || + m_part_info->part_expr->type() == Item::FIELD_ITEM)) + { + Field *order_field= ((Item_field *)item)->field; + DBUG_PRINT("info",("partition order_field: %p", order_field)); + if (order_field && order_field->table == table_list->table) + { + Field *part_field= m_part_info->full_part_field_array[0]; + if (set_top_table_fields) + order_field= top_table_field[order_field->field_index]; + DBUG_PRINT("info",("partition order_field: %p", order_field)); + DBUG_PRINT("info",("partition part_field: %p", part_field)); + if (part_field == order_field) + { + /* + We are using ORDER BY partition_field LIMIT # + In this case, let's not do things in parallel as it's + likely that the query can be satisfied from the first + partition + */ + DBUG_PRINT("info",("partition with ORDER on partition field")); + goto not_parallel; + } + } + } + DBUG_PRINT("info",("partition have order")); + goto parallel; + } + + DBUG_PRINT("info",("partition group_list.elements: %u", + select_lex->group_list.elements)); + if (select_lex->group_list.elements) + { + Item *item= *select_lex->group_list.first->item; + DBUG_PRINT("info",("partition item: %p", item)); + DBUG_PRINT("info",("partition item->type(): %u", item->type())); + DBUG_PRINT("info",("partition m_part_info->part_type: %u", + m_part_info->part_type)); + DBUG_PRINT("info",("partition m_is_sub_partitioned: %s", + m_is_sub_partitioned ? "TRUE" : "FALSE")); + DBUG_PRINT("info",("partition m_part_info->part_expr: %p", + m_part_info->part_expr)); + if (item->type() == Item::FIELD_ITEM && + m_part_info->part_type == RANGE_PARTITION && + !m_is_sub_partitioned && + (!m_part_info->part_expr || + m_part_info->part_expr->type() == Item::FIELD_ITEM)) + { + Field *group_field= ((Item_field *)item)->field; + DBUG_PRINT("info",("partition group_field: %p", group_field)); + if (group_field && group_field->table == table_list->table) + { + Field *part_field= m_part_info->full_part_field_array[0]; + if (set_top_table_fields) + group_field= top_table_field[group_field->field_index]; + DBUG_PRINT("info",("partition group_field: %p", group_field)); + DBUG_PRINT("info",("partition part_field: %p", part_field)); + if (part_field == group_field) + { + DBUG_PRINT("info",("partition with GROUP BY on partition field")); + goto not_parallel; + } + } + } + DBUG_PRINT("info",("partition with GROUP BY")); + goto parallel; + } + } + else if (select_lex->order_list.elements || + select_lex->group_list.elements) + { + DBUG_PRINT("info",("partition is not skip_order")); + DBUG_PRINT("info",("partition order_list.elements: %u", + select_lex->order_list.elements)); + DBUG_PRINT("info",("partition group_list.elements: %u", + select_lex->group_list.elements)); + goto parallel; + } + DBUG_PRINT("info",("partition is not skip_order")); + +not_parallel: + DBUG_PRINT("return",("partition FALSE")); + DBUG_RETURN(FALSE); + +parallel: + DBUG_PRINT("return",("partition TRUE")); + DBUG_RETURN(TRUE); +} + + +int ha_partition::handle_pre_scan(bool reverse_order, bool use_parallel) +{ + uint i; + DBUG_ENTER("ha_partition::handle_pre_scan"); + DBUG_PRINT("enter", + ("m_part_spec.start_part: %u m_part_spec.end_part: %u", + (uint) m_part_spec.start_part, (uint) m_part_spec.end_part)); + + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (!(bitmap_is_set(&(m_part_info->read_partitions), i))) + continue; + int error; + handler *file= m_file[i]; + + switch (m_index_scan_type) { + case partition_index_read: + error= file->pre_index_read_map(m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag, + use_parallel); + break; + case partition_index_first: + error= file->pre_index_first(use_parallel); + break; + case partition_index_last: + error= file->pre_index_last(use_parallel); + break; + case partition_index_read_last: + error= file->pre_index_read_last_map(m_start_key.key, + m_start_key.keypart_map, + use_parallel); + break; + case partition_read_range: + error= file->pre_read_range_first(m_start_key.key? &m_start_key: NULL, + end_range, eq_range, TRUE, use_parallel); + break; + case partition_read_multi_range: + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + error= file->pre_multi_range_read_next(use_parallel); + break; + case partition_ft_read: + error= file->pre_ft_read(use_parallel); + break; + case partition_no_index_scan: + error= file->pre_rnd_next(use_parallel); + break; + default: + DBUG_ASSERT(FALSE); + DBUG_RETURN(0); + } + if (error == HA_ERR_END_OF_FILE) + error= 0; + if (error) + DBUG_RETURN(error); + } + table->status= 0; + DBUG_RETURN(0); +} + /**************************************************************************** Unordered Index Scan Routines @@ -5959,7 +7258,16 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) partition_read_range is_next_same are always local constants */ - if (m_index_scan_type == partition_read_range) + if (m_index_scan_type == partition_read_multi_range) + { + if (!(error= file-> + multi_range_read_next(&m_range_info[m_part_spec.start_part]))) + { + m_last_part= m_part_spec.start_part; + DBUG_RETURN(0); + } + } + else if (m_index_scan_type == partition_read_range) { if (!(error= file->read_range_next())) { @@ -5976,7 +7284,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) DBUG_RETURN(0); } } - else + else { if (!(error= file->ha_index_next(buf))) { @@ -5999,7 +7307,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) SYNOPSIS handle_unordered_scan_next_partition() - buf Read row in MySQL Row Format + buf Read row in MariaDB Row Format RETURN VALUE HA_ERR_END_OF_FILE End of scan @@ -6017,6 +7325,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int saved_error= HA_ERR_END_OF_FILE; DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition"); + /* Read next partition that includes start_part */ if (i) i= bitmap_get_next_set(&m_part_info->read_partitions, i - 1); else @@ -6029,34 +7338,29 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int error; handler *file= m_file[i]; m_part_spec.start_part= i; + switch (m_index_scan_type) { + case partition_read_multi_range: + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + DBUG_PRINT("info", ("read_multi_range on partition %u", i)); + error= file->multi_range_read_next(&m_range_info[i]); + break; case partition_read_range: - DBUG_PRINT("info", ("read_range_first on partition %d", i)); + DBUG_PRINT("info", ("read_range_first on partition %u", i)); error= file->read_range_first(m_start_key.key? &m_start_key: NULL, end_range, eq_range, FALSE); break; case partition_index_read: - DBUG_PRINT("info", ("index_read on partition %d", i)); + DBUG_PRINT("info", ("index_read on partition %u", i)); error= file->ha_index_read_map(buf, m_start_key.key, m_start_key.keypart_map, m_start_key.flag); break; case partition_index_first: - DBUG_PRINT("info", ("index_first on partition %d", i)); + DBUG_PRINT("info", ("index_first on partition %u", i)); error= file->ha_index_first(buf); break; - case partition_index_first_unordered: - /* - We perform a scan without sorting and this means that we - should not use the index_first since not all handlers - support it and it is also unnecessary to restrict sort - order. - */ - DBUG_PRINT("info", ("read_range_first on partition %d", i)); - table->record[0]= buf; - error= file->read_range_first(0, end_range, eq_range, 0); - table->record[0]= m_rec0; - break; default: DBUG_ASSERT(FALSE); DBUG_RETURN(1); @@ -6070,12 +7374,12 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) DBUG_RETURN(error); /* - If HA_ERR_KEY_NOT_FOUND, we must return that error instead of + If HA_ERR_KEY_NOT_FOUND, we must return that error instead of HA_ERR_END_OF_FILE, to be able to continue search. */ if (saved_error != HA_ERR_KEY_NOT_FOUND) saved_error= error; - DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %d", i)); + DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %u", i)); } if (saved_error == HA_ERR_END_OF_FILE) m_part_spec.start_part= NO_CURRENT_PART_ID; @@ -6086,7 +7390,7 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) /** Common routine to start index scan with ordered results. - @param[out] buf Read row in MySQL Row Format + @param[out] buf Read row in MariaDB Row Format @return Operation status @retval HA_ERR_END_OF_FILE End of scan @@ -6112,19 +7416,31 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) { + int error; uint i; uint j= queue_first_element(&m_queue); + uint smallest_range_seq= 0; bool found= FALSE; uchar *part_rec_buf_ptr= m_ordered_rec_buffer; int saved_error= HA_ERR_END_OF_FILE; DBUG_ENTER("ha_partition::handle_ordered_index_scan"); + DBUG_PRINT("enter", ("partition this: %p", this)); + + if (m_pre_calling) + error= handle_pre_scan(reverse_order, m_pre_call_use_parallel); + else + error= handle_pre_scan(reverse_order, check_parallel_search()); + if (error) + DBUG_RETURN(error); if (m_key_not_found) { + /* m_key_not_found was set in the previous call to this function */ m_key_not_found= false; bitmap_clear_all(&m_key_not_found_partitions); } m_top_entry= NO_CURRENT_PART_ID; + DBUG_PRINT("info", ("partition queue_remove_all(1)")); queue_remove_all(&m_queue); DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions, m_part_spec.start_part)); @@ -6144,14 +7460,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u", m_part_spec.start_part, i)); for (/* continue from above */ ; - i <= m_part_spec.end_part; - i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + i <= m_part_spec.end_part ; + i= bitmap_get_next_set(&m_part_info->read_partitions, i), + part_rec_buf_ptr+= m_priority_queue_rec_len) { DBUG_PRINT("info", ("reading from part %u (scan_type: %u)", i, m_index_scan_type)); DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); uchar *rec_buf_ptr= part_rec_buf_ptr + PARTITION_BYTES_IN_POS; - int error; handler *file= m_file[i]; switch (m_index_scan_type) { @@ -6160,6 +7476,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) m_start_key.key, m_start_key.keypart_map, m_start_key.flag); + /* Caller has specified reverse_order */ break; case partition_index_first: error= file->ha_index_first(rec_buf_ptr); @@ -6171,16 +7488,49 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) break; case partition_read_range: { - /* + /* This can only read record to table->record[0], as it was set when the table was being opened. We have to memcpy data ourselves. */ error= file->read_range_first(m_start_key.key? &m_start_key: NULL, end_range, eq_range, TRUE); - memcpy(rec_buf_ptr, table->record[0], m_rec_length); + if (!error) + memcpy(rec_buf_ptr, table->record[0], m_rec_length); reverse_order= FALSE; break; } + case partition_read_multi_range: + { + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + DBUG_PRINT("info", ("partition %u", i)); + error= file->multi_range_read_next(&m_range_info[i]); + DBUG_PRINT("info", ("error: %d", error)); + if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) + { + bitmap_clear_bit(&m_mrr_used_partitions, i); + continue; + } + if (!error) + { + memcpy(rec_buf_ptr, table->record[0], m_rec_length); + reverse_order= FALSE; + m_stock_range_seq[i]= (((PARTITION_KEY_MULTI_RANGE *) + m_range_info[i])->id); + /* Test if the key is in the first key range */ + if (m_stock_range_seq[i] != m_mrr_range_current->id) + { + /* + smallest_range_seq contains the smallest key range we have seen + so far + */ + if (!smallest_range_seq || smallest_range_seq > m_stock_range_seq[i]) + smallest_range_seq= m_stock_range_seq[i]; + continue; + } + } + break; + } default: DBUG_ASSERT(FALSE); DBUG_RETURN(HA_ERR_END_OF_FILE); @@ -6198,10 +7548,6 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) */ queue_element(&m_queue, j++)= part_rec_buf_ptr; } - else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) - { - DBUG_RETURN(error); - } else if (error == HA_ERR_KEY_NOT_FOUND) { DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i)); @@ -6209,7 +7555,53 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) m_key_not_found= true; saved_error= error; } - part_rec_buf_ptr+= m_priority_queue_rec_len; + else if (error != HA_ERR_END_OF_FILE) + { + DBUG_RETURN(error); + } + } + + if (!found && smallest_range_seq) + { + /* We know that there is an existing row based on code above */ + found= TRUE; + part_rec_buf_ptr= m_ordered_rec_buffer; + + /* + No key found in the first key range + Collect all partitions that has a key in smallest_range_seq + */ + DBUG_PRINT("info", ("partition !found && smallest_range_seq")); + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i <= m_part_spec.end_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + { + DBUG_PRINT("info", ("partition current_part: %u", i)); + if (i < m_part_spec.start_part) + { + part_rec_buf_ptr+= m_priority_queue_rec_len; + DBUG_PRINT("info", ("partition i < m_part_spec.start_part")); + continue; + } + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + { + part_rec_buf_ptr+= m_priority_queue_rec_len; + DBUG_PRINT("info", ("partition !bitmap_is_set(&m_mrr_used_partitions, i)")); + continue; + } + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); + if (smallest_range_seq == m_stock_range_seq[i]) + { + m_stock_range_seq[i]= 0; + queue_element(&m_queue, j++)= (uchar *) part_rec_buf_ptr; + DBUG_PRINT("info", ("partition smallest_range_seq == m_stock_range_seq[i]")); + } + part_rec_buf_ptr+= m_priority_queue_rec_len; + } + + /* Update global m_mrr_range_current to the current range */ + while (m_mrr_range_current->id < smallest_range_seq) + m_mrr_range_current= m_mrr_range_current->next; } if (found) { @@ -6218,12 +7610,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) after that read the first entry and copy it to the buffer to return in. */ queue_set_max_at_top(&m_queue, reverse_order); - queue_set_cmp_arg(&m_queue, m_using_extended_keys? m_curr_key_info : (void*)this); + queue_set_cmp_arg(&m_queue, (void*) this); m_queue.elements= j - queue_first_element(&m_queue); queue_fix(&m_queue); return_top_record(buf); - table->status= 0; - DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry)); DBUG_RETURN(0); } DBUG_RETURN(saved_error); @@ -6246,11 +7637,28 @@ void ha_partition::return_top_record(uchar *buf) uint part_id; uchar *key_buffer= queue_top(&m_queue); uchar *rec_buffer= key_buffer + PARTITION_BYTES_IN_POS; + DBUG_ENTER("ha_partition::return_top_record"); + DBUG_PRINT("enter", ("partition this: %p", this)); part_id= uint2korr(key_buffer); memcpy(buf, rec_buffer, m_rec_length); m_last_part= part_id; + DBUG_PRINT("info", ("partition m_last_part: %u", m_last_part)); m_top_entry= part_id; + table->status= 0; // Found an existing row + m_file[part_id]->return_record_by_parent(); + DBUG_VOID_RETURN; +} + +/* + This function is only used if the partitioned table has own partitions. + This can happen if the partitioned VP engine is used (part of spider). +*/ + +void ha_partition::return_record_by_parent() +{ + m_file[m_last_part]->return_record_by_parent(); + DBUG_ASSERT(0); } @@ -6269,6 +7677,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found() uchar *part_buf= m_ordered_rec_buffer; uchar *curr_rec_buf= NULL; DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found"); + DBUG_PRINT("enter", ("partition this: %p", this)); DBUG_ASSERT(m_key_not_found); /* Loop over all used partitions to get the correct offset @@ -6289,7 +7698,10 @@ int ha_partition::handle_ordered_index_scan_key_not_found() /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND); if (!error) + { + DBUG_PRINT("info", ("partition queue_insert(1)")); queue_insert(&m_queue, part_buf); + } else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND) DBUG_RETURN(error); } @@ -6326,11 +7738,15 @@ int ha_partition::handle_ordered_index_scan_key_not_found() int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) { int error; + DBUG_ENTER("ha_partition::handle_ordered_next"); + + if (m_top_entry == NO_CURRENT_PART_ID) + DBUG_RETURN(HA_ERR_END_OF_FILE); + uint part_id= m_top_entry; uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; handler *file; - DBUG_ENTER("ha_partition::handle_ordered_next"); - + if (m_key_not_found) { if (is_next_same) @@ -6372,6 +7788,122 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) error= file->read_range_next(); memcpy(rec_buf, table->record[0], m_rec_length); } + else if (m_index_scan_type == partition_read_multi_range) + { + DBUG_PRINT("info", ("partition_read_multi_range route")); + DBUG_PRINT("info", ("part_id: %u", part_id)); + bool get_next= FALSE; + error= file->multi_range_read_next(&m_range_info[part_id]); + DBUG_PRINT("info", ("error: %d", error)); + if (error == HA_ERR_KEY_NOT_FOUND) + error= HA_ERR_END_OF_FILE; + if (error == HA_ERR_END_OF_FILE) + { + bitmap_clear_bit(&m_mrr_used_partitions, part_id); + DBUG_PRINT("info", ("partition m_queue.elements: %u", m_queue.elements)); + if (m_queue.elements) + { + DBUG_PRINT("info", ("partition queue_remove_top(1)")); + queue_remove_top(&m_queue); + if (m_queue.elements) + { + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u (3)", + m_top_entry)); + DBUG_RETURN(0); + } + } + get_next= TRUE; + } + else if (!error) + { + DBUG_PRINT("info", ("m_range_info[%u])->id: %u", part_id, + ((PARTITION_KEY_MULTI_RANGE *) + m_range_info[part_id])->id)); + DBUG_PRINT("info", ("m_mrr_range_current->id: %u", + m_mrr_range_current->id)); + memcpy(rec_buf, table->record[0], m_rec_length); + if (((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id != + m_mrr_range_current->id) + { + m_stock_range_seq[part_id]= + ((PARTITION_KEY_MULTI_RANGE *) m_range_info[part_id])->id; + DBUG_PRINT("info", ("partition queue_remove_top(2)")); + queue_remove_top(&m_queue); + if (!m_queue.elements) + get_next= TRUE; + } + } + if (get_next) + { + DBUG_PRINT("info", ("get_next route")); + uint i, j= 0, smallest_range_seq= UINT_MAX32; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (!(bitmap_is_set(&(m_part_info->read_partitions), i))) + continue; + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + continue; + if (smallest_range_seq > m_stock_range_seq[i]) + smallest_range_seq= m_stock_range_seq[i]; + } + + DBUG_PRINT("info", ("smallest_range_seq: %u", smallest_range_seq)); + if (smallest_range_seq != UINT_MAX32) + { + uchar *part_rec_buf_ptr= m_ordered_rec_buffer; + DBUG_PRINT("info", ("partition queue_remove_all(2)")); + queue_remove_all(&m_queue); + DBUG_PRINT("info", ("m_part_spec.start_part: %u", + m_part_spec.start_part)); + + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i <= m_part_spec.end_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i), + part_rec_buf_ptr+= m_priority_queue_rec_len) + { + DBUG_PRINT("info",("partition part_id: %u", i)); + if (i < m_part_spec.start_part) + { + DBUG_PRINT("info",("partition i < m_part_spec.start_part")); + continue; + } + if (!bitmap_is_set(&m_mrr_used_partitions, i)) + { + DBUG_PRINT("info",("partition !bitmap_is_set(&m_mrr_used_partitions, i)")); + continue; + } + DBUG_PRINT("info",("partition uint2korr: %u", + uint2korr(part_rec_buf_ptr))); + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); + DBUG_PRINT("info", ("partition m_stock_range_seq[%u]: %u", + i, m_stock_range_seq[i])); + if (smallest_range_seq == m_stock_range_seq[i]) + { + m_stock_range_seq[i]= 0; + DBUG_PRINT("info", ("partition queue_insert(2)")); + queue_insert(&m_queue, part_rec_buf_ptr); + j++; + } + } + while (m_mrr_range_current->id < smallest_range_seq) + m_mrr_range_current= m_mrr_range_current->next; + + DBUG_PRINT("info",("partition m_mrr_range_current: %p", + m_mrr_range_current)); + DBUG_PRINT("info",("partition m_mrr_range_current->id: %u", + m_mrr_range_current ? m_mrr_range_current->id : 0)); + queue_set_max_at_top(&m_queue, FALSE); + queue_set_cmp_arg(&m_queue, (void*) this); + m_queue.elements= j; + queue_fix(&m_queue); + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u (4)", + m_top_entry)); + DBUG_RETURN(0); + } + } + } else if (!is_next_same) error= file->ha_index_next(rec_buf); else @@ -6380,16 +7912,16 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) if (error) { - if (error == HA_ERR_END_OF_FILE) + if (error == HA_ERR_END_OF_FILE && m_queue.elements) { /* Return next buffered row */ + DBUG_PRINT("info", ("partition queue_remove_top(3)")); queue_remove_top(&m_queue); if (m_queue.elements) { + return_top_record(buf); DBUG_PRINT("info", ("Record returned from partition %u (2)", m_top_entry)); - return_top_record(buf); - table->status= 0; error= 0; } } @@ -6425,30 +7957,35 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) int ha_partition::handle_ordered_prev(uchar *buf) { int error; + DBUG_ENTER("ha_partition::handle_ordered_prev"); + DBUG_PRINT("enter", ("partition: %p", this)); + + if (m_top_entry == NO_CURRENT_PART_ID) + DBUG_RETURN(HA_ERR_END_OF_FILE); + uint part_id= m_top_entry; uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS; handler *file= m_file[part_id]; - DBUG_ENTER("ha_partition::handle_ordered_prev"); if ((error= file->ha_index_prev(rec_buf))) { - if (error == HA_ERR_END_OF_FILE) + if (error == HA_ERR_END_OF_FILE && m_queue.elements) { + DBUG_PRINT("info", ("partition queue_remove_top(4)")); queue_remove_top(&m_queue); if (m_queue.elements) { return_top_record(buf); - DBUG_PRINT("info", ("Record returned from partition %d (2)", + DBUG_PRINT("info", ("Record returned from partition %u (2)", m_top_entry)); error= 0; - table->status= 0; } } DBUG_RETURN(error); } queue_replace_top(&m_queue); return_top_record(buf); - DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry)); DBUG_RETURN(0); } @@ -6600,7 +8137,8 @@ int ha_partition::info(uint flag) { set_if_bigger(part_share->next_auto_inc_val, auto_increment_value); - part_share->auto_inc_initialized= true; + if (can_use_for_auto_inc_init()) + part_share->auto_inc_initialized= true; DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu", (ulong) part_share->next_auto_inc_val)); } @@ -6720,15 +8258,18 @@ int ha_partition::info(uint flag) do { file= *file_array; - /* Get variables if not already done */ - if (!(flag & HA_STATUS_VARIABLE) || - !bitmap_is_set(&(m_part_info->read_partitions), - (uint)(file_array - m_file))) - file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); - if (file->stats.records > max_records) + if (bitmap_is_set(&(m_opened_partitions), (uint)(file_array - m_file))) { - max_records= file->stats.records; - handler_instance= i; + /* Get variables if not already done */ + if (!(flag & HA_STATUS_VARIABLE) || + !bitmap_is_set(&(m_part_info->read_partitions), + (uint) (file_array - m_file))) + file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); + if (file->stats.records > max_records) + { + max_records= file->stats.records; + handler_instance= i; + } } i++; } while (*(++file_array)); @@ -6810,6 +8351,94 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, } +void ha_partition::set_partitions_to_open(List<String> *partition_names) +{ + m_partitions_to_open= partition_names; +} + + +int ha_partition::open_read_partitions(char *name_buff, size_t name_buff_size) +{ + handler **file; + char *name_buffer_ptr; + int error; + + name_buffer_ptr= m_name_buffer_ptr; + file= m_file; + m_file_sample= NULL; + do + { + int n_file= (int)(file-m_file); + int is_open= bitmap_is_set(&m_opened_partitions, n_file); + int should_be_open= bitmap_is_set(&m_part_info->read_partitions, n_file); + + if (is_open && !should_be_open) + { + if ((error= (*file)->ha_close())) + goto err_handler; + bitmap_clear_bit(&m_opened_partitions, n_file); + } + else if (!is_open && should_be_open) + { + LEX_CSTRING save_connect_string= table->s->connect_string; + if ((error= create_partition_name(name_buff, name_buff_size, + table->s->normalized_path.str, + name_buffer_ptr, NORMAL_PART_NAME, FALSE))) + goto err_handler; + if (!((*file)->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION)) + table->s->connect_string= m_connect_string[(uint)(file-m_file)]; + error= (*file)->ha_open(table, name_buff, m_mode, + m_open_test_lock | HA_OPEN_NO_PSI_CALL); + table->s->connect_string= save_connect_string; + if (error) + goto err_handler; + bitmap_set_bit(&m_opened_partitions, n_file); + } + if (!m_file_sample && should_be_open) + m_file_sample= *file; + name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + } while (*(++file)); + +err_handler: + return error; +} + + +int ha_partition::change_partitions_to_open(List<String> *partition_names) +{ + char name_buff[FN_REFLEN+1]; + int error= 0; + + if (m_is_clone_of) + return 0; + + m_partitions_to_open= partition_names; + if ((error= m_part_info->set_partition_bitmaps(partition_names))) + goto err_handler; + + if (m_lock_type != F_UNLCK) + { + /* + That happens after the LOCK TABLE statement. + Do nothing in this case. + */ + return 0; + } + + if (bitmap_cmp(&m_opened_partitions, &m_part_info->read_partitions) != 0) + return 0; + + if ((error= read_par_file(table->s->normalized_path.str)) || + (error= open_read_partitions(name_buff, sizeof(name_buff)))) + goto err_handler; + + clear_handler_file(); + +err_handler: + return error; +} + + /** General function to prepare handler for certain behavior. @@ -7110,7 +8739,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, HA_EXTRA_DELETE_CANNOT_BATCH: HA_EXTRA_UPDATE_CANNOT_BATCH: Inform handler that delete_row()/update_row() cannot batch deletes/updates - and should perform them immediately. This may be needed when table has + and should perform them immediately. This may be needed when table has AFTER DELETE/UPDATE triggers which access to subject table. These flags are reset by the handler::extra(HA_EXTRA_RESET) call. @@ -7126,7 +8755,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, int ha_partition::extra(enum ha_extra_function operation) { DBUG_ENTER("ha_partition:extra"); - DBUG_PRINT("info", ("operation: %d", (int) operation)); + DBUG_PRINT("enter", ("operation: %d", (int) operation)); switch (operation) { /* Category 1), used by most handlers */ @@ -7147,13 +8776,13 @@ int ha_partition::extra(enum ha_extra_function operation) { if (!m_myisam) DBUG_RETURN(loop_extra(operation)); - break; } + break; /* Category 3), used by MyISAM handlers */ case HA_EXTRA_PREPARE_FOR_UPDATE: /* - Needs to be run on the first partition in the range now, and + Needs to be run on the first partition in the range now, and later in late_extra_cache, when switching to a new partition to scan. */ m_extra_prepare_for_update= TRUE; @@ -7222,18 +8851,9 @@ int ha_partition::extra(enum ha_extra_function operation) with row being inserted by PK/unique key without reporting error to the SQL-layer. - This optimization is not safe for partitioned table in general case - since we may have to put new version of row into partition which is - different from partition in which old version resides (for example - when we partition by non-PK column or by some column which is not - part of unique key which were violated). - And since NDB which is the only engine at the moment that supports - this optimization handles partitioning on its own we simple disable - it here. (BTW for NDB this optimization is safe since it supports - only KEY partitioning and won't use this optimization for tables - which have additional unique constraints). + At this time, this is safe by limitation of ha_partition */ - break; + DBUG_RETURN(loop_extra(operation)); } /* Category 7), used by federated handlers */ case HA_EXTRA_INSERT_WITH_UPDATE: @@ -7247,20 +8867,38 @@ 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)); case HA_EXTRA_ATTACH_CHILDREN: - case HA_EXTRA_IS_ATTACHED_CHILDREN: - case HA_EXTRA_DETACH_CHILDREN: { - /* Special actions for MERGE tables. Ignore. */ + int result; + uint num_locks; + handler **file; + if ((result= loop_extra(operation))) + DBUG_RETURN(result); + + /* Recalculate lock count as each child may have different set of locks */ + num_locks= 0; + file= m_file; + do + { + num_locks+= (*file)->lock_count(); + } while (*(++file)); + + m_num_locks= num_locks; break; } + case HA_EXTRA_IS_ATTACHED_CHILDREN: + DBUG_RETURN(loop_extra(operation)); + case HA_EXTRA_DETACH_CHILDREN: + DBUG_RETURN(loop_extra(operation)); + case HA_EXTRA_MARK_AS_LOG_TABLE: /* http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html says we no longer support logging to partitioned tables, so we fail here. */ - case HA_EXTRA_MARK_AS_LOG_TABLE: DBUG_RETURN(ER_UNSUPORTED_LOG_ENGINE); + case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: case HA_EXTRA_BEGIN_ALTER_COPY: case HA_EXTRA_END_ALTER_COPY: case HA_EXTRA_FAKE_START_STMT: @@ -7300,7 +8938,8 @@ int ha_partition::reset(void) i < m_tot_parts; i= bitmap_get_next_set(&m_partitions_to_reset, i)) { - if ((tmp= m_file[i]->ha_reset())) + if (bitmap_is_set(&m_opened_partitions, i) && + (tmp= m_file[i]->ha_reset())) result= tmp; } bitmap_clear_all(&m_partitions_to_reset); @@ -7345,7 +8984,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_PRINT("info", ("cachesize %u", cachesize)); + DBUG_PRINT("enter", ("cachesize %u", cachesize)); m_extra_cache= TRUE; m_extra_cache_size= cachesize; @@ -7412,12 +9051,17 @@ int ha_partition::loop_extra(enum ha_extra_function operation) int result= 0, tmp; uint i; DBUG_ENTER("ha_partition::loop_extra()"); - + for (i= bitmap_get_first_set(&m_part_info->lock_partitions); i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) { - if ((tmp= m_file[i]->extra(operation))) + /* + This can be called after an error in ha_open. + In this case calling 'extra' can crash. + */ + if (bitmap_is_set(&m_opened_partitions, i) && + (tmp= m_file[i]->extra(operation))) result= tmp; } /* Add all used partitions to be called in reset(). */ @@ -7441,9 +9085,9 @@ void ha_partition::late_extra_cache(uint partition_id) { handler *file; DBUG_ENTER("ha_partition::late_extra_cache"); - DBUG_PRINT("info", ("extra_cache %u prepare %u partid %u size %u", - m_extra_cache, m_extra_prepare_for_update, - partition_id, m_extra_cache_size)); + DBUG_PRINT("enter", ("extra_cache %u prepare %u partid %u size %u", + m_extra_cache, m_extra_prepare_for_update, + partition_id, m_extra_cache_size)); if (!m_extra_cache && !m_extra_prepare_for_update) DBUG_VOID_RETURN; @@ -7507,7 +9151,7 @@ void ha_partition::late_extra_no_cache(uint partition_id) const key_map *ha_partition::keys_to_use_for_scanning() { DBUG_ENTER("ha_partition::keys_to_use_for_scanning"); - DBUG_RETURN(m_file[0]->keys_to_use_for_scanning()); + DBUG_RETURN(get_open_file_sample()->keys_to_use_for_scanning()); } @@ -7640,7 +9284,7 @@ ha_rows ha_partition::records_in_range(uint inx, key_range *min_key, != NO_CURRENT_PART_ID) { rows= m_file[part_id]->records_in_range(inx, min_key, max_key); - + DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows, (ulong) m_file[part_id]->stats.records)); @@ -7730,7 +9374,7 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows) { DBUG_ENTER("ha_partition::read_time"); - DBUG_RETURN(m_file[0]->read_time(index, ranges, rows)); + DBUG_RETURN(get_open_file_sample()->read_time(index, ranges, rows)); } @@ -7742,7 +9386,8 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows) ha_rows ha_partition::records() { - ha_rows rows, tot_rows= 0; + int error; + ha_rows tot_rows= 0; uint i; DBUG_ENTER("ha_partition::records"); @@ -7750,11 +9395,13 @@ ha_rows ha_partition::records() i < m_tot_parts; i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - rows= m_file[i]->records(); - if (rows == HA_POS_ERROR) + ha_rows rows; + if ((error= m_file[i]->pre_records()) || + (rows= m_file[i]->records()) == HA_POS_ERROR) DBUG_RETURN(HA_POS_ERROR); tot_rows+= rows; } + DBUG_PRINT("exit", ("records: %lld", (longlong) tot_rows)); DBUG_RETURN(tot_rows); } @@ -7778,7 +9425,7 @@ bool ha_partition::can_switch_engines() { handler **file; DBUG_ENTER("ha_partition::can_switch_engines"); - + file= m_file; do { @@ -8013,10 +9660,9 @@ void ha_partition::print_error(int error, myf errflag) { THD *thd= ha_thd(); DBUG_ENTER("ha_partition::print_error"); - - /* Should probably look for my own errors first */ DBUG_PRINT("enter", ("error: %d", error)); + /* Should probably look for my own errors first */ if ((error == HA_ERR_NO_PARTITION_FOUND) && ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION)) { @@ -8133,9 +9779,9 @@ handler::Table_flags ha_partition::table_flags() const alter_table_flags must be on handler/table level, not on hton level due to the ha_partition hton does not know what the underlying hton is. */ -uint ha_partition::alter_table_flags(uint flags) +ulonglong ha_partition::alter_table_flags(ulonglong flags) { - uint flags_to_return; + ulonglong flags_to_return; DBUG_ENTER("ha_partition::alter_table_flags"); flags_to_return= ht->alter_table_flags(flags); @@ -8519,7 +10165,7 @@ uint ha_partition::min_record_length(uint options) const the same record. Otherwise we use the particular handler to decide if they are the same. Sort in partition id order if not equal. - MariaDB note: + MariaDB note: Please don't merge the code from MySQL that does this: We get two references and need to check if those records are the same. @@ -8533,15 +10179,18 @@ uint ha_partition::min_record_length(uint options) const int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) { int cmp; - my_ptrdiff_t diff1, diff2; + uint32 diff1, diff2; DBUG_ENTER("ha_partition::cmp_ref"); - cmp = m_file[0]->cmp_ref((ref1 + PARTITION_BYTES_IN_POS), - (ref2 + PARTITION_BYTES_IN_POS)); + cmp= get_open_file_sample()->cmp_ref((ref1 + PARTITION_BYTES_IN_POS), + (ref2 + PARTITION_BYTES_IN_POS)); if (cmp) DBUG_RETURN(cmp); - if ((ref1[0] == ref2[0]) && (ref1[1] == ref2[1])) + diff2= uint2korr(ref2); + diff1= uint2korr(ref1); + + if (diff1 == diff2) { /* This means that the references are same and are in same partition.*/ DBUG_RETURN(0); @@ -8554,22 +10203,7 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) Remove this assert if DB_ROW_ID is changed to be per partition. */ DBUG_ASSERT(!m_innodb); - - diff1= ref2[1] - ref1[1]; - diff2= ref2[0] - ref1[0]; - if (diff1 > 0) - { - DBUG_RETURN(-1); - } - if (diff1 < 0) - { - DBUG_RETURN(+1); - } - if (diff2 > 0) - { - DBUG_RETURN(-1); - } - DBUG_RETURN(+1); + DBUG_RETURN(diff2 > diff1 ? -1 : 1); } @@ -8578,6 +10212,82 @@ int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) ****************************************************************************/ +/** + Retreive new values for part_share->next_auto_inc_val if needed + + This is needed if the value has not been initialized or if one of + the underlying partitions require that the value should be re-calculated +*/ + +void ha_partition::update_next_auto_inc_val() +{ + if (!part_share->auto_inc_initialized || + need_info_for_auto_inc()) + info(HA_STATUS_AUTO); +} + + +/** + Determine whether a partition needs auto-increment initialization. + + @return + TRUE A partition needs auto-increment initialization + FALSE No partition needs auto-increment initialization + + Resets part_share->auto_inc_initialized if next auto_increment needs to be + recalculated. +*/ + +bool ha_partition::need_info_for_auto_inc() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::need_info_for_auto_inc"); + + do + { + if ((*file)->need_info_for_auto_inc()) + { + /* We have to get new auto_increment values from handler */ + part_share->auto_inc_initialized= FALSE; + DBUG_RETURN(TRUE); + } + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Determine if all partitions can use the current auto-increment value for + auto-increment initialization. + + @return + TRUE All partitions can use the current auto-increment + value for auto-increment initialization + FALSE All partitions cannot use the current + auto-increment value for auto-increment + initialization + + Notes + This function is only called for ::info(HA_STATUS_AUTO) and is + mainly used by the Spider engine, which returns false + except in the case of DROP TABLE or ALTER TABLE when it returns TRUE. + Other engines always returns TRUE for this call. +*/ + +bool ha_partition::can_use_for_auto_inc_init() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::can_use_for_auto_inc_init"); + + do + { + if (!(*file)->can_use_for_auto_inc_init()) + DBUG_RETURN(FALSE); + } while (*(++file)); + DBUG_RETURN(TRUE); +} + + int ha_partition::reset_auto_increment(ulonglong value) { handler **file= m_file; @@ -8611,8 +10321,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, ulonglong *nb_reserved_values) { DBUG_ENTER("ha_partition::get_auto_increment"); - DBUG_PRINT("info", ("offset: %lu inc: %lu desired_values: %lu " - "first_value: %lu", (ulong) offset, (ulong) increment, + DBUG_PRINT("enter", ("offset: %lu inc: %lu desired_values: %lu " + "first_value: %lu", (ulong) offset, (ulong) increment, (ulong) nb_desired_values, (ulong) *first_value)); DBUG_ASSERT(increment && nb_desired_values); *first_value= 0; @@ -8656,7 +10366,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, /* Get a lock for handling the auto_increment in part_share for avoiding two concurrent statements getting the same number. - */ + */ lock_auto_increment(); @@ -8667,8 +10377,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, based replication. Because the statement-based binary log contains only the first generated value used by the statement, and slaves assumes all other generated values used by this statement were consecutive to - this first one, we must exclusively lock the generator until the statement - is done. + this first one, we must exclusively lock the generator until the + statement is done. */ if (!auto_increment_safe_stmt_log_lock && thd->lex->sql_command != SQLCOM_INSERT && @@ -8931,8 +10641,8 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) } else { - DBUG_PRINT("info", ("Moving row from partition %d to %d", - read_part_id, correct_part_id)); + DBUG_PRINT("info", ("Moving row from partition %u to %u", + (uint) read_part_id, (uint) correct_part_id)); /* Insert row into correct partition. Notice that there are no commit @@ -8963,19 +10673,19 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) { /* Log this error, so the DBA can notice it and fix it! */ sql_print_error("Table '%-192s' failed to move/insert a row" - " from part %d into part %d:\n%s", + " from part %u into part %u:\n%s", table->s->table_name.str, - read_part_id, - correct_part_id, + (uint) read_part_id, + (uint) correct_part_id, str.c_ptr_safe()); } print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error", table_share->db.str, table->alias, opt_op_name[REPAIR_PARTS], "Failed to move/insert a row" - " from part %d into part %d:\n%s", - read_part_id, - correct_part_id, + " from part %u into part %u:\n%s", + (uint) read_part_id, + (uint) correct_part_id, str.c_ptr_safe()); break; } @@ -8996,14 +10706,14 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair) append_row_to_str(str); /* Log this error, so the DBA can notice it and fix it! */ - sql_print_error("Table '%-192s': Delete from part %d failed with" + sql_print_error("Table '%-192s': Delete from part %u failed with" " error %d. But it was already inserted into" - " part %d, when moving the misplaced row!" + " part %u, when moving the misplaced row!" "\nPlease manually fix the duplicate row:\n%s", table->s->table_name.str, - read_part_id, + (uint) read_part_id, result, - correct_part_id, + (uint) correct_part_id, str.c_ptr_safe()); break; } @@ -9081,10 +10791,8 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt) partition_info::enum_key_algorithm old_algorithm; old_algorithm= m_part_info->key_algorithm; error= HA_ADMIN_FAILED; - append_identifier(ha_thd(), &db_name, table_share->db.str, - table_share->db.length); - append_identifier(ha_thd(), &table_name, table_share->table_name.str, - table_share->table_name.length); + append_identifier(ha_thd(), &db_name, &table_share->db); + append_identifier(ha_thd(), &table_name, &table_share->table_name); if (m_part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE) { /* @@ -9133,6 +10841,670 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt) } +TABLE_LIST *ha_partition::get_next_global_for_child() +{ + handler **file; + DBUG_ENTER("ha_partition::get_next_global_for_child"); + for (file= m_file; *file; file++) + { + TABLE_LIST *table_list; + if ((table_list= (*file)->get_next_global_for_child())) + DBUG_RETURN(table_list); + } + DBUG_RETURN(0); +} + + +const COND *ha_partition::cond_push(const COND *cond) +{ + handler **file= m_file; + COND *res_cond= NULL; + DBUG_ENTER("ha_partition::cond_push"); + + if (set_top_table_fields) + { + /* + We want to do this in a separate loop to not come into a situation + where we have only done cond_push() to some of the tables + */ + do + { + if (((*file)->set_top_table_and_fields(top_table, + top_table_field, + top_table_fields))) + DBUG_RETURN(cond); // Abort cond push, no error + } while (*(++file)); + file= m_file; + } + + do + { + if ((*file)->pushed_cond != cond) + { + if ((*file)->cond_push(cond)) + res_cond= (COND *) cond; + else + (*file)->pushed_cond= cond; + } + } while (*(++file)); + DBUG_RETURN(res_cond); +} + + +void ha_partition::cond_pop() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::cond_push"); + + do + { + (*file)->cond_pop(); + } while (*(++file)); + DBUG_VOID_RETURN; +} + + +/** + Perform bulk update preparation on each partition. + + SYNOPSIS + start_bulk_update() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +bool ha_partition::start_bulk_update() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::start_bulk_update"); + + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + DBUG_RETURN(TRUE); + + do + { + if ((*file)->start_bulk_update()) + DBUG_RETURN(TRUE); + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Perform bulk update execution on each partition. A bulk update allows + a handler to batch the updated rows instead of performing the updates + one row at a time. + + SYNOPSIS + exec_bulk_update() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +int ha_partition::exec_bulk_update(ha_rows *dup_key_found) +{ + int error; + handler **file= m_file; + DBUG_ENTER("ha_partition::exec_bulk_update"); + + do + { + if ((error= (*file)->exec_bulk_update(dup_key_found))) + DBUG_RETURN(error); + } while (*(++file)); + DBUG_RETURN(0); +} + + +/** + Perform bulk update cleanup on each partition. + + SYNOPSIS + end_bulk_update() + + RETURN VALUE + NONE +*/ + +int ha_partition::end_bulk_update() +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::end_bulk_update"); + + do + { + int tmp; + if ((tmp= (*file)->end_bulk_update())) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +/** + Add the row to the bulk update on the partition on which the row is stored. + A bulk update allows a handler to batch the updated rows instead of + performing the updates one row at a time. + + SYNOPSIS + bulk_update_row() + old_data Old record + new_data New record + dup_key_found Number of duplicate keys found + + RETURN VALUE + >1 Error + 1 Bulk update not used, normal operation used + 0 Bulk update used by handler +*/ + +int ha_partition::bulk_update_row(const uchar *old_data, const uchar *new_data, + ha_rows *dup_key_found) +{ + int error= 0; + uint32 part_id; + longlong func_value; + my_bitmap_map *old_map; + DBUG_ENTER("ha_partition::bulk_update_row"); + + old_map= dbug_tmp_use_all_columns(table, table->read_set); + error= m_part_info->get_partition_id(m_part_info, &part_id, + &func_value); + dbug_tmp_restore_column_map(table->read_set, old_map); + if (unlikely(error)) + { + m_part_info->err_value= func_value; + goto end; + } + + error= m_file[part_id]->ha_bulk_update_row(old_data, new_data, + dup_key_found); + +end: + DBUG_RETURN(error); +} + + +/** + Perform bulk delete preparation on each partition. + + SYNOPSIS + start_bulk_delete() + + RETURN VALUE + TRUE Error + FALSE Success +*/ + +bool ha_partition::start_bulk_delete() +{ + handler **file= m_file; + DBUG_ENTER("ha_partition::start_bulk_delete"); + + do + { + if ((*file)->start_bulk_delete()) + DBUG_RETURN(TRUE); + } while (*(++file)); + DBUG_RETURN(FALSE); +} + + +/** + Perform bulk delete cleanup on each partition. + + SYNOPSIS + end_bulk_delete() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::end_bulk_delete() +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::end_bulk_delete"); + + do + { + int tmp; + if ((tmp= (*file)->end_bulk_delete())) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +/** + Perform initialization for a direct update request. + + SYNOPSIS + direct_update_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_update_rows_init() +{ + int error; + uint i, found; + handler *file; + DBUG_ENTER("ha_partition::direct_update_rows_init"); + + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + table->write_set)) + { + DBUG_PRINT("info", ("partition FALSE by updating part_key")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + m_direct_update_part_spec= m_part_spec; + + found= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + file= m_file[i]; + if ((error= (m_pre_calling ? + file->pre_direct_update_rows_init() : + file->direct_update_rows_init()))) + { + DBUG_PRINT("info", ("partition FALSE by storage engine")); + DBUG_RETURN(error); + } + found++; + } + } + + TABLE_LIST *table_list= table->pos_in_table_list; + if (found != 1 && table_list) + { + while (table_list->parent_l) + table_list= table_list->parent_l; + st_select_lex *select_lex= table_list->select_lex; + DBUG_PRINT("info", ("partition select_lex: %p", select_lex)); + if (select_lex && select_lex->explicit_limit) + { + DBUG_PRINT("info", ("partition explicit_limit=TRUE")); + DBUG_PRINT("info", ("partition offset_limit: %p", + select_lex->offset_limit)); + DBUG_PRINT("info", ("partition select_limit: %p", + select_lex->select_limit)); + DBUG_PRINT("info", ("partition FALSE by select_lex")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + } + DBUG_PRINT("info", ("partition OK")); + DBUG_RETURN(0); +} + + +/** + Do initialization for performing parallel direct update + for a handlersocket update request. + + SYNOPSIS + pre_direct_update_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_update_rows_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_direct_update_rows_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_update_rows_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Execute a direct update request. A direct update request updates all + qualified rows in a single operation, rather than one row at a time. + The direct update operation is pushed down to each individual + partition. + + SYNOPSIS + direct_update_rows() + update_rows Number of updated rows + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_update_rows(ha_rows *update_rows_result) +{ + int error; + bool rnd_seq= FALSE; + ha_rows update_rows= 0; + uint32 i; + DBUG_ENTER("ha_partition::direct_update_rows"); + + /* If first call to direct_update_rows with RND scan */ + if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1) + { + rnd_seq= TRUE; + m_scan_value= 2; + } + + *update_rows_result= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + handler *file= m_file[i]; + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE) + { + if ((error= (m_pre_calling ? + file->ha_pre_rnd_init(TRUE) : + file->ha_rnd_init(TRUE)))) + DBUG_RETURN(error); + } + if ((error= (m_pre_calling ? + (file)->pre_direct_update_rows() : + (file)->ha_direct_update_rows(&update_rows)))) + { + if (rnd_seq) + { + if (m_pre_calling) + file->ha_pre_rnd_end(); + else + file->ha_rnd_end(); + } + DBUG_RETURN(error); + } + *update_rows_result+= update_rows; + } + if (rnd_seq) + { + if ((error= (m_pre_calling ? + file->ha_pre_index_or_rnd_end() : + file->ha_index_or_rnd_end()))) + DBUG_RETURN(error); + } + } + DBUG_RETURN(0); +} + + +/** + Start parallel execution of a direct update for a handlersocket update + request. A direct update request updates all qualified rows in a single + operation, rather than one row at a time. The direct update operation + is pushed down to each individual partition. + + SYNOPSIS + pre_direct_update_rows() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_update_rows() +{ + bool save_m_pre_calling; + int error; + ha_rows not_used= 0; + DBUG_ENTER("ha_partition::pre_direct_update_rows"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_update_rows(¬_used); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Perform initialization for a direct delete request. + + SYNOPSIS + direct_delete_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_delete_rows_init() +{ + int error; + uint i, found; + DBUG_ENTER("ha_partition::direct_delete_rows_init"); + + m_part_spec.start_part= 0; + m_part_spec.end_part= m_tot_parts - 1; + m_direct_update_part_spec= m_part_spec; + + found= 0; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + handler *file= m_file[i]; + if ((error= (m_pre_calling ? + file->pre_direct_delete_rows_init() : + file->direct_delete_rows_init()))) + { + DBUG_PRINT("exit", ("error in direct_delete_rows_init")); + DBUG_RETURN(error); + } + found++; + } + } + + TABLE_LIST *table_list= table->pos_in_table_list; + if (found != 1 && table_list) + { + while (table_list->parent_l) + table_list= table_list->parent_l; + st_select_lex *select_lex= table_list->select_lex; + DBUG_PRINT("info", ("partition select_lex: %p", select_lex)); + if (select_lex && select_lex->explicit_limit) + { + DBUG_PRINT("info", ("partition explicit_limit: TRUE")); + DBUG_PRINT("info", ("partition offset_limit: %p", + select_lex->offset_limit)); + DBUG_PRINT("info", ("partition select_limit: %p", + select_lex->select_limit)); + DBUG_PRINT("info", ("partition FALSE by select_lex")); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + } + DBUG_PRINT("exit", ("OK")); + DBUG_RETURN(0); +} + + +/** + Do initialization for performing parallel direct delete + for a handlersocket delete request. + + SYNOPSIS + pre_direct_delete_rows_init() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_delete_rows_init() +{ + bool save_m_pre_calling; + int error; + DBUG_ENTER("ha_partition::pre_direct_delete_rows_init"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_delete_rows_init(); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + + +/** + Execute a direct delete request. A direct delete request deletes all + qualified rows in a single operation, rather than one row at a time. + The direct delete operation is pushed down to each individual + partition. + + SYNOPSIS + direct_delete_rows() + delete_rows Number of deleted rows + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::direct_delete_rows(ha_rows *delete_rows_result) +{ + int error; + bool rnd_seq= FALSE; + ha_rows delete_rows= 0; + uint32 i; + handler *file; + DBUG_ENTER("ha_partition::direct_delete_rows"); + + if ((m_pre_calling ? pre_inited : inited) == RND && m_scan_value == 1) + { + rnd_seq= TRUE; + m_scan_value= 2; + } + + *delete_rows_result= 0; + m_part_spec= m_direct_update_part_spec; + for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + { + file= m_file[i]; + if (bitmap_is_set(&(m_part_info->read_partitions), i) && + bitmap_is_set(&(m_part_info->lock_partitions), i)) + { + if (rnd_seq && (m_pre_calling ? file->pre_inited : file->inited) == NONE) + { + if ((error= (m_pre_calling ? + file->ha_pre_rnd_init(TRUE) : + file->ha_rnd_init(TRUE)))) + DBUG_RETURN(error); + } + if ((error= (m_pre_calling ? + file->pre_direct_delete_rows() : + file->ha_direct_delete_rows(&delete_rows)))) + { + if (m_pre_calling) + file->ha_pre_rnd_end(); + else + file->ha_rnd_end(); + DBUG_RETURN(error); + } + delete_rows_result+= delete_rows; + } + if (rnd_seq) + { + if ((error= (m_pre_calling ? + file->ha_pre_index_or_rnd_end() : + file->ha_index_or_rnd_end()))) + DBUG_RETURN(error); + } + } + DBUG_RETURN(0); +} + + +/** + Start parallel execution of a direct delete for a handlersocket delete + request. A direct delete request deletes all qualified rows in a single + operation, rather than one row at a time. The direct delete operation + is pushed down to each individual partition. + + SYNOPSIS + pre_direct_delete_rows() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::pre_direct_delete_rows() +{ + bool save_m_pre_calling; + int error; + ha_rows not_used; + DBUG_ENTER("ha_partition::pre_direct_delete_rows"); + save_m_pre_calling= m_pre_calling; + m_pre_calling= TRUE; + error= direct_delete_rows(¬_used); + m_pre_calling= save_m_pre_calling; + DBUG_RETURN(error); +} + +/** + Push metadata for the current operation down to each partition. + + SYNOPSIS + info_push() + + RETURN VALUE + >0 Error + 0 Success +*/ + +int ha_partition::info_push(uint info_type, void *info) +{ + int error= 0; + handler **file= m_file; + DBUG_ENTER("ha_partition::info_push"); + + do + { + int tmp; + if ((tmp= (*file)->info_push(info_type, info))) + error= tmp; + } while (*(++file)); + DBUG_RETURN(error); +} + + +void ha_partition::clear_top_table_fields() +{ + handler **file; + DBUG_ENTER("ha_partition::clear_top_table_fields"); + + if (set_top_table_fields) + { + set_top_table_fields= FALSE; + top_table= NULL; + top_table_field= NULL; + top_table_fields= 0; + for (file= m_file; *file; file++) + (*file)->clear_top_table_fields(); + } + DBUG_VOID_RETURN; +} + + struct st_mysql_storage_engine partition_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 42081546988..30dd24b6014 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -48,10 +48,8 @@ public: { uint i; for (i= 0; i < num_parts; i++) - if (ha_shares[i]) - delete ha_shares[i]; - if (ha_shares) - delete [] ha_shares; + delete ha_shares[i]; + delete[] ha_shares; } bool init(uint arg_num_parts) { @@ -69,6 +67,21 @@ public: } }; +class ha_partition; + +/* Partition Full Text Search info */ +struct st_partition_ft_info +{ + struct _ft_vft *please; + st_partition_ft_info *next; + ha_partition *file; + FT_INFO **part_ft_info; +}; + + +#ifdef HAVE_PSI_MUTEX_INTERFACE +extern PSI_mutex_key key_partition_auto_inc_mutex; +#endif /** Partition specific Handler_share. @@ -86,30 +99,117 @@ public: bool partition_name_hash_initialized; HASH partition_name_hash; /** Storage for each partitions Handler_share */ - Parts_share_refs *partitions_share_refs; - Partition_share() {} + Parts_share_refs partitions_share_refs; + Partition_share() + : auto_inc_initialized(false), + next_auto_inc_val(0), + partition_name_hash_initialized(false), + partition_names(NULL) + { + mysql_mutex_init(key_partition_auto_inc_mutex, + &auto_inc_mutex, + MY_MUTEX_INIT_FAST); + } + ~Partition_share() { - DBUG_ENTER("Partition_share::~Partition_share"); mysql_mutex_destroy(&auto_inc_mutex); + if (partition_names) + { + my_free(partition_names); + } if (partition_name_hash_initialized) + { my_hash_free(&partition_name_hash); - if (partitions_share_refs) - delete partitions_share_refs; - DBUG_VOID_RETURN; + } } + bool init(uint num_parts); - void lock_auto_inc() + + /** + Release reserved auto increment values not used. + @param thd Thread. + @param table_share Table Share + @param next_insert_id Next insert id (first non used auto inc value). + @param max_reserved End of reserved auto inc range. + */ + void release_auto_inc_if_possible(THD *thd, TABLE_SHARE *table_share, + const ulonglong next_insert_id, + const ulonglong max_reserved); + + /** lock mutex protecting auto increment value next_auto_inc_val. */ + inline void lock_auto_inc() { mysql_mutex_lock(&auto_inc_mutex); } - void unlock_auto_inc() + /** unlock mutex protecting auto increment value next_auto_inc_val. */ + inline void unlock_auto_inc() { mysql_mutex_unlock(&auto_inc_mutex); } + /** + Populate partition_name_hash with partition and subpartition names + from part_info. + @param part_info Partition info containing all partitions metadata. + + @return Operation status. + @retval false Success. + @retval true Failure. + */ + bool populate_partition_name_hash(partition_info *part_info); + /** Get partition name. + + @param part_id Partition id (for subpartitioned table only subpartition + names will be returned.) + + @return partition name or NULL if error. + */ + const char *get_partition_name(size_t part_id) const; +private: + const uchar **partition_names; + /** + Insert [sub]partition name into partition_name_hash + @param name Partition name. + @param part_id Partition id. + @param is_subpart True if subpartition else partition. + + @return Operation status. + @retval false Success. + @retval true Failure. + */ + bool insert_partition_name_in_hash(const char *name, + uint part_id, + bool is_subpart); }; +typedef struct st_partition_key_multi_range +{ + uint id; + uchar *key[2]; + uint length[2]; + KEY_MULTI_RANGE key_multi_range; + range_id_t ptr; + st_partition_key_multi_range *next; +} PARTITION_KEY_MULTI_RANGE; + +typedef struct st_partition_part_key_multi_range +{ + PARTITION_KEY_MULTI_RANGE *partition_key_multi_range; + st_partition_part_key_multi_range *next; +} PARTITION_PART_KEY_MULTI_RANGE; + + +class ha_partition; +typedef struct st_partition_part_key_multi_range_hld +{ + ha_partition *partition; + uint32 part_id; + PARTITION_PART_KEY_MULTI_RANGE *partition_part_key_multi_range; +} PARTITION_PART_KEY_MULTI_RANGE_HLD; + + +extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); class ha_partition :public handler @@ -119,16 +219,17 @@ private: { partition_index_read= 0, partition_index_first= 1, - partition_index_first_unordered= 2, partition_index_last= 3, partition_index_read_last= 4, partition_read_range = 5, - partition_no_index_scan= 6 + partition_no_index_scan= 6, + partition_read_multi_range = 7, + partition_ft_read= 8 }; /* Data for the partition handler */ int m_mode; // Open mode uint m_open_test_lock; // Open test_if_locked - uchar *m_file_buffer; // Content of the .par file + uchar *m_file_buffer; // Content of the .par file char *m_name_buffer_ptr; // Pointer to first partition name MEM_ROOT m_mem_root; plugin_ref *m_engine_array; // Array of types of the handlers @@ -141,6 +242,8 @@ private: partition_info *m_part_info; // local reference to partition Field **m_part_field_array; // Part field array locally to save acc uchar *m_ordered_rec_buffer; // Row and key buffer for ord. idx scan + st_partition_ft_info *ft_first; + st_partition_ft_info *ft_current; /* Current index. When used in key_rec_cmp: If clustered pk, index compare @@ -157,10 +260,10 @@ private: Length of an element in m_ordered_rec_buffer. The elements are composed of [part_no] [table->record copy] [underlying_table_rowid] - + underlying_table_rowid is only stored when the table has no extended keys. */ - uint m_priority_queue_rec_len; + size_t m_priority_queue_rec_len; /* If true, then sorting records by key value also sorts them by their @@ -205,14 +308,16 @@ private: bool m_create_handler; // Handler used to create table bool m_is_sub_partitioned; // Is subpartitioned bool m_ordered_scan_ongoing; + bool m_rnd_init_and_first; + bool m_ft_init_and_first; - /* + /* If set, this object was created with ha_partition::clone and doesn't "own" the m_part_info structure. */ ha_partition *m_is_clone_of; MEM_ROOT *m_clone_mem_root; - + /* We keep track if all underlying handlers are MyISAM since MyISAM has a great number of extra flags not needed by other handlers. @@ -257,6 +362,12 @@ private: ha_rows m_bulk_inserted_rows; /** used for prediction of start_bulk_insert rows */ enum_monotonicity_info m_part_func_monotonicity_info; + part_id_range m_direct_update_part_spec; + bool m_pre_calling; + bool m_pre_call_use_parallel; + /* Keep track of bulk access requests */ + bool bulk_access_executing; + /** keep track of locked partitions */ MY_BITMAP m_locked_partitions; /** Stores shared auto_increment etc. */ @@ -274,7 +385,24 @@ private: /** partitions that returned HA_ERR_KEY_NOT_FOUND. */ MY_BITMAP m_key_not_found_partitions; bool m_key_not_found; + List<String> *m_partitions_to_open; + MY_BITMAP m_opened_partitions; + /** This is one of the m_file-s that it guaranteed to be opened. */ + /** It is set in open_read_partitions() */ + handler *m_file_sample; public: + handler **get_child_handlers() + { + return m_file; + } + virtual part_id_range *get_part_spec() + { + return &m_part_spec; + } + virtual uint get_no_current_part_id() + { + return NO_CURRENT_PART_ID; + } Partition_share *get_part_share() { return part_share; } handler *clone(const char *name, MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) @@ -282,6 +410,9 @@ public: m_part_info= part_info; m_is_sub_partitioned= part_info->is_sub_partitioned(); } + + virtual void return_record_by_parent(); + /* ------------------------------------------------------------------------- MODULE create/delete handler object @@ -300,6 +431,7 @@ public: ha_partition *clone_arg, MEM_ROOT *clone_mem_root_arg); ~ha_partition(); + void ha_partition_init(); /* A partition handler has no characteristics in itself. It only inherits those from the underlying handlers. Here we set-up those constants to @@ -483,8 +615,23 @@ public: number of calls to write_row. */ virtual int write_row(uchar * buf); + virtual bool start_bulk_update(); + virtual int exec_bulk_update(ha_rows *dup_key_found); + virtual int end_bulk_update(); + virtual int bulk_update_row(const uchar *old_data, const uchar *new_data, + ha_rows *dup_key_found); virtual int update_row(const uchar * old_data, const uchar * new_data); + virtual int direct_update_rows_init(); + virtual int pre_direct_update_rows_init(); + virtual int direct_update_rows(ha_rows *update_rows); + virtual int pre_direct_update_rows(); + virtual bool start_bulk_delete(); + virtual int end_bulk_delete(); virtual int delete_row(const uchar * buf); + virtual int direct_delete_rows_init(); + virtual int pre_direct_delete_rows_init(); + virtual int direct_delete_rows(ha_rows *delete_rows); + virtual int pre_direct_delete_rows(); virtual int delete_all_rows(void); virtual int truncate(); virtual void start_bulk_insert(ha_rows rows, uint flags); @@ -603,20 +750,16 @@ public: virtual int index_last(uchar * buf); virtual int index_next_same(uchar * buf, const uchar * key, uint keylen); + int index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map); + /* read_first_row is virtual method but is only implemented by handler.cc, no storage engine has implemented it so neither will the partition handler. - - virtual int read_first_row(uchar *buf, uint primary_key); - */ - /* - We don't implement multi read range yet, will do later. - virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, - KEY_MULTI_RANGE *ranges, uint range_count, - bool sorted, HANDLER_BUFFER *buffer); - virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); + virtual int read_first_row(uchar *buf, uint primary_key); */ @@ -625,12 +768,59 @@ public: bool eq_range, bool sorted); virtual int read_range_next(); + + HANDLER_BUFFER *m_mrr_buffer; + uint *m_mrr_buffer_size; + uchar *m_mrr_full_buffer; + uint m_mrr_full_buffer_size; + uint m_mrr_new_full_buffer_size; + MY_BITMAP m_mrr_used_partitions; + uint *m_stock_range_seq; + uint m_current_range_seq; + uint m_mrr_mode; + uint m_mrr_n_ranges; + range_id_t *m_range_info; + bool m_multi_range_read_first; + uint m_mrr_range_init_flags; + uint m_mrr_range_length; + PARTITION_KEY_MULTI_RANGE *m_mrr_range_first; + PARTITION_KEY_MULTI_RANGE *m_mrr_range_current; + uint *m_part_mrr_range_length; + PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_first; + PARTITION_PART_KEY_MULTI_RANGE **m_part_mrr_range_current; + PARTITION_PART_KEY_MULTI_RANGE_HLD *m_partition_part_key_multi_range_hld; + range_seq_t m_seq; + RANGE_SEQ_IF *m_seq_if; + RANGE_SEQ_IF m_part_seq_if; + + virtual int multi_range_key_create_key( + RANGE_SEQ_IF *seq, + range_seq_t seq_it + ); + virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint *bufsz, + uint *mrr_mode, + Cost_estimate *cost); + virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys, + uint key_parts, uint *bufsz, + uint *mrr_mode, Cost_estimate *cost); + virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, + uint n_ranges, uint mrr_mode, + HANDLER_BUFFER *buf); + virtual int multi_range_read_next(range_id_t *range_info); + virtual int multi_range_read_explain_info(uint mrr_mode, char *str, + size_t size); + uint last_part() { return m_last_part; } + private: bool init_record_priority_queue(); void destroy_record_priority_queue(); int common_index_read(uchar * buf, bool have_start_key); int common_first_last(uchar * buf); int partition_scan_set_up(uchar * buf, bool idx_read_flag); + bool check_parallel_search(); + int handle_pre_scan(bool reverse_order, bool use_parallel); int handle_unordered_next(uchar * buf, bool next_same); int handle_unordered_scan_next_partition(uchar * buf); int handle_ordered_index_scan(uchar * buf, bool reverse_order); @@ -651,6 +841,9 @@ public: virtual int info(uint); void get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id); + void set_partitions_to_open(List<String> *partition_names); + 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 reset(void); @@ -671,12 +864,13 @@ private: Query_cache_block_table **block_table, handler *file, uint *n); - static const uint NO_CURRENT_PART_ID; + static const uint NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; int loop_extra(enum ha_extra_function operation); int loop_extra_alter(enum ha_extra_function operations); void late_extra_cache(uint partition_id); void late_extra_no_cache(uint partition_id); void prepare_extra_cache(uint cachesize); + handler *get_open_file_sample() const { return m_file_sample; } public: /* @@ -920,7 +1114,7 @@ public: special file for handling names of partitions, engine types. HA_REC_NOT_IN_SEQ is always set for partition handler since we cannot guarantee that the records will be returned in sequence. - HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPLICATE_POS, + HA_DUPLICATE_POS, HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled until further investigated. */ @@ -995,14 +1189,14 @@ public: wrapper function for handlerton alter_table_flags, since the ha_partition_hton cannot know all its capabilities */ - virtual uint alter_table_flags(uint flags); + virtual ulonglong alter_table_flags(ulonglong flags); /* unireg.cc will call the following to make sure that the storage engine can handle the data it is about to send. The maximum supported values is the minimum of all handlers in the table */ - uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const; + uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const; virtual uint max_supported_record_length() const; virtual uint max_supported_keys() const; virtual uint max_supported_key_parts() const; @@ -1047,6 +1241,8 @@ public: auto_increment_column_changed ------------------------------------------------------------------------- */ + virtual bool need_info_for_auto_inc(); + virtual bool can_use_for_auto_inc_init(); virtual void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, @@ -1054,16 +1250,17 @@ public: virtual void release_auto_increment(); private: virtual int reset_auto_increment(ulonglong value); + void update_next_auto_inc_val(); virtual void lock_auto_increment() { /* lock already taken */ if (auto_increment_safe_stmt_log_lock) return; - DBUG_ASSERT(!auto_increment_lock); - if(table_share->tmp_table == NO_TMP_TABLE) + if (table_share->tmp_table == NO_TMP_TABLE) { - auto_increment_lock= TRUE; part_share->lock_auto_inc(); + DBUG_ASSERT(!auto_increment_lock); + auto_increment_lock= TRUE; } } virtual void unlock_auto_increment() @@ -1073,10 +1270,10 @@ private: It will be set to false and thus unlocked at the end of the statement by ha_partition::release_auto_increment. */ - if(auto_increment_lock && !auto_increment_safe_stmt_log_lock) + if (auto_increment_lock && !auto_increment_safe_stmt_log_lock) { - part_share->unlock_auto_inc(); auto_increment_lock= FALSE; + part_share->unlock_auto_inc(); } } virtual void set_auto_increment_if_higher(Field *field) @@ -1084,7 +1281,8 @@ private: ulonglong nr= (((Field_num*) field)->unsigned_flag || field->val_int() > 0) ? field->val_int() : 0; lock_auto_increment(); - DBUG_ASSERT(part_share->auto_inc_initialized); + DBUG_ASSERT(part_share->auto_inc_initialized || + !can_use_for_auto_inc_init()); /* must check when the mutex is taken */ if (nr >= part_share->next_auto_inc_val) part_share->next_auto_inc_val= nr + 1; @@ -1133,14 +1331,15 @@ public: ------------------------------------------------------------------------- MODULE fulltext index ------------------------------------------------------------------------- - Fulltext stuff not yet. - ------------------------------------------------------------------------- - virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } - virtual FT_INFO *ft_init_ext(uint flags,uint inx,const uchar *key, - uint keylen) - { return NULL; } - virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } */ + void ft_close_search(FT_INFO *handler); + virtual int ft_init(); + virtual int pre_ft_init(); + virtual void ft_end(); + virtual int pre_ft_end(); + virtual FT_INFO *ft_init_ext(uint flags, uint inx, String *key); + virtual int ft_read(uchar *buf); + virtual int pre_ft_read(bool use_parallel); /* ------------------------------------------------------------------------- @@ -1202,6 +1401,16 @@ public: virtual bool is_crashed() const; virtual int check_for_upgrade(HA_CHECK_OPT *check_opt); + /* + ------------------------------------------------------------------------- + MODULE condition pushdown + ------------------------------------------------------------------------- + */ + virtual const COND *cond_push(const COND *cond); + virtual void cond_pop(); + virtual void clear_top_table_fields(); + virtual int info_push(uint info_type, void *info); + private: int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags); int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, uint part_id, @@ -1229,6 +1438,7 @@ public: /* Enabled keycache for performance reasons, WL#4571 */ virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt); virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); + virtual TABLE_LIST *get_next_global_for_child(); /* ------------------------------------------------------------------------- @@ -1275,7 +1485,32 @@ public: return h; } + virtual ha_rows part_records(void *_part_elem) + { + partition_element *part_elem= reinterpret_cast<partition_element *>(_part_elem); + DBUG_ASSERT(m_part_info); + uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + uint32 part_id= part_elem->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part_id_end <= m_tot_parts); + ha_rows part_recs= 0; + for (; part_id < part_id_end; ++part_id) + { + handler *file= m_file[part_id]; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); + file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_OPEN); + part_recs+= file->stats.records; + } + return part_recs; + } + + virtual handler* part_handler(uint32 part_id) + { + DBUG_ASSERT(part_id < m_tot_parts); + return m_file[part_id]; + } + friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); + friend int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2); }; - #endif /* HA_PARTITION_INCLUDED */ diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc index 6923997cf07..4afa2168b8d 100644 --- a/sql/ha_sequence.cc +++ b/sql/ha_sequence.cc @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_list.h" #include "table.h" #include "sql_sequence.h" @@ -259,7 +259,8 @@ int ha_sequence::write_row(uchar *buf) sequence->copy(&tmp_seq); rows_changed++; /* We have to do the logging while we hold the sequence mutex */ - error= binlog_log_row(table, 0, buf, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, 0, buf, log_func); row_already_logged= 1; } diff --git a/sql/handler.cc b/sql/handler.cc index 9148d9a65f2..0534a701b73 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -20,10 +20,10 @@ Handler-calling-functions */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" -#include "rpl_handler.h" +#include "rpl_rli.h" #include "sql_cache.h" // query_cache, query_cache_* #include "sql_connect.h" // global_table_stats #include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp @@ -50,6 +50,7 @@ #ifdef WITH_ARIA_STORAGE_ENGINE #include "../storage/maria/ha_maria.h" #endif +#include "semisync_master.h" #include "wsrep_mysqld.h" #include "wsrep.h" @@ -1414,6 +1415,40 @@ int ha_commit_trans(THD *thd, bool all) goto err; } +#if 1 // FIXME: This should be done in ha_prepare(). + if (rw_trans || (thd->lex->sql_command == SQLCOM_ALTER_TABLE && + thd->lex->alter_info.flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING)) + { + ulonglong trx_start_id= 0, trx_end_id= 0; + for (Ha_trx_info *ha_info= trans->ha_list; ha_info; ha_info= ha_info->next()) + { + if (ha_info->ht()->prepare_commit_versioned) + { + trx_end_id= ha_info->ht()->prepare_commit_versioned(thd, &trx_start_id); + if (trx_end_id) + break; // FIXME: use a common ID for cross-engine transactions + } + } + + if (trx_end_id) + { + if (!TR_table::use_transaction_registry) + { + my_error(ER_VERS_TRT_IS_DISABLED, MYF(0)); + goto err; + } + DBUG_ASSERT(trx_start_id); + TR_table trt(thd, true); + if (trt.update(trx_start_id, trx_end_id)) + goto err; + // Here, the call will not commit inside InnoDB. It is only working + // around closing thd->transaction.stmt open by TR_table::open(). + if (all) + commit_one_phase_2(thd, false, &thd->transaction.stmt, false); + } + } +#endif + if (trans->no_2pc || (rw_ha_count <= 1)) { error= ha_commit_one_phase(thd, all); @@ -1484,7 +1519,10 @@ done: mysql_mutex_assert_not_owner(mysql_bin_log.get_log_lock()); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - RUN_HOOK(transaction, after_commit, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, all); + DEBUG_SYNC(thd, "after_group_after_commit"); +#endif goto end; /* Come here if error and we need to rollback. */ @@ -1558,6 +1596,7 @@ static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) { int error= 0; + uint count= 0; Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; DBUG_ENTER("commit_one_phase_2"); if (is_real_trans) @@ -1575,6 +1614,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) } /* Should this be done only if is_real_trans is set ? */ status_var_increment(thd->status_var.ha_commit_count); + if (is_real_trans && ht != binlog_hton && ha_info->is_trx_read_write()) + ++count; ha_info_next= ha_info->next(); ha_info->reset(); /* keep it conveniently zero-filled */ } @@ -1593,6 +1634,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) { thd->has_waiter= false; thd->transaction.cleanup(); + if (count >= 2) + statistic_increment(transactions_multi_engine, LOCK_status); } DBUG_RETURN(error); @@ -1724,7 +1767,9 @@ int ha_rollback_trans(THD *thd, bool all) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER_THD(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK)); - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, all); +#endif DBUG_RETURN(error); } @@ -2456,7 +2501,7 @@ const char *get_canonical_filename(handler *file, const char *path, The .frm file will be deleted only if we return 0. */ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, - const char *db, const char *alias, bool generate_warning) + const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning) { handler *file; char tmp_path[FN_REFLEN]; @@ -2489,12 +2534,9 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, dummy_share.path.str= (char*) path; dummy_share.path.length= strlen(path); dummy_share.normalized_path= dummy_share.path; - dummy_share.db.str= (char*) db; - dummy_share.db.length= strlen(db); - dummy_share.table_name.str= (char*) alias; - dummy_share.table_name.length= strlen(alias); - dummy_table.alias.set(alias, dummy_share.table_name.length, - table_alias_charset); + dummy_share.db= *db; + dummy_share.table_name= *alias; + dummy_table.alias.set(alias->str, alias->length, table_alias_charset); file->change_table_ptr(&dummy_table, &dummy_share); file->print_error(error, MYF(intercept ? ME_JUST_WARNING : 0)); } @@ -2509,6 +2551,18 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, /**************************************************************************** ** General handler functions ****************************************************************************/ + + +/** + Clone a handler + + @param name name of new table instance + @param mem_root Where 'this->ref' should be allocated. It can't be + in this->table->mem_root as otherwise we will not be + able to reclaim that memory when the clone handler + object is destroyed. +*/ + handler *handler::clone(const char *name, MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, ht); @@ -2519,16 +2573,6 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) goto err; /* - Allocate handler->ref here because otherwise ha_open will allocate it - on this->table->mem_root and we will not be able to reclaim that memory - when the clone handler object is destroyed. - */ - - if (!(new_handler->ref= (uchar*) alloc_root(mem_root, - ALIGN_SIZE(ref_length)*2))) - goto err; - - /* TODO: Implement a more efficient way to have more than one index open for the same table instance. The ha_open call is not cachable for clone. @@ -2536,7 +2580,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) and should be able to use the original instance of the table. */ if (new_handler->ha_open(table, name, table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) + HA_OPEN_IGNORE_IF_LOCKED, mem_root)) goto err; return new_handler; @@ -2563,7 +2607,7 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows) engines that support that (e.g. InnoDB) may want to overwrite this method. The model counts in the time to read index entries from cache. */ - ulong len= table->key_info[index].key_length + ref_length; + size_t len= table->key_info[index].key_length + ref_length; if (index == table->s->primary_key && table->file->primary_key_is_clustered()) len= table->s->stored_rec_length; double keys_per_block= (stats.block_size/2.0/len+1); @@ -2614,7 +2658,8 @@ PSI_table_share *handler::ha_table_share_psi() const Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ int handler::ha_open(TABLE *table_arg, const char *name, int mode, - uint test_if_locked) + uint test_if_locked, MEM_ROOT *mem_root, + List<String> *partitions_to_open) { int error; DBUG_ENTER("handler::ha_open"); @@ -2629,6 +2674,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, DBUG_PRINT("info", ("old m_lock_type: %d F_UNLCK %d", m_lock_type, F_UNLCK)); DBUG_ASSERT(alloc_root_inited(&table->mem_root)); + set_partitions_to_open(partitions_to_open); + if ((error=open(name,mode,test_if_locked))) { if ((error == EACCES || error == EROFS) && mode == O_RDWR && @@ -2661,9 +2708,9 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table->db_stat|=HA_READ_ONLY; (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL - /* ref is already allocated for us if we're called from handler::clone() */ - if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root, - ALIGN_SIZE(ref_length)*2))) + /* Allocate ref in thd or on the table's mem_root */ + if (!(ref= (uchar*) alloc_root(mem_root ? mem_root : &table->mem_root, + ALIGN_SIZE(ref_length)*2))) { ha_close(); error=HA_ERR_OUT_OF_MEM; @@ -3012,45 +3059,6 @@ 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. @@ -3163,6 +3171,25 @@ int handler::update_auto_increment() DBUG_RETURN(0); } + // ALTER TABLE ... ADD COLUMN ... AUTO_INCREMENT + if (thd->lex->sql_command == SQLCOM_ALTER_TABLE) + { + if (table->versioned()) + { + Field *end= table->vers_end_field(); + DBUG_ASSERT(end); + bitmap_set_bit(table->read_set, end->field_index); + if (!end->is_max()) + { + if (!table->next_number_field->real_maybe_null()) + DBUG_RETURN(HA_ERR_UNSUPPORTED); + table->next_number_field->set_null(); + DBUG_RETURN(0); + } + } + table->next_number_field->set_notnull(); + } + if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum()) { /* next_insert_id is beyond what is reserved, so we reserve more. */ @@ -3262,7 +3289,7 @@ int handler::update_auto_increment() /* Store field without warning (Warning will be printed by insert) */ save_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_IGNORE; - tmp= table->next_number_field->store((longlong) nr, TRUE); + tmp= table->next_number_field->store((longlong)nr, TRUE); thd->count_cuted_fields= save_count_cuted_fields; if (unlikely(tmp)) // Out of range value in store @@ -3449,6 +3476,10 @@ void handler::ha_release_auto_increment() @param msg Error message template to which key value should be added. @param errflag Flags for my_error() call. + + @notes + The error message is from ER_DUP_ENTRY_WITH_KEY_NAME but to keep things compatibly + with old code, the error number is ER_DUP_ENTRY */ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag) @@ -3475,7 +3506,8 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag) str.length(max_length-4); str.append(STRING_WITH_LEN("...")); } - my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), key->name); + my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), + key->name.str); } } @@ -3647,13 +3679,19 @@ void handler::print_error(int error, myf errflag) textno=ER_UNSUPPORTED_EXTENSION; break; case HA_ERR_RECORD_FILE_FULL: - case HA_ERR_INDEX_FILE_FULL: { textno=ER_RECORD_FILE_FULL; /* Write the error message to error log */ errflag|= ME_NOREFRESH; break; } + case HA_ERR_INDEX_FILE_FULL: + { + textno=ER_INDEX_FILE_FULL; + /* Write the error message to error log */ + errflag|= ME_NOREFRESH; + break; + } case HA_ERR_LOCK_WAIT_TIMEOUT: textno=ER_LOCK_WAIT_TIMEOUT; break; @@ -3708,7 +3746,7 @@ void handler::print_error(int error, myf errflag) const char *ptr= "???"; uint key_nr= get_dup_key(error); if ((int) key_nr >= 0) - ptr= table->key_info[key_nr].name; + ptr= table->key_info[key_nr].name.str; my_error(ER_DROP_INDEX_FK, errflag, ptr); DBUG_VOID_RETURN; } @@ -3957,7 +3995,7 @@ static bool update_frm_version(TABLE *table) int4store(version, MYSQL_VERSION_ID); - if ((result= mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW))) + if ((result= (int)mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW))) goto err; table->s->mysql_version= MYSQL_VERSION_ID; @@ -4170,7 +4208,7 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) int handler::ha_bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found) + ha_rows *dup_key_found) { DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || m_lock_type == F_WRLCK); @@ -4377,6 +4415,9 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table, HA_CREATE_INFO *create_info= ha_alter_info->create_info; + if (altered_table->versioned(VERS_TIMESTAMP)) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations= Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH | Alter_inplace_info::ALTER_COLUMN_NAME | @@ -4736,7 +4777,7 @@ void handler::update_global_table_stats() } memcpy(table_stats->table, table->s->table_cache_key.str, table->s->table_cache_key.length); - table_stats->table_name_length= table->s->table_cache_key.length; + table_stats->table_name_length= (uint)table->s->table_cache_key.length; table_stats->engine_type= ht->db_type; /* No need to set variables to 0, as we use MY_ZEROFILL above */ @@ -4779,13 +4820,13 @@ void handler::update_global_index_stats() if (index_rows_read[index]) { INDEX_STATS* index_stats; - uint key_length; + size_t key_length; KEY *key_info = &table->key_info[index]; // Rows were read using this DBUG_ASSERT(key_info->cache_name); if (!key_info->cache_name) continue; - key_length= table->s->table_cache_key.length + key_info->name_length + 1; + key_length= table->s->table_cache_key.length + key_info->name.length + 1; mysql_mutex_lock(&LOCK_global_index_stats); // Gets the global index stats, creating one if necessary. if (!(index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats, @@ -4838,7 +4879,6 @@ int ha_create_table(THD *thd, const char *path, TABLE_SHARE share; bool temp_table __attribute__((unused)) = create_info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER); - DBUG_ENTER("ha_create_table"); init_tmp_table_share(thd, &share, db, 0, table_name, path); @@ -4866,7 +4906,8 @@ int ha_create_table(THD *thd, const char *path, share.m_psi= PSI_CALL_get_table_share(temp_table, &share); - if (open_table_from_share(thd, &share, "", 0, READ_ALL, 0, &table, true)) + if (open_table_from_share(thd, &share, &empty_clex_str, 0, READ_ALL, 0, + &table, true)) goto err; update_create_info_from_table(create_info, &table); @@ -4880,8 +4921,8 @@ int ha_create_table(THD *thd, const char *path, if (!thd->is_error()) my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error); table.file->print_error(error, MYF(ME_JUST_WARNING)); - PSI_CALL_drop_table_share(temp_table, share.db.str, share.db.length, - share.table_name.str, share.table_name.length); + PSI_CALL_drop_table_share(temp_table, share.db.str, (uint)share.db.length, + share.table_name.str, (uint)share.table_name.length); } (void) closefrm(&table); @@ -5177,7 +5218,7 @@ private: *hton will be NULL. */ -bool ha_table_exists(THD *thd, const char *db, const char *table_name, +bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, handlerton **hton, bool *is_sequence) { handlerton *dummy; @@ -5192,7 +5233,7 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, is_sequence= &dummy2; *is_sequence= 0; - TDC_element *element= tdc_lock_share(thd, db, table_name); + TDC_element *element= tdc_lock_share(thd, db->str, table_name->str); if (element && element != MY_ERRPTR) { if (hton) @@ -5204,8 +5245,8 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, char path[FN_REFLEN + 1]; size_t path_len = build_table_filename(path, sizeof(path) - 1, - db, table_name, "", 0); - st_discover_existence_args args= {path, path_len, db, table_name, 0, true}; + db->str, table_name->str, "", 0); + st_discover_existence_args args= {path, path_len, db->str, table_name->str, 0, true}; if (file_ext_exists(path, path_len, reg_ext)) { @@ -5248,14 +5289,12 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, { TABLE_LIST table; uint flags = GTS_TABLE | GTS_VIEW; - if (!hton) flags|= GTS_NOLOCK; Table_exists_error_handler no_such_table_handler; thd->push_internal_handler(&no_such_table_handler); - table.init_one_table(db, strlen(db), table_name, strlen(table_name), - table_name, TL_READ); + table.init_one_table(db, table_name, 0, TL_READ); TABLE_SHARE *share= tdc_acquire_share(thd, &table, flags); thd->pop_internal_handler(); @@ -5291,6 +5330,13 @@ static int cmp_table_names(LEX_CSTRING * const *a, LEX_CSTRING * const *b) (uchar*)((*b)->str), (*b)->length); } +#ifndef DBUG_OFF +static int cmp_table_names_desc(LEX_CSTRING * const *a, LEX_CSTRING * const *b) +{ + return -cmp_table_names(a, b); +} +#endif + } Discovered_table_list::Discovered_table_list(THD *thd_arg, @@ -5343,6 +5389,15 @@ void Discovered_table_list::sort() tables->sort(cmp_table_names); } + +#ifndef DBUG_OFF +void Discovered_table_list::sort_desc() +{ + tables->sort(cmp_table_names_desc); +} +#endif + + void Discovered_table_list::remove_duplicates() { LEX_CSTRING **src= tables->front(); @@ -5378,7 +5433,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin, if (ht->state == SHOW_OPTION_YES && ht->discover_table_names) { - uint old_elements= args->result->tables->elements(); + size_t old_elements= args->result->tables->elements(); if (ht->discover_table_names(ht, args->db, args->dirp, args->result)) return 1; @@ -5387,7 +5442,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin, a corresponding .frm file; but custom engine discover methods might */ if (ht->discover_table_names != hton_ext_based_table_discovery) - args->possible_duplicates+= args->result->tables->elements() - old_elements; + args->possible_duplicates+= (uint)(args->result->tables->elements() - old_elements); } return 0; @@ -5441,6 +5496,27 @@ int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp, } +/* +int handler::pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, + uint range_count, + bool sorted, HANDLER_BUFFER *buffer, + bool use_parallel) +{ + int result; + DBUG_ENTER("handler::pre_read_multi_range_first"); + result = pre_read_range_first(ranges->start_key.keypart_map ? + &ranges->start_key : 0, + ranges->end_key.keypart_map ? + &ranges->end_key : 0, + test(ranges->range_flag & EQ_RANGE), + sorted, + use_parallel); + DBUG_RETURN(result); +} +*/ + + /** Read first row between two ranges. Store ranges for future calls to read_range_next. @@ -5799,8 +5875,10 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) 1 Row needs to be logged */ -inline bool handler::check_table_binlog_row_based(bool binlog_row) +bool handler::check_table_binlog_row_based(bool binlog_row) { + if (table->versioned(VERS_TRX_ID)) + return false; if (unlikely((table->in_use->variables.sql_log_bin_off))) return 0; /* Called by partitioning engine */ if (unlikely((!check_table_binlog_row_based_done))) @@ -5839,7 +5917,7 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row) } #endif - return (table->s->cached_row_logging_check && + return (table->s->can_do_row_logging && thd->is_current_stmt_binlog_format_row() && /* Wsrep partially enables binary logging if it have not been @@ -5949,10 +6027,10 @@ static int write_locked_table_maps(THD *thd) static int check_wsrep_max_ws_rows(); -static int binlog_log_row_internal(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func) { bool error= 0; THD *const thd= table->in_use; @@ -5987,16 +6065,6 @@ static int binlog_log_row_internal(TABLE* table, return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } -int binlog_log_row(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) -{ - if (!table->file->check_table_binlog_row_based(1)) - return 0; - return binlog_log_row_internal(table, before_record, after_record, log_func); -} - int handler::ha_external_lock(THD *thd, int lock_type) { @@ -6097,6 +6165,7 @@ int handler::ha_reset() /* Reset information about pushed engine conditions */ cancel_pushed_idx_cond(); /* Reset information about pushed index conditions */ + clear_top_table_fields(); DBUG_RETURN(reset()); } @@ -6145,7 +6214,8 @@ int handler::ha_write_row(uchar *buf) if (likely(!error) && !row_already_logged) { rows_changed++; - error= binlog_log_row(table, 0, buf, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, 0, buf, log_func); } DEBUG_SYNC_C("ha_write_row_end"); DBUG_RETURN(error); @@ -6177,7 +6247,8 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data) if (likely(!error) && !row_already_logged) { rows_changed++; - error= binlog_log_row(table, old_data, new_data, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, old_data, new_data, log_func); } return error; } @@ -6232,12 +6303,66 @@ int handler::ha_delete_row(const uchar *buf) if (likely(!error)) { rows_changed++; - error= binlog_log_row(table, buf, 0, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, buf, 0, log_func); } return error; } +/** + Execute a direct update request. A direct update request updates all + qualified rows in a single operation, rather than one row at a time. + In a Spider cluster the direct update operation is pushed down to the + child levels of the cluster. + + Note that this can't be used in case of statment logging + + @param update_rows Number of updated rows. + + @retval 0 Success. + @retval != 0 Failure. +*/ + +int handler::ha_direct_update_rows(ha_rows *update_rows) +{ + int error; + + MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); + mark_trx_read_write(); + + error = direct_update_rows(update_rows); + MYSQL_UPDATE_ROW_DONE(error); + return error; +} + + +/** + Execute a direct delete request. A direct delete request deletes all + qualified rows in a single operation, rather than one row at a time. + In a Spider cluster the direct delete operation is pushed down to the + child levels of the cluster. + + @param delete_rows Number of deleted rows. + + @retval 0 Success. + @retval != 0 Failure. +*/ + +int handler::ha_direct_delete_rows(ha_rows *delete_rows) +{ + int error; + /* Ensure we are not using binlog row */ + DBUG_ASSERT(!table->in_use->is_current_stmt_binlog_format_row()); + + MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str); + mark_trx_read_write(); + + error = direct_delete_rows(delete_rows); + MYSQL_DELETE_ROW_DONE(error); + return error; +} + /** @brief use_hidden_primary_key() is called in case of an update/delete when @@ -6621,7 +6746,7 @@ bool HA_CREATE_INFO::check_conflicting_charset_declarations(CHARSET_INFO *cs) /* Remove all indexes for a given table from global index statistics */ static -int del_global_index_stats_for_table(THD *thd, uchar* cache_key, uint cache_key_length) +int del_global_index_stats_for_table(THD *thd, uchar* cache_key, size_t cache_key_length) { int res = 0; DBUG_ENTER("del_global_index_stats_for_table"); @@ -6662,7 +6787,7 @@ int del_global_table_stat(THD *thd, LEX_CSTRING *db, LEX_CSTRING *table) TABLE_STATS *table_stats; int res = 0; uchar *cache_key; - uint cache_key_length; + size_t cache_key_length; DBUG_ENTER("del_global_table_stat"); cache_key_length= db->length + 1 + table->length + 1; @@ -6699,7 +6824,7 @@ end: int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) { INDEX_STATS *index_stats; - uint key_length= table->s->table_cache_key.length + key_info->name_length + 1; + size_t key_length= table->s->table_cache_key.length + key_info->name.length + 1; int res = 0; DBUG_ENTER("del_global_index_stat"); mysql_mutex_lock(&LOCK_global_index_stats); @@ -6712,3 +6837,572 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) mysql_mutex_unlock(&LOCK_global_index_stats); DBUG_RETURN(res); } + +bool Vers_parse_info::is_start(const char *name) const +{ + DBUG_ASSERT(name); + return as_row.start && as_row.start == LString_i(name); +} +bool Vers_parse_info::is_end(const char *name) const +{ + DBUG_ASSERT(name); + return as_row.end && as_row.end == LString_i(name); +} +bool Vers_parse_info::is_start(const Create_field &f) const +{ + return f.flags & VERS_SYS_START_FLAG; +} +bool Vers_parse_info::is_end(const Create_field &f) const +{ + return f.flags & VERS_SYS_END_FLAG; +} + +static Create_field *vers_init_sys_field(THD *thd, const char *field_name, int flags, bool integer) +{ + Create_field *f= new (thd->mem_root) Create_field(); + if (!f) + return NULL; + + memset(f, 0, sizeof(*f)); + f->field_name.str= field_name; + f->field_name.length= strlen(field_name); + f->charset= system_charset_info; + f->flags= flags; + if (integer) + { + f->set_handler(&type_handler_longlong); + f->length= MY_INT64_NUM_DECIMAL_DIGITS - 1; + f->flags|= UNSIGNED_FLAG; + } + else + { + f->set_handler(&type_handler_timestamp2); + f->length= MAX_DATETIME_PRECISION; + } + f->invisible= DBUG_EVALUATE_IF("sysvers_show", VISIBLE, INVISIBLE_SYSTEM); + + if (f->check(thd)) + return NULL; + + return f; +} + +static bool vers_create_sys_field(THD *thd, const char *field_name, + Alter_info *alter_info, int flags) +{ + Create_field *f= vers_init_sys_field(thd, field_name, flags, false); + if (!f) + return true; + + alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; + alter_info->create_list.push_back(f); + + return false; +} + +const LString Vers_parse_info::default_start= "row_start"; +const LString Vers_parse_info::default_end= "row_end"; + +bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, int *added) +{ + // If user specified some of these he must specify the others too. Do nothing. + if (*this) + return false; + + alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; + + system_time= start_end_t(default_start, default_end); + as_row= system_time; + + if (vers_create_sys_field(thd, default_start, alter_info, VERS_SYS_START_FLAG) || + vers_create_sys_field(thd, default_end, alter_info, VERS_SYS_END_FLAG)) + { + return true; + } + if (added) + *added+= 2; + return false; +} + +bool Table_scope_and_contents_source_st::vers_native(THD *thd) const +{ + if (ha_check_storage_engine_flag(db_type, HTON_NATIVE_SYS_VERSIONING)) + return true; + +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *info= thd->work_part_info; + if (info && !(used_fields & HA_CREATE_USED_ENGINE)) + { + if (handlerton *hton= info->default_engine_type) + return ha_check_storage_engine_flag(hton, HTON_NATIVE_SYS_VERSIONING); + + List_iterator_fast<partition_element> it(info->partitions); + while (partition_element *partition_element= it++) + { + if (partition_element->find_engine_flag(HTON_NATIVE_SYS_VERSIONING)) + return true; + } + } +#endif + return false; +} + +bool Table_scope_and_contents_source_st::vers_fix_system_fields( + THD *thd, + Alter_info *alter_info, + const TABLE_LIST &create_table, + const TABLE_LIST *select_tables, + List<Item> *items, + bool *versioned_write) +{ + DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING)); + int vers_tables= 0; + + if (select_tables) + { + for (const TABLE_LIST *table= select_tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + vers_tables++; + } + } + + DBUG_EXECUTE_IF("sysvers_force", if (!tmp_table()) { + alter_info->flags|= Alter_info::ALTER_ADD_SYSTEM_VERSIONING; + options|= HA_VERSIONED_TABLE; }); + + // Possibly override default storage engine to match one used in source table. + if (vers_tables && alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING && + !(used_fields & HA_CREATE_USED_ENGINE)) + { + List_iterator_fast<Create_field> it(alter_info->create_list); + while (Create_field *f= it++) + { + if (vers_info.is_start(*f) || vers_info.is_end(*f)) + { + if (f->field) + { + db_type= f->field->orig_table->file->ht; + } + break; + } + } + } + + if (!vers_info.need_check(alter_info)) + return false; + + if (!vers_info.versioned_fields && vers_info.unversioned_fields && + !(alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING)) + { + // All is correct but this table is not versioned. + options&= ~HA_VERSIONED_TABLE; + return false; + } + + if (!(alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING) && vers_info) + { + my_error(ER_MISSING, MYF(0), create_table.table_name.str, + "WITH SYSTEM VERSIONING"); + return true; + } + + if (vers_tables) + { + DBUG_ASSERT(options & HA_VERSIONED_TABLE); + DBUG_ASSERT(versioned_write); + *versioned_write= true; + } + + List_iterator<Create_field> it(alter_info->create_list); + bool explicit_declared= vers_info.as_row.start || vers_info.as_row.end; + while (Create_field *f= it++) + { + if ((f->versioning == Column_definition::VERSIONING_NOT_SET && + !(alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING)) || + f->versioning == Column_definition::WITHOUT_VERSIONING) + { + f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; + } + + /* Assign selected implicit fields when no explicit fields */ + if (!vers_tables || explicit_declared) + continue; + + DBUG_ASSERT(versioned_write); + if (vers_info.is_start(*f) && + vers_info.default_start == f->field_name) + { + if (vers_info.as_row.start) + it.remove(); + else + { + vers_info.set_start(f->field_name); + *versioned_write= false; + } + continue; + } + if (vers_info.is_end(*f) && + vers_info.default_end == f->field_name) + { + if (vers_info.as_row.end) + it.remove(); + else + { + vers_info.set_end(f->field_name); + *versioned_write= false; + } + continue; + } + } // while (Create_field *f= it++) + + /* Assign selected system fields to explicit system fields if any */ + if (vers_tables) + { + it.rewind(); + while (Create_field *f= it++) + { + uint flags_left= VERS_SYSTEM_FIELD; + if (flags_left && (vers_info.is_start(*f) || vers_info.is_end(*f)) && !f->field) + { + uint sys_flag= f->flags & flags_left; + flags_left-= sys_flag; + List_iterator_fast<Item> it2(*items); + while (Item *item= it2++) + { + if (item->type() != Item::FIELD_ITEM) + continue; + Field *fld= static_cast<Item_field *>(item)->field; + DBUG_ASSERT(fld); + if ((fld->flags & sys_flag) && + LString_i(f->field_name) == fld->field_name) + { + f->field= fld; + *versioned_write= false; + } + } // while (item) + } // if (flags_left ... + } // while (Create_field *f= it++) + } // if (vers_tables) + + int added= 0; + if (vers_info.fix_implicit(thd, alter_info, &added)) + return true; + + DBUG_ASSERT(added >= 0); + if (vers_tables) + { + DBUG_ASSERT(items); + while (added--) + { + Item_default_value *item= new (thd->mem_root) + Item_default_value(thd, thd->lex->current_context()); + items->push_back(item, thd->mem_root); + } + } + + int plain_cols= 0; // columns don't have WITH or WITHOUT SYSTEM VERSIONING + int vers_cols= 0; // columns have WITH SYSTEM VERSIONING + it.rewind(); + while (const Create_field *f= it++) + { + if (vers_info.is_start(*f) || vers_info.is_end(*f)) + continue; + + if (f->versioning == Column_definition::VERSIONING_NOT_SET) + plain_cols++; + else if (f->versioning == Column_definition::WITH_VERSIONING) + vers_cols++; + } + + if (!thd->lex->tmp_table() && + // CREATE from SELECT (Create_fields are not yet added) + !select_tables && + vers_cols == 0 && + (plain_cols == 0 || !vers_info)) + { + my_error(ER_VERS_TABLE_MUST_HAVE_COLUMNS, MYF(0), + create_table.table_name.str); + return true; + } + + if (vers_info.check_with_conditions(create_table.table_name.str)) + return true; + + bool native= vers_native(thd); + if (vers_info.check_sys_fields(create_table.table_name.str, alter_info, native)) + return true; + + return false; +} + +bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info, TABLE *table) +{ + TABLE_SHARE *share= table->s; + const char *table_name= share->table_name.str; + + if (!need_check(alter_info) && !share->versioned) + return false; + + if (DBUG_EVALUATE_IF("sysvers_force", 0, share->tmp_table)) + { + my_error(ER_VERS_TEMPORARY, MYF(0)); + return true; + } + + if (alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING && + table->versioned()) + { + my_error(ER_VERS_ALREADY_VERSIONED, MYF(0), table_name); + return true; + } + + if (alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING) + { + if (!share->versioned) + { + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name); + return true; + } + + return false; + } + + { + List_iterator_fast<Create_field> it(alter_info->create_list); + while (Create_field *f= it++) + { + if (f->change.length && f->flags & VERS_SYSTEM_FIELD) + { + my_error(ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN, MYF(0)); + return true; + } + } + } + + if ((versioned_fields || unversioned_fields) && !share->versioned) + { + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name); + return true; + } + + if (add_period) + { + if (share->versioned) + { + my_error(ER_VERS_ALREADY_VERSIONED, MYF(0), table_name); + return true; + } + } + + if (share->versioned) + { + // copy info from existing table + create_info->options|= HA_VERSIONED_TABLE; + + DBUG_ASSERT(share->vers_start_field() && share->vers_end_field()); + LString start(share->vers_start_field()->field_name); + LString end(share->vers_end_field()->field_name); + DBUG_ASSERT(start.ptr() && end.ptr()); + + as_row= start_end_t(start, end); + system_time= as_row; + + if (alter_info->create_list.elements) + { + List_iterator_fast<Create_field> it(alter_info->create_list); + while (Create_field *f= it++) + { + if (f->versioning == Column_definition::WITHOUT_VERSIONING) + f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; + + if (f->change.str && (start == f->change || end == f->change)) + { + my_error(ER_VERS_ALTER_SYSTEM_FIELD, MYF(0), f->change.str); + return true; + } + } + } + + return false; + } + + if (fix_implicit(thd, alter_info)) + return true; + + if (alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING) + { + if (check_with_conditions(table_name)) + return true; + bool native= create_info->vers_native(thd); + if (check_sys_fields(table_name, alter_info, native)) + return true; + } + + return false; +} + +bool +Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_info, + TABLE_LIST &src_table, TABLE_LIST &table) +{ + List_iterator<Create_field> it(alter_info.create_list); + Create_field *f, *f_start=NULL, *f_end= NULL; + + DBUG_ASSERT(alter_info.create_list.elements > 2); + + if (create_info.tmp_table()) + { + int remove= 2; + while (remove && (f= it++)) + { + if (f->flags & VERS_SYSTEM_FIELD) + { + it.remove(); + remove--; + } + } + DBUG_ASSERT(remove == 0); + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_ERROR, + "System versioning is stripped from temporary `%s.%s`", + table.db.str, table.table_name.str); + return false; + } + + while ((f= it++)) + { + if (f->flags & VERS_SYS_START_FLAG) + { + f_start= f; + if (f_end) + break; + } + else if (f->flags & VERS_SYS_END_FLAG) + { + f_end= f; + if (f_start) + break; + } + } + + if (!f_start || !f_end) + { + my_error(ER_MISSING, MYF(0), src_table.table_name.str, + f_start ? "AS ROW END" : "AS ROW START"); + return true; + } + + as_row= start_end_t(f_start->field_name, f_end->field_name); + system_time= as_row; + + create_info.options|= HA_VERSIONED_TABLE; + return false; +} + +bool Vers_parse_info::need_check(const Alter_info *alter_info) const +{ + return versioned_fields || unversioned_fields || add_period || + alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING || + alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING || *this; +} + +bool Vers_parse_info::check_with_conditions(const char *table_name) const +{ + if (!as_row.start || !as_row.end) + { + my_error(ER_MISSING, MYF(0), table_name, + as_row.start ? "AS ROW END" : "AS ROW START"); + return true; + } + + if (!system_time.start || !system_time.end) + { + my_error(ER_MISSING, MYF(0), table_name, "PERIOD FOR SYSTEM_TIME"); + return true; + } + + if (as_row.start != system_time.start || as_row.end != system_time.end) + { + my_error(ER_VERS_PERIOD_COLUMNS, MYF(0), as_row.start.str, as_row.end.str); + return true; + } + + return false; +} + +bool Vers_parse_info::check_sys_fields(const char *table_name, + Alter_info *alter_info, + bool native) const +{ + List_iterator<Create_field> it(alter_info->create_list); + vers_sys_type_t found= VERS_UNDEFINED; + uint found_flag= 0; + while (Create_field *f= it++) + { + vers_sys_type_t check_unit= VERS_UNDEFINED; + uint sys_flag= f->flags & VERS_SYSTEM_FIELD; + + if (!sys_flag) + continue; + + if (sys_flag & found_flag) + { + my_error(ER_VERS_DUPLICATE_ROW_START_END, MYF(0), + found_flag & VERS_SYS_START_FLAG ? "START" : "END", + f->field_name.str); + return true; + } + + sys_flag|= found_flag; + + if ((f->type_handler() == &type_handler_datetime2 || + f->type_handler() == &type_handler_timestamp2) && + f->length == MAX_DATETIME_FULL_WIDTH) + { + check_unit= VERS_TIMESTAMP; + } + else if (native + && f->type_handler() == &type_handler_longlong + && (f->flags & UNSIGNED_FLAG) + && f->length == (MY_INT64_NUM_DECIMAL_DIGITS - 1)) + { + check_unit= VERS_TRX_ID; + } + else + { + if (!found) + found= VERS_TIMESTAMP; + goto error; + } + + if (check_unit) + { + if (found) + { + if (found == check_unit) + { + if (found == VERS_TRX_ID && !TR_table::use_transaction_registry) + { + my_error(ER_VERS_TRT_IS_DISABLED, MYF(0)); + return true; + } + return false; + } + error: + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str, + found == VERS_TIMESTAMP ? + "TIMESTAMP(6)" : + "BIGINT(20) UNSIGNED", + table_name); + return true; + } + found= check_unit; + } + } + + my_error(ER_MISSING, MYF(0), table_name, found_flag & VERS_SYS_START_FLAG ? + "ROW END" : found_flag ? "ROW START" : "ROW START/END"); + return true; +} diff --git a/sql/handler.h b/sql/handler.h index 3b5ce46c878..a96e98c2f84 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -25,7 +25,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> /* For handlers */ #include "sql_const.h" #include "sql_basic_types.h" #include "mysqld.h" /* server_id */ @@ -35,6 +34,7 @@ #include "structs.h" /* SHOW_COMP_OPTION */ #include "sql_array.h" /* Dynamic_array<> */ #include "mdl.h" +#include "vers_string.h" #include "sql_analyze_stmt.h" // for Exec_time_tracker @@ -162,6 +162,7 @@ enum enum_alter_inplace_result { */ #define HA_BINLOG_ROW_CAPABLE (1ULL << 34) #define HA_BINLOG_STMT_CAPABLE (1ULL << 35) + /* When a multiple key conflict happens in a REPLACE command mysql expects the conflicts to be reported in the ascending order of @@ -287,6 +288,17 @@ enum enum_alter_inplace_result { */ #define HA_BINLOG_FLAGS (HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE) +/* The following are used by Spider */ +#define HA_CAN_FORCE_BULK_UPDATE (1ULL << 50) +#define HA_CAN_FORCE_BULK_DELETE (1ULL << 51) +#define HA_CAN_DIRECT_UPDATE_AND_DELETE (1ULL << 52) + +/* The following is for partition handler */ +#define HA_CAN_MULTISTEP_MERGE (1LL << 53) + +/* calling cmp_ref() on the engine is expensive */ +#define HA_CMP_REF_IS_EXPENSIVE (1ULL << 54) + /* 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 */ @@ -401,6 +413,8 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_TMP_TABLE 1U #define HA_CREATE_TMP_ALTER 8U #define HA_LEX_CREATE_SEQUENCE 16U +#define HA_VERSIONED_TABLE 32U +#define HA_VTMD 64U #define HA_MAX_REC_LENGTH 65535 @@ -436,6 +450,12 @@ static const uint MYSQL_START_TRANS_OPT_READ_WRITE = 4; #define HA_CHECK_DUP (HA_CHECK_DUP_KEY + HA_CHECK_DUP_UNIQUE) #define HA_CHECK_ALL (~0U) +/* Options for info_push() */ +#define INFO_KIND_UPDATE_FIELDS 101 +#define INFO_KIND_UPDATE_VALUES 102 +#define INFO_KIND_FORCE_LIMIT_BEGIN 103 +#define INFO_KIND_FORCE_LIMIT_END 104 + enum legacy_db_type { /* note these numerical values are fixed and can *not* be changed */ @@ -1229,7 +1249,7 @@ struct handlerton bool (*flush_logs)(handlerton *hton); bool (*show_status)(handlerton *hton, THD *thd, stat_print_fn *print, enum ha_stat_type stat); uint (*partition_flags)(); - uint (*alter_table_flags)(uint flags); + ulonglong (*alter_table_flags)(ulonglong flags); int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info); int (*fill_is_table)(handlerton *hton, THD *thd, TABLE_LIST *tables, class Item *cond, @@ -1392,6 +1412,16 @@ struct handlerton */ int (*discover_table_structure)(handlerton *hton, THD* thd, TABLE_SHARE *share, HA_CREATE_INFO *info); + + /* + System Versioning + */ + /** Determine if system-versioned data was modified by the transaction. + @param[in,out] thd current session + @param[out] trx_id transaction start ID + @return transaction commit ID + @retval 0 if no system-versioned data was affected by the transaction */ + ulonglong (*prepare_commit_versioned)(THD *thd, ulonglong *trx_id); }; @@ -1439,10 +1469,15 @@ handlerton *ha_default_tmp_handlerton(THD *thd); */ #define HTON_NO_BINLOG_ROW_OPT (1 << 9) #define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys +#define HTON_NATIVE_SYS_VERSIONING (1 << 11) //Engine supports System Versioning // MySQL compatibility. Unused. #define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported. +#define HTON_CAN_MERGE (1 <<11) //Merge type table +// Engine needs to access the main connect string in partitions +#define HTON_CAN_READ_CONNECT_STRING_IN_PARTITION (1 <<12) + class Ha_trx_info; struct THD_TRANS @@ -1520,6 +1555,7 @@ struct THD_TRANS bool trans_did_wait() const { return (m_unsafe_rollback_flags & DID_WAIT) != 0; } + bool is_trx_read_write() const; void mark_trans_did_ddl() { m_unsafe_rollback_flags|= DID_DDL; } bool trans_did_ddl() const { return (m_unsafe_rollback_flags & DID_DDL) != 0; @@ -1624,6 +1660,16 @@ private: }; +inline bool THD_TRANS::is_trx_read_write() const +{ + Ha_trx_info *ha_info; + for (ha_info= ha_list; ha_info; ha_info= ha_info->next()) + if (ha_info->is_trx_read_write()) + return TRUE; + return FALSE; +} + + enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, ISO_REPEATABLE_READ, ISO_SERIALIZABLE}; @@ -1674,6 +1720,79 @@ struct Schema_specification_st } }; +class Create_field; + +struct Vers_parse_info +{ + Vers_parse_info() : + versioned_fields(false), + unversioned_fields(false), + add_period(false) + {} + + struct start_end_t + { + start_end_t() + {} + start_end_t(LEX_CSTRING _start, LEX_CSTRING _end) : + start(_start), + end(_end) {} + LString_i start; + LString_i end; + }; + + start_end_t system_time; + start_end_t as_row; + + void set_system_time(LString start, LString end) + { + system_time.start= start; + system_time.end= end; + } + +protected: + friend struct Table_scope_and_contents_source_st; + void set_start(const LEX_CSTRING field_name) + { + as_row.start= field_name; + system_time.start= field_name; + } + void set_end(const LEX_CSTRING field_name) + { + as_row.end= field_name; + system_time.end= field_name; + } + bool is_start(const char *name) const; + bool is_end(const char *name) const; + bool is_start(const Create_field &f) const; + bool is_end(const Create_field &f) const; + bool fix_implicit(THD *thd, Alter_info *alter_info, int *added= NULL); + operator bool() const + { + return as_row.start || as_row.end || system_time.start || system_time.end; + } + bool need_check(const Alter_info *alter_info) const; + bool check_with_conditions(const char *table_name) const; + bool check_sys_fields(const char *table_name, Alter_info *alter_info, + bool native) const; + +public: + static const LString default_start; + static const LString default_end; + + bool fix_alter_info(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info, TABLE *table); + bool fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_info, + TABLE_LIST &src_table, TABLE_LIST &table); + + /** + At least one field was specified 'WITH/WITHOUT SYSTEM VERSIONING'. + Useful for error handling. + */ + bool versioned_fields : 1; + bool unversioned_fields : 1; + bool add_period : 1; // ADD PERIOD FOR SYSTEM_TIME was specified +}; /** A helper struct for table DDL statements, e.g.: @@ -1695,9 +1814,9 @@ struct Table_scope_and_contents_source_st LEX_CUSTRING tabledef_version; LEX_CSTRING connect_string; LEX_CSTRING comment; + LEX_CSTRING alias; const char *password, *tablespace; const char *data_file_name, *index_file_name; - const char *alias; ulonglong max_rows,min_rows; ulonglong auto_increment_value; ulong table_options; ///< HA_OPTION_ values @@ -1749,6 +1868,16 @@ 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, + const TABLE_LIST *select_table= NULL, + List<Item> *items= NULL, + bool *versioned_write= NULL); + + bool vers_native(THD *thd) const; + void init() { bzero(this, sizeof(*this)); @@ -1759,6 +1888,16 @@ struct Table_scope_and_contents_source_st db_type= tmp_table() ? ha_default_tmp_handlerton(thd) : ha_default_handlerton(thd); } + + bool versioned() const + { + return options & HA_VERSIONED_TABLE; + } + + bool vtmd() const + { + return options & HA_VTMD; + } }; @@ -2032,6 +2171,14 @@ public: static const HA_ALTER_FLAGS ALTER_DROP_CHECK_CONSTRAINT= 1ULL << 40; + static const HA_ALTER_FLAGS ALTER_DROP_HISTORICAL = 1ULL << 41; + + static const HA_ALTER_FLAGS ALTER_COLUMN_UNVERSIONED = 1ULL << 42; + + static const HA_ALTER_FLAGS ALTER_ADD_SYSTEM_VERSIONING= 1ULL << 43; + + static const HA_ALTER_FLAGS ALTER_DROP_SYSTEM_VERSIONING= 1ULL << 44; + /** Create options (like MAX_ROWS) for the new version of table. @@ -2716,7 +2863,8 @@ public: /** Length of ref (1-8 or the clustered key length) */ uint ref_length; FT_INFO *ft_handler; - enum {NONE=0, INDEX, RND} inited; + enum init_stat { NONE=0, INDEX, RND }; + init_stat inited, pre_inited; const COND *pushed_cond; /** @@ -2782,6 +2930,11 @@ public: virtual void unbind_psi(); virtual void rebind_psi(); + bool set_top_table_fields; + struct TABLE *top_table; + Field **top_table_field; + uint top_table_fields; + private: /** The lock type set by when calling::ha_external_lock(). This is @@ -2810,13 +2963,15 @@ public: key_used_on_scan(MAX_KEY), active_index(MAX_KEY), keyread(MAX_KEY), ref_length(sizeof(my_off_t)), - ft_handler(0), inited(NONE), + ft_handler(0), inited(NONE), pre_inited(NONE), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), tracker(NULL), pushed_idx_cond(NULL), pushed_idx_cond_keyno(MAX_KEY), auto_inc_intervals_count(0), - m_psi(NULL), m_lock_type(F_UNLCK), ha_share(NULL) + m_psi(NULL), set_top_table_fields(FALSE), top_table(0), + top_table_field(0), top_table_fields(0), + m_lock_type(F_UNLCK), ha_share(NULL) { DBUG_PRINT("info", ("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d", @@ -2836,7 +2991,8 @@ public: } /* ha_ methods: pubilc wrappers for private virtual API */ - int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked); + int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked, + MEM_ROOT *mem_root= 0, List<String> *partitions_to_open=NULL); int ha_index_init(uint idx, bool sorted) { DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;); @@ -2948,7 +3104,7 @@ public: DBUG_RETURN(ret); } int ha_bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found); + ha_rows *dup_key_found); int ha_delete_all_rows(); int ha_truncate(); int ha_reset_auto_increment(ulonglong value); @@ -3089,6 +3245,7 @@ public: Number of rows in table. It will only be called if (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0 */ + virtual int pre_records() { return 0; } virtual ha_rows records() { return stats.records; } /** Return upper bound of current number of records in the table @@ -3145,7 +3302,7 @@ public: @retval 0 Success @retval >0 Error code */ - virtual int exec_bulk_update(uint *dup_key_found) + virtual int exec_bulk_update(ha_rows *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; @@ -3154,7 +3311,7 @@ public: Perform any needed clean-up, no outstanding updates are there at the moment. */ - virtual void end_bulk_update() { return; } + virtual int end_bulk_update() { return 0; } /** Execute all outstanding deletes and close down the bulk delete. @@ -3166,6 +3323,79 @@ public: DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } + virtual int pre_index_read_map(const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag, + bool use_parallel) + { return 0; } + virtual int pre_index_first(bool use_parallel) + { return 0; } + virtual int pre_index_last(bool use_parallel) + { return 0; } + virtual int pre_index_read_last_map(const uchar *key, + key_part_map keypart_map, + bool use_parallel) + { return 0; } +/* + virtual int pre_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, + uint range_count, + bool sorted, HANDLER_BUFFER *buffer, + bool use_parallel); +*/ + virtual int pre_multi_range_read_next(bool use_parallel) + { return 0; } + virtual int pre_read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted, + bool use_parallel) + { return 0; } + virtual int pre_ft_read(bool use_parallel) + { return 0; } + virtual int pre_rnd_next(bool use_parallel) + { return 0; } + int ha_pre_rnd_init(bool scan) + { + int result; + DBUG_ENTER("ha_pre_rnd_init"); + DBUG_ASSERT(pre_inited==NONE || (pre_inited==RND && scan)); + pre_inited= (result= pre_rnd_init(scan)) ? NONE: RND; + DBUG_RETURN(result); + } + int ha_pre_rnd_end() + { + DBUG_ENTER("ha_pre_rnd_end"); + DBUG_ASSERT(pre_inited==RND); + pre_inited=NONE; + DBUG_RETURN(pre_rnd_end()); + } + virtual int pre_rnd_init(bool scan) { return 0; } + virtual int pre_rnd_end() { return 0; } + virtual int pre_index_init(uint idx, bool sorted) { return 0; } + virtual int pre_index_end() { return 0; } + int ha_pre_index_init(uint idx, bool sorted) + { + int result; + DBUG_ENTER("ha_pre_index_init"); + DBUG_ASSERT(pre_inited==NONE); + if (!(result= pre_index_init(idx, sorted))) + pre_inited=INDEX; + DBUG_RETURN(result); + } + int ha_pre_index_end() + { + DBUG_ENTER("ha_pre_index_end"); + DBUG_ASSERT(pre_inited==INDEX); + pre_inited=NONE; + DBUG_RETURN(pre_index_end()); + } + int ha_pre_index_or_rnd_end() + { + return (pre_inited == INDEX ? + ha_pre_index_end() : + pre_inited == RND ? ha_pre_rnd_end() : 0 ); + } + /** @brief Positions an index cursor to the index specified in the @@ -3198,6 +3428,18 @@ protected: virtual int index_last(uchar * buf) { return HA_ERR_WRONG_COMMAND; } virtual int index_next_same(uchar *buf, const uchar *key, uint keylen); + /** + @brief + The following functions works like index_read, but it find the last + row with the current key value or prefix. + @returns @see index_read_map(). + */ + virtual int index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map) + { + uint key_len= calculate_key_len(table, active_index, key, keypart_map); + return index_read_last(buf, key, key_len); + } virtual int close(void)=0; inline void update_rows_read() { @@ -3274,10 +3516,12 @@ public: int compare_key(key_range *range); int compare_key2(key_range *range) const; virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } - void ft_end() { ft_handler=NULL; } + virtual int pre_ft_init() { return HA_ERR_WRONG_COMMAND; } + virtual void ft_end() {} + virtual int pre_ft_end() { return 0; } virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } -private: +public: virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(uchar *buf)=0; virtual int rnd_pos(uchar * buf, uchar *pos)=0; @@ -3297,6 +3541,7 @@ public: /* Same as above, but with statistics */ inline int ha_ft_read(uchar *buf); + inline void ha_ft_end() { ft_end(); ft_handler=NULL; } int ha_rnd_next(uchar *buf); int ha_rnd_pos(uchar *buf, uchar *pos); inline int ha_rnd_pos_by_record(uchar *buf); @@ -3326,6 +3571,9 @@ public: virtual int info(uint)=0; // see my_base.h for full description virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id); + virtual void set_partitions_to_open(List<String> *partition_names) {} + virtual int change_partitions_to_open(List<String> *partition_names) + { return 0; } virtual int extra(enum ha_extra_function operation) { return 0; } virtual int extra_opt(enum ha_extra_function operation, ulong cache_size) @@ -3354,6 +3602,8 @@ public: virtual void try_semi_consistent_read(bool) {} virtual void unlock_row() {} virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;} + virtual bool need_info_for_auto_inc() { return 0; } + virtual bool can_use_for_auto_inc_init() { return 1; } virtual void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, @@ -3461,6 +3711,7 @@ public: return 0; } virtual void set_part_info(partition_info *part_info) {return;} + virtual void return_record_by_parent() { return; } virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0; @@ -3665,6 +3916,41 @@ public: virtual void cond_pop() { return; }; /** + Push metadata for the current operation down to the table handler. + */ + virtual int info_push(uint info_type, void *info) { return 0; }; + + /** + This function is used to get correlating of a parent (table/column) + and children (table/column). When conditions are pushed down to child + table (like child of myisam_merge), child table needs to know about + which table/column is my parent for understanding conditions. + */ + virtual int set_top_table_and_fields(TABLE *top_table, + Field **top_table_field, + uint top_table_fields) + { + if (!set_top_table_fields) + { + set_top_table_fields= TRUE; + this->top_table= top_table; + this->top_table_field= top_table_field; + this->top_table_fields= top_table_fields; + } + return 0; + } + virtual void clear_top_table_fields() + { + if (set_top_table_fields) + { + set_top_table_fields= FALSE; + top_table= NULL; + top_table_field= NULL; + top_table_fields= 0; + } + } + + /** Push down an index condition to the handler. The server will use this method to push down a condition it wants @@ -3697,6 +3983,10 @@ public: pushed_idx_cond_keyno= MAX_KEY; in_range_check_pushed_down= false; } + + /* Needed for partition / spider */ + virtual TABLE_LIST *get_next_global_for_child() { return NULL; } + /** Part of old, deprecated in-place ALTER API. */ @@ -3971,7 +4261,7 @@ public: but we don't have a primary key */ virtual void use_hidden_primary_key(); - virtual uint alter_table_flags(uint flags) + virtual ulonglong alter_table_flags(ulonglong flags) { if (ht->alter_table_flags) return ht->alter_table_flags(flags); @@ -4008,8 +4298,8 @@ protected: virtual int delete_table(const char *name); public: - inline bool check_table_binlog_row_based(bool binlog_row); -private: + bool check_table_binlog_row_based(bool binlog_row); + /* Cache result to avoid extra calls */ inline void mark_trx_read_write() { @@ -4019,6 +4309,8 @@ private: mark_trx_read_write_internal(); } } + +private: void mark_trx_read_write_internal(); bool check_table_binlog_row_based_internal(bool binlog_row); @@ -4080,6 +4372,49 @@ private: { return HA_ERR_WRONG_COMMAND; } + + /* Perform initialization for a direct update request */ +public: + int ha_direct_update_rows(ha_rows *update_rows); + virtual int direct_update_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } +private: + virtual int pre_direct_update_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } + virtual int direct_update_rows(ha_rows *update_rows __attribute__((unused))) + { + return HA_ERR_WRONG_COMMAND; + } + virtual int pre_direct_update_rows() + { + return HA_ERR_WRONG_COMMAND; + } + + /* Perform initialization for a direct delete request */ +public: + int ha_direct_delete_rows(ha_rows *delete_rows); + virtual int direct_delete_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } +private: + virtual int pre_direct_delete_rows_init() + { + return HA_ERR_WRONG_COMMAND; + } + virtual int direct_delete_rows(ha_rows *delete_rows __attribute__((unused))) + { + return HA_ERR_WRONG_COMMAND; + } + virtual int pre_direct_delete_rows() + { + return HA_ERR_WRONG_COMMAND; + } + /** Reset state of file to after 'open'. This function is called after every statement for all tables used @@ -4137,6 +4472,11 @@ protected: virtual int index_read(uchar * buf, const uchar * key, uint key_len, enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } + virtual int index_read_last(uchar * buf, const uchar * key, uint key_len) + { + my_errno= HA_ERR_WRONG_COMMAND; + return HA_ERR_WRONG_COMMAND; + } friend class ha_partition; friend class ha_sequence; public: @@ -4154,7 +4494,7 @@ public: @retval 1 Bulk delete not used, normal operation used */ virtual int bulk_update_row(const uchar *old_data, const uchar *new_data, - uint *dup_key_found) + ha_rows *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; @@ -4260,6 +4600,15 @@ public: */ virtual int find_unique_row(uchar *record, uint unique_ref) { return -1; /*unsupported */} + + bool native_versioned() const + { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_NATIVE_SYS_VERSIONING; } + virtual ha_rows part_records(void *_part_elem) + { DBUG_ASSERT(0); return false; } + virtual handler* part_handler(uint32 part_id) + { DBUG_ASSERT(0); return NULL; } + virtual void update_partition(uint part_id) + {} protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); @@ -4337,7 +4686,7 @@ int ha_create_table(THD *thd, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, LEX_CUSTRING *frm); int ha_delete_table(THD *thd, handlerton *db_type, const char *path, - const char *db, const char *alias, bool generate_warning); + const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning); /* statistics and info */ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat); @@ -4363,12 +4712,19 @@ public: void sort(); void remove_duplicates(); // assumes that the list is sorted +#ifndef DBUG_OFF + /* + Used to find unstable mtr tests querying + INFORMATION_SCHEMA.TABLES without ORDER BY. + */ + void sort_desc(); +#endif }; int ha_discover_table(THD *thd, TABLE_SHARE *share); int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp, Discovered_table_list *result, bool reusable); -bool ha_table_exists(THD *thd, const char *db, const char *table_name, +bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, handlerton **hton= 0, bool *is_sequence= 0); #endif @@ -4419,9 +4775,9 @@ const char *get_canonical_filename(handler *file, const char *path, bool mysql_xa_recover(THD *thd); void commit_checkpoint_notify_ha(handlerton *hton, void *cookie); -inline const char *table_case_name(HA_CREATE_INFO *info, const char *name) +inline const LEX_CSTRING *table_case_name(HA_CREATE_INFO *info, const LEX_CSTRING *name) { - return ((lower_case_table_names == 2 && info->alias) ? info->alias : name); + return ((lower_case_table_names == 2 && info->alias.str) ? &info->alias : name); } typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc index fc89bb83a9d..641d1a20f73 100644 --- a/sql/hash_filo.cc +++ b/sql/hash_filo.cc @@ -23,7 +23,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "hash_filo.h" diff --git a/sql/hostname.cc b/sql/hostname.cc index e8d6780d095..2d39a8bb03d 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -24,7 +24,7 @@ Hostnames are checked with reverse name lookup and checked that they doesn't resemble an IP address. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" // SPECIAL_NO_HOST_CACHE #include "hostname.h" @@ -180,7 +180,7 @@ void hostname_cache_unlock() static void prepare_hostname_cache_key(const char *ip_string, char *ip_key) { - int ip_string_length= strlen(ip_string); + size_t ip_string_length= strlen(ip_string); DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE); memset(ip_key, 0, HOST_ENTRY_KEY_SIZE); @@ -229,12 +229,12 @@ static void add_hostname_impl(const char *ip_key, const char *hostname, { if (hostname != NULL) { - uint len= strlen(hostname); + size_t len= strlen(hostname); if (len > sizeof(entry->m_hostname) - 1) len= sizeof(entry->m_hostname) - 1; memcpy(entry->m_hostname, hostname, len); entry->m_hostname[len]= '\0'; - entry->m_hostname_length= len; + entry->m_hostname_length= (uint)len; DBUG_PRINT("info", ("Adding/Updating '%s' -> '%s' (validated) to the hostname cache...'", @@ -702,12 +702,12 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, /* Simulating ipv4 192.0.2.126 */ debug_addr= & debug_sock_addr[0]; debug_addr->sin_family= AF_INET; - debug_addr->sin_addr.s_addr= inet_addr("192.0.2.126"); + inet_pton(AF_INET,"192.0.2.126", &debug_addr->sin_addr); /* Simulating ipv4 192.0.2.127 */ debug_addr= & debug_sock_addr[1]; debug_addr->sin_family= AF_INET; - debug_addr->sin_addr.s_addr= inet_addr("192.0.2.127"); + inet_pton(AF_INET,"192.0.2.127", &debug_addr->sin_addr); debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0]; debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in); @@ -734,12 +734,12 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, /* Simulating ipv4 192.0.2.5 */ debug_addr= & debug_sock_addr[0]; debug_addr->sin_family= AF_INET; - debug_addr->sin_addr.s_addr= inet_addr("192.0.2.5"); + inet_pton(AF_INET,"192.0.2.5", &debug_addr->sin_addr); /* Simulating ipv4 192.0.2.4 */ debug_addr= & debug_sock_addr[1]; debug_addr->sin_family= AF_INET; - debug_addr->sin_addr.s_addr= inet_addr("192.0.2.4"); + inet_pton(AF_INET,"192.0.2.4", &debug_addr->sin_addr); debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0]; debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in); @@ -774,44 +774,13 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, debug_addr= & debug_sock_addr[0]; debug_addr->sin6_family= AF_INET6; ip6= & debug_addr->sin6_addr; - /* inet_pton not available on Windows XP. */ - ip6->s6_addr[ 0] = 0x20; - ip6->s6_addr[ 1] = 0x01; - ip6->s6_addr[ 2] = 0x0d; - ip6->s6_addr[ 3] = 0xb8; - ip6->s6_addr[ 4] = 0x00; - ip6->s6_addr[ 5] = 0x00; - ip6->s6_addr[ 6] = 0x00; - ip6->s6_addr[ 7] = 0x00; - ip6->s6_addr[ 8] = 0x00; - ip6->s6_addr[ 9] = 0x00; - ip6->s6_addr[10] = 0x00; - ip6->s6_addr[11] = 0x00; - ip6->s6_addr[12] = 0x00; - ip6->s6_addr[13] = 0x06; - ip6->s6_addr[14] = 0x00; - ip6->s6_addr[15] = 0x7e; + inet_pton(AF_INET6,"2001:DB8::6:7E",ip6); /* Simulating ipv6 2001:DB8::6:7F */ debug_addr= & debug_sock_addr[1]; debug_addr->sin6_family= AF_INET6; ip6= & debug_addr->sin6_addr; - ip6->s6_addr[ 0] = 0x20; - ip6->s6_addr[ 1] = 0x01; - ip6->s6_addr[ 2] = 0x0d; - ip6->s6_addr[ 3] = 0xb8; - ip6->s6_addr[ 4] = 0x00; - ip6->s6_addr[ 5] = 0x00; - ip6->s6_addr[ 6] = 0x00; - ip6->s6_addr[ 7] = 0x00; - ip6->s6_addr[ 8] = 0x00; - ip6->s6_addr[ 9] = 0x00; - ip6->s6_addr[10] = 0x00; - ip6->s6_addr[11] = 0x00; - ip6->s6_addr[12] = 0x00; - ip6->s6_addr[13] = 0x06; - ip6->s6_addr[14] = 0x00; - ip6->s6_addr[15] = 0x7f; + inet_pton(AF_INET6,"2001:DB8::6:7F",ip6); debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0]; debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6); @@ -946,7 +915,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, { err_status= - vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen, + vio_get_normalized_ip_string(addr_info->ai_addr, (int)addr_info->ai_addrlen, ip_buffer, sizeof (ip_buffer)); DBUG_ASSERT(!err_status); } @@ -990,7 +959,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, char ip_buffer[HOST_ENTRY_KEY_SIZE]; err_status= - vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen, + vio_get_normalized_ip_string(addr_info->ai_addr, (int)addr_info->ai_addrlen, ip_buffer, sizeof (ip_buffer)); DBUG_ASSERT(!err_status); diff --git a/sql/hostname.h b/sql/hostname.h index d6137b7c260..3e1669b29ed 100644 --- a/sql/hostname.h +++ b/sql/hostname.h @@ -16,7 +16,6 @@ #ifndef HOSTNAME_INCLUDED #define HOSTNAME_INCLUDED -#include "my_global.h" /* uint */ #include "my_net.h" #include "hash_filo.h" diff --git a/sql/init.cc b/sql/init.cc index 8001e60b65e..0d00e3cf846 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -21,10 +21,9 @@ Init and dummy functions for interface with unireg */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "init.h" -#include "my_sys.h" #include "mysqld.h" // abort_loop, ... #include "my_time.h" // my_init_time #include "unireg.h" // SPECIAL_SAME_DB_NAME diff --git a/sql/init.h b/sql/init.h index af2621e5e70..e8dec0c1e2e 100644 --- a/sql/init.h +++ b/sql/init.h @@ -16,8 +16,6 @@ #ifndef INIT_INCLUDED #define INIT_INCLUDED -#include "my_global.h" /* ulong */ - void unireg_init(ulong options); ATTRIBUTE_NORETURN void unireg_end(void); diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h index 27aa9ac8645..7fbaa7cfc2f 100644 --- a/sql/innodb_priv.h +++ b/sql/innodb_priv.h @@ -22,11 +22,11 @@ class THD; -int get_quote_char_for_identifier(THD *thd, const char *name, uint length); +int get_quote_char_for_identifier(THD *thd, const char *name, size_t length); bool schema_table_store_record(THD *thd, TABLE *table); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); -uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length, - CHARSET_INFO *to_cs, char *to, uint to_length, +uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length, + CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors); void sql_print_error(const char *format, ...); diff --git a/sql/item.cc b/sql/item.cc index e05ba7d6389..34db1a80dcd 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -19,7 +19,7 @@ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include <mysql.h> #include <m_ctype.h> @@ -78,6 +78,12 @@ inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select) select->nest_level - 1); } + +MEM_ROOT *get_thd_memroot(THD *thd) +{ + return thd->mem_root; +} + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -610,7 +616,7 @@ void Item::print_item_w_name(String *str, enum_query_type query_type) DBUG_ASSERT(name.length == strlen(name.str)); THD *thd= current_thd; str->append(STRING_WITH_LEN(" AS ")); - append_identifier(thd, str, name.str, name.length); + append_identifier(thd, str, &name); } } @@ -756,10 +762,10 @@ Item_ident::Item_ident(THD *thd, Name_resolution_context *context_arg, Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, const LEX_CSTRING *field_name_arg) :Item_result_field(thd), orig_db_name(NullS), - orig_table_name(view_arg->table_name), + orig_table_name(view_arg->table_name.str), orig_field_name(*field_name_arg), context(&view_arg->view->select_lex.context), - db_name(NullS), table_name(view_arg->alias), + db_name(NullS), table_name(view_arg->alias.str), field_name(*field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), cached_table(NULL), depended_from(NULL), can_be_depended(TRUE) @@ -1194,7 +1200,7 @@ bool Item::check_type_scalar(const char *opname) const } -void Item::set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs) +void Item::set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs) { if (!length) { @@ -1288,8 +1294,7 @@ bool Item::eq(const Item *item, bool binary_cmp) const type() can be only among basic constant types. */ return type() == item->type() && name.str && item->name.str && - name.length == item->name.length && - !my_strcasecmp(system_charset_info, name.str, item->name.str); + !lex_string_cmp(system_charset_info, &name, &item->name); } @@ -1298,7 +1303,7 @@ Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) if (!needs_charset_converter(tocs)) return this; Item_func_conv_charset *conv= new (thd->mem_root) Item_func_conv_charset(thd, this, tocs, 1); - return conv->safe ? conv : NULL; + return conv && conv->safe ? conv : NULL; } @@ -1867,7 +1872,7 @@ Item_splocal::this_item_addr(THD *thd, Item **) void Item_splocal::print(String *str, enum_query_type) { str->reserve(m_name.length+8); - str->append(m_name.str, m_name.length); + str->append(&m_name); str->append('@'); str->qs_append(m_var_idx); } @@ -1992,9 +1997,9 @@ Item_splocal_row_field::this_item_addr(THD *thd, Item **) void Item_splocal_row_field::print(String *str, enum_query_type) { str->reserve(m_name.length + m_field_name.length + 8); - str->append(m_name.str, m_name.length); + str->append(&m_name); str->append('.'); - str->append(m_field_name.str, m_field_name.length); + str->append(&m_field_name); str->append('@'); str->qs_append(m_var_idx); str->append('['); @@ -2027,13 +2032,13 @@ void Item_splocal_row_field_by_name::print(String *str, enum_query_type) // +16 should be enough for .NNN@[""] if (str->reserve(m_name.length + 2 * m_field_name.length + 16)) return; - str->qs_append(m_name.str, m_name.length); + str->qs_append(&m_name); str->qs_append('.'); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append('@'); str->qs_append(m_var_idx); str->qs_append("[\"", 2); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append("\"]", 2); } @@ -2759,15 +2764,15 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll, 0 if an error occured */ -Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_func_or_sum::build_clone(THD *thd) { - Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd, mem_root); + Item_func_or_sum *copy= (Item_func_or_sum *) get_copy(thd); if (!copy) return 0; if (arg_count > 2) { copy->args= - (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count); + (Item**) alloc_root(thd->mem_root, sizeof(Item*) * arg_count); if (!copy->args) return 0; } @@ -2777,7 +2782,7 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) for (uint i= 0; i < arg_count; i++) { - Item *arg_clone= args[i]->build_clone(thd, mem_root); + Item *arg_clone= args[i]->build_clone(thd); if (!arg_clone) return 0; copy->args[i]= arg_clone; @@ -2785,6 +2790,252 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) return copy; } +Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg) : + context(context_arg), m_name(name_arg), m_sp(NULL), func_ctx(NULL), + sp_result_field(NULL) +{ + dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE) + + sizeof(Query_arena)); + dummy_table->s= (TABLE_SHARE*) (dummy_table + 1); + /* TODO(cvicentiu) Move this sp_query_arena in the class as a direct member. + Currently it can not be done due to header include dependencies. */ + sp_query_arena= (Query_arena *) (dummy_table->s + 1); + memset(&sp_mem_root, 0, sizeof(sp_mem_root)); +} + +const char * +Item_sp::func_name(THD *thd) const +{ + /* Calculate length to avoid reallocation of string for sure */ + size_t len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + + m_name->m_name.length)*2 + //characters*quoting + 2 + // ` and ` + (m_name->m_explicit_name ? + 3 : 0) + // '`', '`' and '.' for the db + 1 + // end of string + ALIGN_SIZE(1)); // to avoid String reallocation + String qname((char *)alloc_root(thd->mem_root, len), len, + system_charset_info); + + qname.length(0); + if (m_name->m_explicit_name) + { + append_identifier(thd, &qname, &m_name->m_db); + qname.append('.'); + } + append_identifier(thd, &qname, &m_name->m_name); + return qname.c_ptr_safe(); +} + +void +Item_sp::cleanup() +{ + delete sp_result_field; + sp_result_field= NULL; + m_sp= NULL; + delete func_ctx; + func_ctx= NULL; + free_root(&sp_mem_root, MYF(0)); + dummy_table->alias.free(); +} + +/** + @brief Checks if requested access to function can be granted to user. + If function isn't found yet, it searches function first. + If function can't be found or user don't have requested access + error is raised. + + @param thd thread handler + + @return Indication if the access was granted or not. + @retval FALSE Access is granted. + @retval TRUE Requested access can't be granted or function doesn't exists. + +*/ +bool +Item_sp::sp_check_access(THD *thd) +{ + DBUG_ENTER("Item_sp::sp_check_access"); + DBUG_ASSERT(m_sp); + DBUG_RETURN(m_sp->check_execute_access(thd)); +} + +/** + @brief Execute function & store value in field. + + @return Function returns error status. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ +bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count) +{ + if (execute_impl(thd, args, arg_count)) + { + *null_value= 1; + context->process_error(thd); + if (thd->killed) + thd->send_kill_message(); + return true; + } + + /* Check that the field (the value) is not NULL. */ + + *null_value= sp_result_field->is_null(); + return (*null_value); +} + +/** + @brief Execute function and store the return value in the field. + + @note This function was intended to be the concrete implementation of + the interface function execute. This was never realized. + + @return The error state. + @retval FALSE on success + @retval TRUE if an error occurred. +*/ +bool +Item_sp::execute_impl(THD *thd, Item **args, uint arg_count) +{ + Sub_statement_state statement_state; + Security_context *save_security_ctx= thd->security_ctx; + enum enum_sp_data_access access= + (m_sp->daccess() == SP_DEFAULT_ACCESS) ? + SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess(); + + DBUG_ENTER("Item_sp::execute_impl"); + + if (context->security_ctx) + { + /* Set view definer security context */ + thd->security_ctx= context->security_ctx; + } + + if (sp_check_access(thd)) + { + thd->security_ctx= save_security_ctx; + DBUG_RETURN(TRUE); + } + + /* + Throw an error if a non-deterministic function is called while + statement-based replication (SBR) is active. + */ + + if (!m_sp->detistic() && !trust_function_creators && + (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) && + (mysql_bin_log.is_open() && + thd->variables.binlog_format == BINLOG_FORMAT_STMT)) + { + my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0)); + thd->security_ctx= save_security_ctx; + DBUG_RETURN(TRUE); + } + + /* + Disable the binlogging if this is not a SELECT statement. If this is a + SELECT, leave binlogging on, so execute_function() code writes the + function call into binlog. + */ + thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); + + /* + If this function is an aggregate function, we want to initialise the + mem_root only once per group. For a regular stored function, we will + initialise once for each call to execute_function. + */ + m_sp->agg_type(); + DBUG_ASSERT(m_sp->agg_type() == GROUP_AGGREGATE || + (m_sp->agg_type() == NOT_AGGREGATE && !func_ctx)); + if (!func_ctx) + { + init_sql_alloc(&sp_mem_root, "Item_sp", MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); + *sp_query_arena= Query_arena(&sp_mem_root, + Query_arena::STMT_INITIALIZED_FOR_SP); + } + + bool err_status= m_sp->execute_function(thd, args, arg_count, + sp_result_field, &func_ctx, + sp_query_arena); + /* + We free the function context when the function finished executing normally + (quit_func == TRUE) or the function has exited with an error. + */ + if (err_status || func_ctx->quit_func) + { + /* Free Items allocated during function execution. */ + delete func_ctx; + func_ctx= NULL; + sp_query_arena->free_items(); + free_root(&sp_mem_root, MYF(0)); + memset(&sp_mem_root, 0, sizeof(sp_mem_root)); + } + thd->restore_sub_statement_state(&statement_state); + + thd->security_ctx= save_security_ctx; + DBUG_RETURN(err_status); +} + + +/** + @brief Initialize the result field by creating a temporary dummy table + and assign it to a newly created field object. Meta data used to + create the field is fetched from the sp_head belonging to the stored + proceedure found in the stored procedure functon cache. + + @note This function should be called from fix_fields to init the result + field. It is some what related to Item_field. + + @see Item_field + + @param thd A pointer to the session and thread context. + + @return Function return error status. + @retval TRUE is returned on an error + @retval FALSE is returned on success. +*/ + +bool +Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null, + bool *null_value, LEX_CSTRING *name) +{ + DBUG_ENTER("Item_sp::init_result_field"); + + DBUG_ASSERT(m_sp != NULL); + DBUG_ASSERT(sp_result_field == NULL); + + /* + A Field needs to be attached to a Table. + Below we "create" a dummy table by initializing + the needed pointers. + */ + dummy_table->alias.set("", 0, table_alias_charset); + dummy_table->in_use= thd; + dummy_table->copy_blobs= TRUE; + dummy_table->s->table_cache_key= empty_clex_str; + dummy_table->s->table_name= empty_clex_str; + dummy_table->maybe_null= maybe_null; + + if (!(sp_result_field= m_sp->create_result_field(max_length, name, + dummy_table))) + DBUG_RETURN(TRUE); + + if (sp_result_field->pack_length() > sizeof(result_buf)) + { + void *tmp; + if (!(tmp= thd->alloc(sp_result_field->pack_length()))) + DBUG_RETURN(TRUE); + sp_result_field->move_field((uchar*) tmp); + } + else + sp_result_field->move_field(result_buf); + + sp_result_field->null_ptr= (uchar *) null_value; + sp_result_field->null_bit= 1; + + DBUG_RETURN(FALSE); +} /** @brief @@ -2802,19 +3053,13 @@ Item* Item_func_or_sum::build_clone(THD *thd, MEM_ROOT *mem_root) 0 if an error occured */ -Item* Item_ref::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_ref::build_clone(THD *thd) { - Item_ref *copy= (Item_ref *) get_copy(thd, mem_root); - if (!copy) - return 0; - copy->ref= - (Item**) alloc_root(mem_root, sizeof(Item*)); - if (!copy->ref) - return 0; - Item *item_clone= (* ref)->build_clone(thd, mem_root); - if (!item_clone) + Item_ref *copy= (Item_ref *) get_copy(thd); + if (!copy || + !(copy->ref= (Item**) alloc_root(thd->mem_root, sizeof(Item*))) || + !(*copy->ref= (* ref)->build_clone(thd))) return 0; - *copy->ref= item_clone; return copy; } @@ -3085,7 +3330,7 @@ void Item_ident::print(String *str, enum_query_type query_type) bool use_db_name= use_table_name && db_name && db_name[0] && !alias_name_used; if (use_db_name && (query_type & QT_ITEM_IDENT_SKIP_DB_NAMES)) - use_db_name= !thd->db || strcmp(thd->db, db_name); + use_db_name= !thd->db.str || strcmp(thd->db.str, db_name); if (use_db_name) use_db_name= !(cached_table && cached_table->belong_to_view && @@ -3142,7 +3387,7 @@ void Item_ident::print(String *str, enum_query_type query_type) append_identifier(thd, str, t_name, (uint) strlen(t_name)); str->append('.'); } - append_identifier(thd, str, field_name.str, field_name.length); + append_identifier(thd, str, &field_name); } /* ARGSUSED */ @@ -3273,8 +3518,8 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const (In cases where we would choose wrong we would have to generate a ER_NON_UNIQ_ERROR). */ - return (!my_strcasecmp(system_charset_info, item_field->name.str, - field_name.str) && + return (!lex_string_cmp(system_charset_info, &item_field->name, + &field_name) && (!item_field->table_name || !table_name || (!my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && @@ -3296,6 +3541,11 @@ table_map Item_field::all_used_tables() const return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } + +/* + @Note thd->fatal_error can be set in case of OOM +*/ + void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge) { @@ -3355,6 +3605,8 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref, } Name_resolution_context *ctx= new Name_resolution_context(); + if (!ctx) + return; // Fatal error set if (context->select_lex == new_parent) { /* @@ -3408,7 +3660,7 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp) This is always 'signed'. Unsigned values are created with Item_uint() */ -Item_int::Item_int(THD *thd, const char *str_arg, uint length): +Item_int::Item_int(THD *thd, const char *str_arg, size_t length): Item_num(thd) { char *end_ptr= (char*) str_arg + length; @@ -3455,7 +3707,7 @@ Item *Item_bool::neg_transformer(THD *thd) } -Item_uint::Item_uint(THD *thd, const char *str_arg, uint length): +Item_uint::Item_uint(THD *thd, const char *str_arg, size_t length): Item_int(thd, str_arg, length) { unsigned_flag= 1; @@ -3486,7 +3738,7 @@ void Item_uint::print(String *str, enum_query_type query_type) } -Item_decimal::Item_decimal(THD *thd, const char *str_arg, uint length, +Item_decimal::Item_decimal(THD *thd, const char *str_arg, size_t length, CHARSET_INFO *charset): Item_num(thd) { @@ -4388,13 +4640,13 @@ const String *Item_param::value_query_val_str(THD *thd, String *str) const /* Create date string inplace */ switch (value.time.time_type) { case MYSQL_TIMESTAMP_DATE: - str->append(C_STRING_WITH_LEN("DATE")); + str->append(STRING_WITH_LEN("DATE")); break; case MYSQL_TIMESTAMP_TIME: - str->append(C_STRING_WITH_LEN("TIME")); + str->append(STRING_WITH_LEN("TIME")); break; case MYSQL_TIMESTAMP_DATETIME: - str->append(C_STRING_WITH_LEN("TIMESTAMP")); + str->append(STRING_WITH_LEN("TIMESTAMP")); break; case MYSQL_TIMESTAMP_ERROR: case MYSQL_TIMESTAMP_NONE: @@ -5247,8 +5499,8 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) /* SELECT list element with explicit alias */ if ((*(cur_group->item))->name.str && !table_name && !(*(cur_group->item))->is_autogenerated_name && - !my_strcasecmp(system_charset_info, - (*(cur_group->item))->name.str, field_name->str)) + !lex_string_cmp(system_charset_info, + &(*(cur_group->item))->name, field_name)) { ++cur_match_degree; } @@ -5263,8 +5515,8 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) DBUG_ASSERT(l_field_name->str != 0); - if (!my_strcasecmp(system_charset_info, - l_field_name->str, field_name->str)) + if (!lex_string_cmp(system_charset_info, + l_field_name, field_name)) ++cur_match_degree; else continue; @@ -5869,6 +6121,12 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Field *from_field= (Field *)not_found_field; bool outer_fixed= false; SELECT_LEX *select= thd->lex->current_select; + + if (select && select->in_tvc) + { + my_error(ER_FIELD_REFERENCE_IN_TVC, MYF(0), full_name()); + return(1); + } if (!field) // If field is not checked { @@ -5878,6 +6136,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) expression to 'reference', i.e. it substitute that expression instead of this Item_field */ + DBUG_ASSERT(context); if ((from_field= find_field_in_tables(thd, this, context->first_name_resolution_table, context->last_name_resolution_table, @@ -6015,11 +6274,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) set_field(from_field); } - else if (thd->mark_used_columns != MARK_COLUMNS_NONE) + else if (should_mark_column(thd->column_usage)) { TABLE *table= field->table; MY_BITMAP *current_bitmap, *other_bitmap; - if (thd->mark_used_columns == MARK_COLUMNS_READ) + if (thd->column_usage == MARK_COLUMNS_READ) { current_bitmap= table->read_set; other_bitmap= table->write_set; @@ -6739,7 +6998,7 @@ int Item_int::save_in_field(Field *field, bool no_conversions) Item *Item_int::clone_item(THD *thd) { - return new (thd->mem_root) Item_int(thd, name.str, value, max_length); + return new (thd->mem_root) Item_int(thd, name.str, value, max_length, unsigned_flag); } @@ -6903,7 +7162,7 @@ static uint nr_of_decimals(const char *str, const char *end) Item->name should be fixed to use LEX_STRING eventually. */ -Item_float::Item_float(THD *thd, const char *str_arg, uint length): +Item_float::Item_float(THD *thd, const char *str_arg, size_t length): Item_num(thd) { int error; @@ -6913,13 +7172,13 @@ Item_float::Item_float(THD *thd, const char *str_arg, uint length): if (error) { char tmp[NAME_LEN + 1]; - my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg); + my_snprintf(tmp, sizeof(tmp), "%.*s", (int)length, str_arg); my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp); } presentation= name.str= str_arg; name.length= strlen(str_arg); decimals=(uint8) nr_of_decimals(str_arg, str_arg+length); - max_length=length; + max_length=(uint32)length; fixed= 1; } @@ -6956,10 +7215,9 @@ inline uint char_val(char X) } -void Item_hex_constant::hex_string_init(THD *thd, const char *str, - uint str_length) +void Item_hex_constant::hex_string_init(THD *thd, const char *str, size_t str_length) { - max_length=(str_length+1)/2; + max_length=(uint)((str_length+1)/2); char *ptr=(char*) thd->alloc(max_length+1); if (!ptr) { @@ -7021,7 +7279,7 @@ void Item_hex_string::print(String *str, enum_query_type query_type) In number context this is a longlong value. */ -Item_bin_string::Item_bin_string(THD *thd, const char *str, uint str_length): +Item_bin_string::Item_bin_string(THD *thd, const char *str, size_t str_length): Item_hex_hybrid(thd) { const char *end= str + str_length - 1; @@ -7029,7 +7287,7 @@ Item_bin_string::Item_bin_string(THD *thd, const char *str, uint str_length): uchar bits= 0; uint power= 1; - max_length= (str_length + 7) >> 3; + max_length= (uint)((str_length + 7) >> 3); if (!(ptr= (char*) thd->alloc(max_length + 1))) return; str_value.set(ptr, max_length, &my_charset_bin); @@ -7069,6 +7327,26 @@ bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const &((Item_temporal_literal *) item)->cached_time); } +bool Item_temporal_literal::operator<(const MYSQL_TIME <ime) const +{ + if (my_time_compare(&cached_time, <ime) < 0) + return true; + return false; +} + +bool Item_temporal_literal::operator>(const MYSQL_TIME <ime) const +{ + if (my_time_compare(&cached_time, <ime) > 0) + return true; + return false; +} + +bool Item_temporal_literal::operator==(const MYSQL_TIME <ime) const +{ + if (my_time_compare(&cached_time, <ime) == 0) + return true; + return false; +} void Item_date_literal::print(String *str, enum_query_type query_type) { @@ -7412,7 +7690,7 @@ Item *Item_field::derived_field_transformer_for_where(THD *thd, uchar *arg) st_select_lex *sel= (st_select_lex *)arg; Item *producing_item= find_producing_item(this, sel); if (producing_item) - return producing_item->build_clone(thd, thd->mem_root); + return producing_item->build_clone(thd); return this; } @@ -7424,7 +7702,7 @@ Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd, st_select_lex *sel= (st_select_lex *)arg; Item *producing_item= find_producing_item(this, sel); DBUG_ASSERT (producing_item != NULL); - return producing_item->build_clone(thd, thd->mem_root); + return producing_item->build_clone(thd); } return this; } @@ -7470,7 +7748,7 @@ Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd, st_select_lex *sel= (st_select_lex *)arg; Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); if (gr_field) - return gr_field->producing_item->build_clone(thd, thd->mem_root); + return gr_field->producing_item->build_clone(thd); return this; } @@ -7483,7 +7761,7 @@ Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd, return this; st_select_lex *sel= (st_select_lex *)arg; Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); - return gr_field->producing_item->build_clone(thd, thd->mem_root); + return gr_field->producing_item->build_clone(thd); } void Item_field::print(String *str, enum_query_type query_type) @@ -7993,8 +8271,7 @@ void Item_ref::print(String *str, enum_query_type query_type) !table_name && name.str && alias_name_used) { THD *thd= current_thd; - append_identifier(thd, str, (*ref)->real_item()->name.str, - (*ref)->real_item()->name.length); + append_identifier(thd, str, &(*ref)->real_item()->name); } else (*ref)->print(str, query_type); @@ -8730,7 +9007,7 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) */ Field *fld= ((Item_field*) ref_item)->field; DBUG_ASSERT(fld && fld->table); - if (thd->mark_used_columns == MARK_COLUMNS_READ) + if (thd->column_usage == MARK_COLUMNS_READ) bitmap_set_bit(fld->table->read_set, fld->field_index); } } @@ -9024,14 +9301,14 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) goto error; memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); - IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED + IF_DBUG_ASSERT(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED if (def_field->default_value && def_field->default_value->flags) { uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length()); if (!newptr) goto error; fix_session_vcol_expr_for_read(thd, def_field, def_field->default_value); - if (thd->mark_used_columns != MARK_COLUMNS_NONE) + if (should_mark_column(thd->column_usage)) def_field->default_value->expr->walk(&Item::register_field_in_read_map, 1, 0); def_field->move_field(newptr+1, def_field->maybe_null() ? newptr : 0, 1); } @@ -9273,7 +9550,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) void Item_insert_value::print(String *str, enum_query_type query_type) { - str->append(STRING_WITH_LEN("values(")); + str->append(STRING_WITH_LEN("value(")); arg->print(str, query_type); str->append(')'); } @@ -9309,15 +9586,16 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table, So instead we do it in Table_triggers_list::mark_fields_used() method which is called during execution of these statements. */ - enum_mark_columns save_mark_used_columns= thd->mark_used_columns; - thd->mark_used_columns= MARK_COLUMNS_NONE; + enum_column_usage saved_column_usage= thd->column_usage; + thd->column_usage= want_privilege == SELECT_ACL ? COLUMNS_READ + : COLUMNS_WRITE; /* Try to find field by its name and if it will be found set field_idx properly. */ (void)find_field_in_table(thd, table, field_name.str, field_name.length, 0, &field_idx); - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; triggers= table->triggers; table_grants= table_grant_info; } @@ -9327,8 +9605,8 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const { return item->type() == TRIGGER_FIELD_ITEM && row_version == ((Item_trigger_field *)item)->row_version && - !my_strcasecmp(system_charset_info, field_name.str, - ((Item_trigger_field *)item)->field_name.str); + !lex_string_cmp(system_charset_info, &field_name, + &((Item_trigger_field *)item)->field_name); } @@ -10414,6 +10692,12 @@ Item_field::excl_dep_on_grouping_fields(st_select_lex *sel) return find_matching_grouping_field(this, sel) != NULL; } +bool Item_field::vers_trx_id() const +{ + DBUG_ASSERT(field); + return field->vers_trx_id(); +} + void Item::register_in(THD *thd) { next= thd->free_list; diff --git a/sql/item.h b/sql/item.h index 77aa6eb901e..7572b7cece2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -132,8 +132,6 @@ enum precedence { HIGHEST_PRECEDENCE }; -typedef Bounds_checked_array<Item*> Ref_ptr_array; - bool mark_unsupported_function(const char *where, void *store, uint result); /* convenience helper for mark_unsupported_function() above */ @@ -307,6 +305,28 @@ public: } }; +class Name_resolution_context_backup +{ + Name_resolution_context &ctx; + TABLE_LIST &table_list; + table_map save_map; + Name_resolution_context_state ctx_state; + +public: + Name_resolution_context_backup(Name_resolution_context &_ctx, TABLE_LIST &_table_list) + : ctx(_ctx), table_list(_table_list), save_map(_table_list.map) + { + ctx_state.save_state(&ctx, &table_list); + ctx.table_list= &table_list; + ctx.first_name_resolution_table= &table_list; + } + ~Name_resolution_context_backup() + { + ctx_state.restore_state(&ctx, &table_list); + table_list.map= save_map; + } +}; + /* This enum is used to report information about monotonicity of function @@ -547,7 +567,6 @@ public: String_copier_for_item(THD *thd): m_thd(thd) { } }; - class Item: public Value_source, public Type_all_attributes { @@ -731,7 +750,7 @@ public: name.length= 0; #endif } /*lint -e1509 */ - void set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs); + void set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs); void set_name_no_truncate(THD *thd, const char *str, uint length, CHARSET_INFO *cs); void init_make_field(Send_field *tmp_field,enum enum_field_types type); @@ -786,6 +805,10 @@ public: return type_handler()->field_type(); } virtual const Type_handler *type_handler() const= 0; + virtual uint field_flags() const + { + return 0; + } const Type_handler *type_handler_for_comparison() const { return type_handler()->type_handler_for_comparison(); @@ -1206,7 +1229,7 @@ public: virtual bool basic_const_item() const { return 0; } /* cloning of constant items (0 if it is not const) */ virtual Item *clone_item(THD *thd) { return 0; } - virtual Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return get_copy(thd, mem_root); } + virtual Item* build_clone(THD *thd) { return get_copy(thd); } virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} @@ -1688,7 +1711,7 @@ public: virtual bool set_fields_as_dependent_processor(void *arg) { return 0; } /*============== End of Item processor list ======================*/ - virtual Item *get_copy(THD *thd, MEM_ROOT *mem_root)=0; + virtual Item *get_copy(THD *thd)=0; bool cache_const_expr_analyzer(uchar **arg); Item* cache_const_expr_transformer(THD *thd, uchar *arg); @@ -1762,6 +1785,8 @@ public: virtual Item_field *field_for_view_update() { return 0; } + virtual bool vers_trx_id() const + { return false; } virtual Item *neg_transformer(THD *thd) { return NULL; } virtual Item *update_value_transformer(THD *thd, uchar *select_arg) { return this; } @@ -1774,6 +1799,8 @@ public: virtual Item *derived_grouping_field_transformer_for_where(THD *thd, uchar *arg) { return this; } + virtual Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg) + { return this; } virtual bool expr_cache_is_needed(THD *) { return FALSE; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const @@ -1955,12 +1982,14 @@ public: } }; +MEM_ROOT *get_thd_memroot(THD *thd); template <class T> -inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item) +inline Item* get_item_copy (THD *thd, T* item) { - Item *copy= new (mem_root) T(*item); - copy->register_in(thd); + Item *copy= new (get_thd_memroot(thd)) T(*item); + if (copy) + copy->register_in(thd); return copy; } @@ -2299,7 +2328,7 @@ public: LEX_CSTRING m_name; public: -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /* Routine to which this Item_splocal belongs. Used for checking if correct runtime context is used for variable handling. @@ -2408,7 +2437,7 @@ public: bool append_for_log(THD *thd, String *str); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } /* Override the inherited create_field_for_create_select(), @@ -2536,7 +2565,7 @@ public: purposes. */ virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } private: uint m_case_expr_id; @@ -2607,8 +2636,8 @@ public: { return mark_unsupported_function("name_const()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_name_const>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_name_const>(thd, this); } }; class Item_num: public Item_basic_constant @@ -2752,8 +2781,8 @@ public: const Type_handler *handler= field->type_handler(); return handler->type_handler_for_item_field(); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ident_for_show>(thd, mem_root, this); } + Item* get_copy(THD *thd) + { return get_item_copy<Item_ident_for_show>(thd, this); } }; @@ -2829,6 +2858,10 @@ public: return field->type_handler(); } TYPELIB *get_typelib() const { return field->get_typelib(); } + uint32 field_flags() const + { + return field->flags; + } enum_monotonicity_info get_monotonicity_info() const { return MONOTONIC_STRICT_INCREASING; @@ -2926,6 +2959,7 @@ public: uint32 max_display_length() const { return field->max_display_length(); } Item_field *field_for_view_update() { return this; } int fix_outer_field(THD *thd, Field **field, Item **reference); + virtual bool vers_trx_id() const; virtual Item *update_value_transformer(THD *thd, uchar *select_arg); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); Item *derived_field_transformer_for_where(THD *thd, uchar *arg); @@ -2938,8 +2972,8 @@ public: bool cleanup_excluding_const_fields_processor(void *arg) { return field && const_item() ? 0 : cleanup_processor(arg); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_field>(thd, this); } bool is_outer_field() const { DBUG_ASSERT(fixed); @@ -2967,8 +3001,8 @@ public: :Item_field(thd, field), Item_args() { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_field_row>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_field_row>(thd, this); } const Type_handler *type_handler() const { return &type_handler_row; } uint cols() const { return arg_count; } @@ -3062,8 +3096,8 @@ public: Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool check_partition_func_processor(void *int_arg) {return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_null>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_null>(thd, this); } }; class Item_null_result :public Item_null @@ -3447,7 +3481,7 @@ public: bool append_for_log(THD *thd, String *str); bool check_vcol_func_processor(void *int_arg) {return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } private: void invalid_default_param() const; @@ -3473,23 +3507,31 @@ class Item_int :public Item_num { public: longlong value; - Item_int(THD *thd, int32 i,uint length= MY_INT32_NUM_DECIMAL_DIGITS): + Item_int(THD *thd, int32 i,size_t length= MY_INT32_NUM_DECIMAL_DIGITS): Item_num(thd), value((longlong) i) - { max_length=length; fixed= 1; } - Item_int(THD *thd, longlong i,uint length= MY_INT64_NUM_DECIMAL_DIGITS): + { max_length=(uint32)length; fixed= 1; } + Item_int(THD *thd, longlong i,size_t length= MY_INT64_NUM_DECIMAL_DIGITS): Item_num(thd), value(i) - { max_length=length; fixed= 1; } - Item_int(THD *thd, ulonglong i, uint length= MY_INT64_NUM_DECIMAL_DIGITS): + { max_length=(uint32)length; fixed= 1; } + Item_int(THD *thd, ulonglong i, size_t length= MY_INT64_NUM_DECIMAL_DIGITS): Item_num(thd), value((longlong)i) - { max_length=length; fixed= 1; unsigned_flag= 1; } - Item_int(THD *thd, const char *str_arg,longlong i,uint length): + { max_length=(uint32)length; fixed= 1; unsigned_flag= 1; } + Item_int(THD *thd, const char *str_arg,longlong i,size_t length): Item_num(thd), value(i) { - max_length=length; + max_length=(uint32)length; name.str= str_arg; name.length= safe_strlen(name.str); fixed= 1; } - Item_int(THD *thd, const char *str_arg, uint length=64); + Item_int(THD *thd, const char *str_arg,longlong i,size_t length, bool flag): + Item_num(thd), value(i) + { + max_length=(uint32)length; + name.str= str_arg; name.length= safe_strlen(name.str); + fixed= 1; + unsigned_flag= flag; + } + Item_int(THD *thd, const char *str_arg, size_t length=64); enum Type type() const { return INT_ITEM; } const Type_handler *type_handler() const { return type_handler_long_or_longlong(); } @@ -3511,8 +3553,8 @@ public: { return (uint) (max_length - MY_TEST(value < 0)); } bool eq(const Item *item, bool binary_cmp) const { return int_eq(value, item); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_int>(thd, this); } }; @@ -3534,7 +3576,7 @@ public: class Item_uint :public Item_int { public: - Item_uint(THD *thd, const char *str_arg, uint length); + Item_uint(THD *thd, const char *str_arg, size_t length); Item_uint(THD *thd, ulonglong i): Item_int(thd, i, 10) {} Item_uint(THD *thd, const char *str_arg, longlong i, uint length); double val_real() @@ -3544,8 +3586,8 @@ public: virtual void print(String *str, enum_query_type query_type); Item *neg(THD *thd); uint decimal_precision() const { return max_length; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_uint>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_uint>(thd, this); } }; @@ -3568,7 +3610,7 @@ class Item_decimal :public Item_num protected: my_decimal decimal_value; public: - Item_decimal(THD *thd, const char *str_arg, uint length, + Item_decimal(THD *thd, const char *str_arg, size_t length, CHARSET_INFO *charset); Item_decimal(THD *thd, const char *str, const my_decimal *val_arg, uint decimal_par, uint length); @@ -3591,8 +3633,8 @@ public: uint decimal_precision() const { return decimal_value.precision(); } bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_decimal>(thd, this); } }; @@ -3601,7 +3643,7 @@ class Item_float :public Item_num const char *presentation; public: double value; - Item_float(THD *thd, const char *str_arg, uint length); + Item_float(THD *thd, const char *str_arg, size_t length); Item_float(THD *thd, const char *str, double val_arg, uint decimal_par, uint length): Item_num(thd), value(val_arg) { @@ -3642,8 +3684,8 @@ public: virtual void print(String *str, enum_query_type query_type); bool eq(const Item *item, bool binary_cmp) const { return real_eq(value, item); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_float>(thd, this); } }; @@ -3712,7 +3754,7 @@ public: str_value.set_or_copy_aligned(str, length, cs); fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire)); } - Item_string(THD *thd, const char *str, uint length, + Item_string(THD *thd, const char *str, size_t length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE): Item_basic_constant(thd) { @@ -3728,21 +3770,21 @@ public: fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire)); } // Constructors with an externally provided item name - Item_string(THD *thd, const char *name_par, const char *str, uint length, + Item_string(THD *thd, const char *name_par, const char *str, size_t length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE): Item_basic_constant(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value)); - set_name(thd, name_par, (uint) safe_strlen(name_par), system_charset_info); + set_name(thd, name_par,safe_strlen(name_par), system_charset_info); } - Item_string(THD *thd, const char *name_par, const char *str, uint length, + Item_string(THD *thd, const char *name_par, const char *str, size_t length, CHARSET_INFO *cs, Derivation dv, uint repertoire): Item_basic_constant(thd) { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value, repertoire)); - set_name(thd, name_par, (uint) safe_strlen(name_par), system_charset_info); + set_name(thd, name_par, safe_strlen(name_par), system_charset_info); } void print_value(String *to) const { @@ -3837,8 +3879,8 @@ public: return MYSQL_TYPE_STRING; // Not a temporal literal } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_string>(thd, this); } }; @@ -3943,10 +3985,10 @@ class Item_return_date_time :public Item_partition_func_safe_string enum_field_types date_time_field_type; public: Item_return_date_time(THD *thd, const char *name_arg, uint length_arg, - enum_field_types field_type_arg): + enum_field_types field_type_arg, uint dec_arg= 0): Item_partition_func_safe_string(thd, name_arg, length_arg, &my_charset_bin), date_time_field_type(field_type_arg) - { decimals= 0; } + { decimals= dec_arg; } const Type_handler *type_handler() const { return Type_handler::get_handler_by_field_type(date_time_field_type); @@ -4022,13 +4064,13 @@ public: class Item_hex_constant: public Item_basic_constant { private: - void hex_string_init(THD *thd, const char *str, uint str_length); + void hex_string_init(THD *thd, const char *str, size_t str_length); public: Item_hex_constant(THD *thd): Item_basic_constant(thd) { hex_string_init(thd, "", 0); } - Item_hex_constant(THD *thd, const char *str, uint str_length): + Item_hex_constant(THD *thd, const char *str, size_t str_length): Item_basic_constant(thd) { hex_string_init(thd, str, str_length); @@ -4064,7 +4106,7 @@ class Item_hex_hybrid: public Item_hex_constant { public: Item_hex_hybrid(THD *thd): Item_hex_constant(thd) {} - Item_hex_hybrid(THD *thd, const char *str, uint str_length): + Item_hex_hybrid(THD *thd, const char *str, size_t str_length): Item_hex_constant(thd, str, str_length) {} uint decimal_precision() const; double val_real() @@ -4096,8 +4138,8 @@ public: return &type_handler_longlong; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_hex_hybrid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_hex_hybrid>(thd, this); } }; @@ -4114,7 +4156,7 @@ class Item_hex_string: public Item_hex_constant { public: Item_hex_string(THD *thd): Item_hex_constant(thd) {} - Item_hex_string(THD *thd, const char *str, uint str_length): + Item_hex_string(THD *thd, const char *str, size_t str_length): Item_hex_constant(thd, str, str_length) {} longlong val_int() { @@ -4137,15 +4179,15 @@ public: collation.collation); } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_hex_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_hex_string>(thd, this); } }; class Item_bin_string: public Item_hex_hybrid { public: - Item_bin_string(THD *thd, const char *str,uint str_length); + Item_bin_string(THD *thd, const char *str, size_t str_length); }; @@ -4191,6 +4233,13 @@ public: { return val_decimal_from_date(decimal_value); } int save_in_field(Field *field, bool no_conversions) { return save_date_in_field(field, no_conversions); } + void set_time(MYSQL_TIME *ltime) + { + cached_time= *ltime; + } + bool operator>(const MYSQL_TIME <ime) const; + bool operator<(const MYSQL_TIME <ime) const; + bool operator==(const MYSQL_TIME <ime) const; }; @@ -4218,8 +4267,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_literal>(thd, this); } }; @@ -4239,8 +4288,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_time_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_time_literal>(thd, this); } }; @@ -4250,7 +4299,7 @@ public: class Item_datetime_literal: public Item_temporal_literal { public: - Item_datetime_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg): + Item_datetime_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg= 0): Item_temporal_literal(thd, ltime, dec_arg) { max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0); @@ -4262,8 +4311,8 @@ public: void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_datetime_literal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_datetime_literal>(thd, this); } }; @@ -4502,9 +4551,38 @@ public: virtual void fix_length_and_dec()= 0; bool const_item() const { return const_item_cache; } table_map used_tables() const { return used_tables_cache; } - Item* build_clone(THD *thd, MEM_ROOT *mem_root); + Item* build_clone(THD *thd); }; +class sp_head; +class sp_name; +struct st_sp_security_context; + +class Item_sp +{ +public: + Name_resolution_context *context; + sp_name *m_name; + sp_head *m_sp; + TABLE *dummy_table; + uchar result_buf[64]; + sp_rcontext *func_ctx; + MEM_ROOT sp_mem_root; + Query_arena *sp_query_arena; + + /* + The result field of the stored function. + */ + Field *sp_result_field; + Item_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg); + const char *func_name(THD *thd) const; + void cleanup(); + bool sp_check_access(THD *thd); + bool execute(THD *thd, bool *null_value, Item **args, uint arg_count); + bool execute_impl(THD *thd, Item **args, uint arg_count); + bool init_result_field(THD *thd, uint max_length, uint maybe_null, + bool *null_value, LEX_CSTRING *name); +}; class Item_ref :public Item_ident { @@ -4685,7 +4763,7 @@ public: return (*ref)->is_outer_field(); } - Item* build_clone(THD *thd, MEM_ROOT *mem_root); + Item* build_clone(THD *thd); /** Checks if the item tree that ref points to contains a subquery. @@ -4694,8 +4772,8 @@ public: { return (*ref)->with_subquery(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_ref>(thd, this); } bool excl_dep_on_table(table_map tab_map) { table_map used= used_tables(); @@ -4721,6 +4799,12 @@ public: return 0; return cleanup_processor(arg); } + virtual bool vers_trx_id() const + { + DBUG_ASSERT(ref); + DBUG_ASSERT(*ref); + return (*ref)->vers_trx_id(); + } }; @@ -4763,8 +4847,8 @@ public: bool is_null(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); virtual Ref_Type ref_type() { return DIRECT_REF; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_direct_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_direct_ref>(thd, this); } }; @@ -4929,9 +5013,9 @@ public: { return mark_unsupported_function("cache", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_wrapper>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_wrapper>(thd, this); } + Item *build_clone(THD *thd) { return 0; } }; @@ -5099,8 +5183,8 @@ public: item_equal= NULL; Item_direct_ref::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_direct_view_ref>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_direct_view_ref>(thd, this); } }; @@ -5193,8 +5277,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); virtual void print(String *str, enum_query_type query_type); table_map used_tables() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_ref_null_helper>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_ref_null_helper>(thd, this); } }; /* @@ -5237,6 +5321,7 @@ public: #include "item_xmlfunc.h" #include "item_jsonfunc.h" #include "item_create.h" +#include "item_vers.h" #endif /** @@ -5358,8 +5443,8 @@ public: { return get_date_from_string(ltime, fuzzydate); } void copy(); int save_in_field(Field *field, bool no_conversions); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_string>(thd, this); } }; @@ -5384,8 +5469,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { return get_date_from_int(ltime, fuzzydate); } virtual void copy(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_int>(thd, this); } }; @@ -5402,8 +5487,8 @@ public: { return null_value ? 0.0 : (double) (ulonglong) cached_value; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_uint>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_uint>(thd, this); } }; @@ -5434,8 +5519,8 @@ public: cached_value= item->val_real(); null_value= item->null_value; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_float>(thd, this); } }; @@ -5459,8 +5544,8 @@ public: return get_date_from_decimal(ltime, fuzzydate); } void copy(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_copy_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_copy_decimal>(thd, this); } }; @@ -5697,7 +5782,7 @@ public: bool update_vcol_processor(void *arg) { return 0; } bool check_vcol_func_processor(void *arg) { - return mark_unsupported_function("values()", arg, VCOL_IMPOSSIBLE); + return mark_unsupported_function("value()", arg, VCOL_IMPOSSIBLE); } }; @@ -5894,6 +5979,7 @@ public: } virtual void store(Item *item); + virtual Item *get_item() { return example; } virtual bool cache_value()= 0; bool basic_const_item() const { return MY_TEST(example && example->basic_const_item()); } @@ -5956,8 +6042,8 @@ public: bool cache_value(); int save_in_field(Field *field, bool no_conversions); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_int>(thd, this); } }; @@ -6001,8 +6087,8 @@ public: Item_cache_time(THD *thd) :Item_cache_temporal(thd, &type_handler_time2) { } bool cache_value(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_time>(thd, this); } }; @@ -6011,8 +6097,8 @@ class Item_cache_datetime: public Item_cache_temporal public: Item_cache_datetime(THD *thd) :Item_cache_temporal(thd, &type_handler_datetime2) { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_datetime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_datetime>(thd, this); } }; @@ -6021,8 +6107,8 @@ class Item_cache_date: public Item_cache_temporal public: Item_cache_date(THD *thd) :Item_cache_temporal(thd, &type_handler_newdate) { } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_date>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_date>(thd, this); } }; @@ -6041,8 +6127,8 @@ public: { return get_date_from_real(ltime, fuzzydate); } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_real>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_real>(thd, this); } }; @@ -6061,8 +6147,8 @@ public: { return get_date_from_decimal(ltime, fuzzydate); } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_decimal>(thd, this); } }; @@ -6091,8 +6177,8 @@ public: int save_in_field(Field *field, bool no_conversions); bool cache_value(); Item *convert_to_basic_const_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_str>(thd, this); } }; @@ -6116,8 +6202,8 @@ public: */ return Item::safe_charset_converter(thd, tocs); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_str_for_nullif>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_str_for_nullif>(thd, this); } }; @@ -6192,8 +6278,8 @@ public: } bool cache_value(); virtual void set_null(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cache_row>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cache_row>(thd, this); } }; @@ -6210,37 +6296,59 @@ class Item_type_holder: public Item, { protected: TYPELIB *enum_set_typelib; +private: + void init_flags(Item *item) + { + if (item->real_type() == Item::FIELD_ITEM) + { + Item_field *item_field= (Item_field *)item->real_item(); + m_flags|= (item_field->field->flags & + (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG)); + // TODO: additional field flag? + m_vers_trx_id= item_field->field->vers_trx_id(); + } + } public: Item_type_holder(THD *thd, Item *item) :Item(thd, item), Type_handler_hybrid_field_type(item->real_type_handler()), - enum_set_typelib(0) + enum_set_typelib(0), + m_flags(0), + m_vers_trx_id(false) { DBUG_ASSERT(item->fixed); maybe_null= item->maybe_null; + init_flags(item); } Item_type_holder(THD *thd, - const LEX_CSTRING *name_arg, + Item *item, const Type_handler *handler, const Type_all_attributes *attr, bool maybe_null_arg) :Item(thd), Type_handler_hybrid_field_type(handler), Type_geometry_attributes(handler, attr), - enum_set_typelib(attr->get_typelib()) + enum_set_typelib(attr->get_typelib()), + m_flags(0), + m_vers_trx_id(false) { - name= *name_arg; + name= item->name; Type_std_attributes::set(*attr); maybe_null= maybe_null_arg; + init_flags(item); } const Type_handler *type_handler() const { - const Type_handler *handler= Type_handler_hybrid_field_type::type_handler(); + const Type_handler *handler= m_vers_trx_id ? + &type_handler_vers_trx_id : + Type_handler_hybrid_field_type::type_handler(); return handler->type_handler_for_item_field(); } const Type_handler *real_type_handler() const { + if (m_vers_trx_id) + return &type_handler_vers_trx_id; return Type_handler_hybrid_field_type::type_handler(); } @@ -6265,7 +6373,20 @@ public: { Type_geometry_attributes::set_geometry_type(type); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } + +private: + uint m_flags; + bool m_vers_trx_id; +public: + uint32 field_flags() const + { + return m_flags; + } + virtual bool vers_trx_id() const + { + return m_vers_trx_id; + } }; diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 488eb52fb77..4d03462d7c3 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -22,7 +22,7 @@ Buffers to save and compare item values */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5846ef094d9..4296b2c6468 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include <m_ctype.h> #include "sql_select.h" @@ -127,9 +127,12 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const char *funcname, many cases. */ set_handler(items[0]->type_handler()->type_handler_for_comparison()); + m_vers_trx_id= items[0]->vers_trx_id(); for (uint i= 1 ; i < nitems ; i++) { unsigned_count+= items[i]->unsigned_flag; + if (!m_vers_trx_id) + m_vers_trx_id= items[i]->vers_trx_id(); if (aggregate_for_comparison(items[i]->type_handler()-> type_handler_for_comparison())) { @@ -421,7 +424,7 @@ void Item_func::convert_const_compared_to_int_field(THD *thd) args[field= 1]->real_item()->type() == FIELD_ITEM) { Item_field *field_item= (Item_field*) (args[field]->real_item()); - if ((field_item->field_type() == MYSQL_TYPE_LONGLONG || + if (((field_item->field_type() == MYSQL_TYPE_LONGLONG && !field_item->vers_trx_id()) || field_item->field_type() == MYSQL_TYPE_YEAR)) convert_const_to_int(thd, field_item, &args[!field]); } @@ -4479,6 +4482,26 @@ longlong Item_func_in::val_int() } +void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding) +{ + THD *thd= current_thd; + + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (to_be_transformed_into_in_subq(thd)) + { + transform_into_subq= true; + thd->lex->current_select->in_funcs.push_back(this, thd->mem_root); + } + + if (arena) + thd->restore_active_arena(arena, &backup); + + emb_on_expr_nest= embedding; +} + + longlong Item_func_bit_or::val_int() { DBUG_ASSERT(fixed == 1); @@ -4969,20 +4992,20 @@ void Item_cond::neg_arguments(THD *thd) 0 if an error occured */ -Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root) +Item *Item_cond::build_clone(THD *thd) { List_iterator_fast<Item> li(list); Item *item; - Item_cond *copy= (Item_cond *) get_copy(thd, mem_root); + Item_cond *copy= (Item_cond *) get_copy(thd); if (!copy) return 0; copy->list.empty(); while ((item= li++)) { - Item *arg_clone= item->build_clone(thd, mem_root); + Item *arg_clone= item->build_clone(thd); if (!arg_clone) return 0; - if (copy->list.push_back(arg_clone, mem_root)) + if (copy->list.push_back(arg_clone, thd->mem_root)) return 0; } return copy; @@ -5322,7 +5345,6 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, return FALSE; } - bool Item_func_like::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); @@ -5620,9 +5642,9 @@ int Regexp_processor_pcre::pcre_exec_with_warn(const pcre *code, } -bool Regexp_processor_pcre::exec(const char *str, int length, int offset) +bool Regexp_processor_pcre::exec(const char *str, size_t length, size_t offset) { - m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra, str, length, offset, 0, + m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra, str, (int)length, (int)offset, 0, m_SubStrVec, array_elements(m_SubStrVec)); return false; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index d4908ddf48d..44d1063fd57 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -268,8 +268,8 @@ public: Item_func_istrue(THD *thd, Item *a): Item_func_truth(thd, a, true, true) {} ~Item_func_istrue() {} virtual const char* func_name() const { return "istrue"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_istrue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_istrue>(thd, this); } }; @@ -284,8 +284,8 @@ public: Item_func_truth(thd, a, true, false) {} ~Item_func_isnottrue() {} virtual const char* func_name() const { return "isnottrue"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnottrue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnottrue>(thd, this); } }; @@ -299,8 +299,8 @@ public: Item_func_isfalse(THD *thd, Item *a): Item_func_truth(thd, a, false, true) {} ~Item_func_isfalse() {} virtual const char* func_name() const { return "isfalse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isfalse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isfalse>(thd, this); } }; @@ -315,8 +315,8 @@ public: Item_func_truth(thd, a, false, false) {} ~Item_func_isnotfalse() {} virtual const char* func_name() const { return "isnotfalse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnotfalse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnotfalse>(thd, this); } }; @@ -380,8 +380,8 @@ public: void reset_cache() { cache= NULL; } virtual void print(String *str, enum_query_type query_type); void restore_first_argument(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_in_optimizer>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_in_optimizer>(thd, this); } }; @@ -541,10 +541,10 @@ public: return add_key_fields_optimize_op(join, key_fields, and_level, usable_tables, sargables, false); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { Item_bool_rowready_func2 *clone= - (Item_bool_rowready_func2 *) Item_func::build_clone(thd, mem_root); + (Item_bool_rowready_func2 *) Item_func::build_clone(thd); if (clone) { clone->cmp.comparators= 0; @@ -575,8 +575,8 @@ public: Item_args::propagate_equal_fields(thd, Context_boolean(), cond); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xor>(thd, this); } }; class Item_func_not :public Item_bool_func @@ -594,8 +594,8 @@ public: Item *neg_transformer(THD *thd); bool fix_fields(THD *, Item **); virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_not>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_not>(thd, this); } }; class Item_maxmin_subselect; @@ -643,8 +643,8 @@ public: void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_trig_cond>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_trig_cond>(thd, this); } }; class Item_func_not_all :public Item_func_not @@ -681,8 +681,8 @@ public: longlong val_int(); const char *func_name() const { return "<nop>"; } Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nop_all>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nop_all>(thd, this); } }; @@ -722,8 +722,8 @@ public: uint in_equality_no; virtual uint exists2in_reserved_items() { return 1; }; friend class Arg_comparator; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_eq>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_eq>(thd, this); } }; class Item_func_equal :public Item_bool_rowready_func2 @@ -746,8 +746,8 @@ public: return add_key_fields_optimize_op(join, key_fields, and_level, usable_tables, sargables, true); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_equal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_equal>(thd, this); } }; @@ -762,8 +762,8 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return ">="; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ge>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ge>(thd, this); } }; @@ -778,8 +778,8 @@ public: cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return ">"; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_gt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_gt>(thd, this); } }; @@ -794,8 +794,8 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<="; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_le>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_le>(thd, this); } }; @@ -810,8 +810,8 @@ public: cond_result eq_cmp_result() const { return COND_FALSE; } const char *func_name() const { return "<"; } Item *negated_item(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lt>(thd, this); } }; @@ -835,8 +835,8 @@ public: Item *negated_item(THD *thd); void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ne>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ne>(thd, this); } }; @@ -925,8 +925,8 @@ public: cond); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_between>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_between>(thd, this); } longlong val_int_cmp_string(); longlong val_int_cmp_temporal(); @@ -953,8 +953,8 @@ public: agg_arg_charsets_for_comparison(cmp_collation, args, 2); fix_char_length(2); // returns "1" or "0" or "-1" } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_strcmp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_strcmp>(thd, this); } }; @@ -988,8 +988,8 @@ public: str->append(func_name()); print_args(str, 0, query_type); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_interval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_interval>(thd, this); } }; @@ -1012,8 +1012,8 @@ public: } const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_coalesce>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_coalesce>(thd, this); } }; @@ -1085,8 +1085,8 @@ public: const char *func_name() const { return "ifnull"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ifnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ifnull>(thd, this); } }; @@ -1148,8 +1148,8 @@ public: const char *func_name() const { return "if"; } bool eval_not_null_tables(void *opt_arg); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_if>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_if>(thd, this); } private: void cache_type_info(Item *source); }; @@ -1169,8 +1169,8 @@ public: { fix_length_and_dec2_eliminate_null(args + 1); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nvl2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nvl2>(thd, this); } }; @@ -1256,8 +1256,8 @@ public: cond, &args[2]); return this; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nullif>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nullif>(thd, this); } Item *derived_field_transformer_for_having(THD *thd, uchar *arg) { reset_first_arg_if_needed(); return this; } Item *derived_field_transformer_for_where(THD *thd, uchar *arg) @@ -2117,9 +2117,9 @@ public: enum precedence precedence() const { return BETWEEN_PRECEDENCE; } CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } bool need_parentheses_in_default() { return true; } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { - Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); + Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd); if (clone) clone->arg_buffer= 0; return clone; @@ -2154,8 +2154,8 @@ public: return this; } Item *find_item(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case_searched>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_case_searched>(thd, this); } }; @@ -2200,17 +2200,17 @@ public: void fix_length_and_dec(); Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); Item *find_item(); - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *build_clone(THD *thd) { Item_func_case_simple *clone= (Item_func_case_simple *) - Item_func_case::build_clone(thd, mem_root); + Item_func_case::build_clone(thd); uint ncases= when_count(); if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases)) return NULL; return clone; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case_simple>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_case_simple>(thd, this); } }; @@ -2225,8 +2225,8 @@ public: { Item_func::print(str, query_type); } void fix_length_and_dec(); Item *find_item(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode_oracle>(thd, this); } }; @@ -2283,6 +2283,7 @@ class Item_func_in :public Item_func_opt_neg, protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value); + bool transform_into_subq; public: /// An array of values, created when the bisection lookup method is used in_vector *array; @@ -2299,11 +2300,14 @@ public: */ bool arg_types_compatible; + TABLE_LIST *emb_on_expr_nest; + Item_func_in(THD *thd, List<Item> &list): Item_func_opt_neg(thd, list), Predicant_to_list_comparator(thd, arg_count - 1), + transform_into_subq(false), array(0), have_null(0), - arg_types_compatible(FALSE) + arg_types_compatible(FALSE), emb_on_expr_nest(0) { } longlong val_int(); bool fix_fields(THD *, Item **); @@ -2383,11 +2387,11 @@ public: bool eval_not_null_tables(void *opt_arg); void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge); bool count_sargable_conds(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_in>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_in>(thd, this); } + Item *build_clone(THD *thd) { - Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd, mem_root); + Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd); if (clone) { clone->array= 0; @@ -2395,7 +2399,11 @@ public: return NULL; } return clone; - } + } + void mark_as_condition_AND_part(TABLE_LIST *embedding); + bool to_be_transformed_into_in_subq(THD *thd); + bool create_value_list_for_tvc(THD *thd, List< List<Item> > *values); + Item *in_predicate_to_in_subs_transformer(THD *thd, uchar *arg); }; class cmp_item_row :public cmp_item @@ -2511,8 +2519,8 @@ public: bool top_level); table_map not_null_tables() const { return 0; } Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnull>(thd, this); } }; /* Functions used by HAVING for rewriting IN subquery */ @@ -2559,8 +2567,8 @@ public: Item *neg_transformer(THD *thd); void print(String *str, enum_query_type query_type); void top_level_item() { abort_on_null=1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isnotnull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isnotnull>(thd, this); } }; @@ -2701,8 +2709,8 @@ public: bool find_selective_predicates_list_processor(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like>(thd, this); } }; @@ -2760,7 +2768,7 @@ public: { return !m_is_const && compile(item, false); } - bool exec(const char *str, int length, int offset); + bool exec(const char *str, size_t length, size_t offset); bool exec(String *str, int offset, uint n_result_offsets_to_convert); bool exec(Item *item, int offset, uint n_result_offsets_to_convert); bool match() const { return m_pcre_exec_rc < 0 ? 0 : 1; } @@ -2813,11 +2821,11 @@ public: void fix_length_and_dec(); const char *func_name() const { return "regexp"; } enum precedence precedence() const { return CMP_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_regex>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + 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, mem_root); + Item_func_regex *clone= (Item_func_regex*) Item_bool_func::build_clone(thd); if (clone) clone->re.reset(); return clone; @@ -2862,8 +2870,8 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_instr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_regexp_instr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_regexp_instr>(thd, this); } }; @@ -2940,7 +2948,7 @@ public: Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); bool eval_not_null_tables(void *opt_arg); - Item *build_clone(THD *thd, MEM_ROOT *mem_root); + Item *build_clone(THD *thd); }; template <template<class> class LI, class T> class Item_equal_iterator; @@ -3114,7 +3122,7 @@ public: void set_context_field(Item_field *ctx_field) { context_field= ctx_field; } void set_link_equal_fields(bool flag) { link_equal_fields= flag; } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } /* This does not comply with the specification of the virtual method, but Item_equal items are processed distinguishly anyway @@ -3268,8 +3276,8 @@ public: void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, SARGABLE_PARAM **sargables); SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cond_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cond_and>(thd, this); } }; inline bool is_cond_and(Item *item) @@ -3295,8 +3303,8 @@ public: table_map not_null_tables() const { return and_tables_cache; } Item *copy_andor_structure(THD *thd); Item *neg_transformer(THD *thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_cond_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_cond_or>(thd, this); } }; class Item_func_dyncol_check :public Item_bool_func @@ -3306,8 +3314,8 @@ public: longlong val_int(); const char *func_name() const { return "column_check"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_check>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_check>(thd, this); } }; class Item_func_dyncol_exists :public Item_bool_func @@ -3318,8 +3326,8 @@ public: longlong val_int(); const char *func_name() const { return "column_exists"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_exists>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_exists>(thd, this); } }; @@ -3347,8 +3355,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { } const char *func_name() const { return "%ISOPEN"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_isopen>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_isopen>(thd, this); } }; @@ -3359,8 +3367,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } const char *func_name() const { return "%FOUND"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_found>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_found>(thd, this); } }; @@ -3371,8 +3379,8 @@ public: :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } const char *func_name() const { return "%NOTFOUND"; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_notfound>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_notfound>(thd, this); } }; diff --git a/sql/item_create.cc b/sql/item_create.cc index bc3ec0266ee..548ce3bac94 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -22,7 +22,7 @@ Functions to create an item. Used by sql_yac.yy */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -43,38 +43,6 @@ */ /** - Adapter for native functions with a variable number of arguments. - The main use of this class is to discard the following calls: - <code>foo(expr1 AS name1, expr2 AS name2, ...)</code> - which are syntactically correct (the syntax can refer to a UDF), - but semantically invalid for native functions. -*/ - -class Create_native_func : public Create_func -{ -public: - virtual Item *create_func(THD *thd, LEX_CSTRING *name, - List<Item> *item_list); - - /** - Builder method, with no arguments. - @param thd The current thread - @param name The native function name - @param item_list The function parameters, none of which are named - @return An item representing the function call - */ - virtual Item *create_native(THD *thd, LEX_CSTRING *name, - List<Item> *item_list) = 0; - -protected: - /** Constructor. */ - Create_native_func() {} - /** Destructor. */ - virtual ~Create_native_func() {} -}; - - -/** Adapter for functions that takes exactly zero arguments. */ @@ -3285,7 +3253,7 @@ Create_qfunc::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { LEX_CSTRING db; - if (! thd->db && ! thd->lex->sphead) + if (! thd->db.str && ! thd->lex->sphead) { /* The proper error message should be in the lines of: @@ -3303,7 +3271,7 @@ Create_qfunc::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) return NULL; } - if (thd->lex->copy_db_to(&db.str, &db.length)) + if (thd->lex->copy_db_to(&db)) return NULL; return create_with_db(thd, &db, name, false, item_list); @@ -3316,7 +3284,7 @@ Create_udf_func Create_udf_func::s_singleton; Item* Create_udf_func::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { - udf_func *udf= find_udf(name->str, (uint) name->length); + udf_func *udf= find_udf(name->str, name->length); DBUG_ASSERT(udf); return create(thd, udf, item_list); } @@ -6827,12 +6795,6 @@ Create_func_year_week::create_native(THD *thd, LEX_CSTRING *name, } -struct Native_func_registry -{ - LEX_CSTRING name; - Create_func *builder; -}; - #define BUILDER(F) & F::s_singleton #ifdef HAVE_SPATIAL @@ -6854,347 +6816,347 @@ struct Native_func_registry static Native_func_registry func_array[] = { - { { C_STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)}, - { { C_STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)}, - { { C_STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)}, - { { C_STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)}, - { { C_STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)}, - { { C_STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)}, - { { C_STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)}, - { { C_STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)}, - { { C_STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)}, - { { C_STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)}, - { { C_STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, - { { C_STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, - { { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, - { { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)}, - { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)}, - { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)}, - { { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)}, - { { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)}, - { { C_STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)}, - { { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, - { { C_STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)}, - { { C_STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)}, - { { C_STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, - { { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)}, - { { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)}, - { { C_STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)}, - { { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)}, - { { C_STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)}, - { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, - { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, - { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, - { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, - { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, - { { C_STRING_WITH_LEN("CONCAT_OPERATOR_ORACLE") }, BUILDER(Create_func_concat_operator_oracle)}, - { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, - { { C_STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)}, - { { C_STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)}, - { { C_STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)}, - { { C_STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)}, - { { C_STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)}, - { { C_STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)}, - { { C_STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)}, - { { C_STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, - { { C_STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)}, - { { C_STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)}, - { { C_STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)}, - { { C_STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)}, - { { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)}, - { { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)}, - { { C_STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)}, - { { C_STRING_WITH_LEN("DECODE_ORACLE") }, BUILDER(Create_func_decode_oracle)}, - { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)}, - { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)}, - { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, - { { C_STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, - { { C_STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)}, - { { C_STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)}, - { { C_STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)}, - { { C_STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)}, - { { C_STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)}, - { { C_STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)}, - { { C_STRING_WITH_LEN("EXP") }, BUILDER(Create_func_exp)}, - { { C_STRING_WITH_LEN("EXPORT_SET") }, BUILDER(Create_func_export_set)}, - { { C_STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)}, - { { C_STRING_WITH_LEN("EXTRACTVALUE") }, BUILDER(Create_func_xml_extractvalue)}, - { { C_STRING_WITH_LEN("FIELD") }, BUILDER(Create_func_field)}, - { { C_STRING_WITH_LEN("FIND_IN_SET") }, BUILDER(Create_func_find_in_set)}, - { { C_STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)}, - { { C_STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)}, - { { C_STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)}, - { { C_STRING_WITH_LEN("FROM_BASE64") }, BUILDER(Create_func_from_base64)}, - { { C_STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)}, - { { C_STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)}, - { { C_STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)}, - { { C_STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)}, - { { C_STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("GET_LOCK") }, BUILDER(Create_func_get_lock)}, - { { C_STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)}, - { { C_STRING_WITH_LEN("GREATEST") }, BUILDER(Create_func_greatest)}, - { { C_STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)}, - { { C_STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)}, - { { C_STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)}, - { { C_STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)}, - { { C_STRING_WITH_LEN("INET6_ATON") }, BUILDER(Create_func_inet6_aton)}, - { { C_STRING_WITH_LEN("INET6_NTOA") }, BUILDER(Create_func_inet6_ntoa)}, - { { C_STRING_WITH_LEN("IS_IPV4") }, BUILDER(Create_func_is_ipv4)}, - { { C_STRING_WITH_LEN("IS_IPV6") }, BUILDER(Create_func_is_ipv6)}, - { { C_STRING_WITH_LEN("IS_IPV4_COMPAT") }, BUILDER(Create_func_is_ipv4_compat)}, - { { C_STRING_WITH_LEN("IS_IPV4_MAPPED") }, BUILDER(Create_func_is_ipv4_mapped)}, - { { C_STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)}, - { { C_STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, - { { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, - { { C_STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, - { { C_STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, - { { C_STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)}, - { { C_STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)}, - { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, - { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)}, - { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)}, - { { C_STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)}, - { { C_STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)}, - { { C_STRING_WITH_LEN("JSON_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)}, - { { C_STRING_WITH_LEN("JSON_COMPACT") }, BUILDER(Create_func_json_compact)}, - { { C_STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)}, - { { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)}, - { { C_STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)}, - { { C_STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)}, - { { C_STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)}, - { { C_STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)}, - { { C_STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)}, - { { C_STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)}, - { { C_STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)}, - { { C_STRING_WITH_LEN("JSON_LOOSE") }, BUILDER(Create_func_json_loose)}, - { { C_STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)}, - { { C_STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)}, - { { C_STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)}, - { { C_STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)}, - { { C_STRING_WITH_LEN("JSON_REMOVE") }, BUILDER(Create_func_json_remove)}, - { { C_STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)}, - { { C_STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)}, - { { C_STRING_WITH_LEN("JSON_SEARCH") }, BUILDER(Create_func_json_search)}, - { { C_STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)}, - { { C_STRING_WITH_LEN("JSON_UNQUOTE") }, BUILDER(Create_func_json_unquote)}, - { { C_STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)}, - { { C_STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)}, - { { C_STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)}, - { { C_STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)}, - { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)}, - { { C_STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)}, - { { C_STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)}, - { { C_STRING_WITH_LEN("LENGTHB") }, BUILDER(Create_func_octet_length)}, + { { STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)}, + { { STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)}, + { { STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)}, + { { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)}, + { { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)}, + { { STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)}, + { { STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)}, + { { STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, + { { STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, + { { STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)}, + { { STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)}, + { { STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)}, + { { STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)}, + { { STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)}, + { { STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)}, + { { STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, + { { STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)}, + { { STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)}, + { { STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, + { { STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)}, + { { STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)}, + { { STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)}, + { { STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)}, + { { STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)}, + { { STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, + { { STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, + { { STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, + { { STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, + { { STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, + { { STRING_WITH_LEN("CONCAT_OPERATOR_ORACLE") }, BUILDER(Create_func_concat_operator_oracle)}, + { { STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, + { { STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)}, + { { STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)}, + { { STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)}, + { { STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)}, + { { STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)}, + { { STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)}, + { { STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)}, + { { STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, + { { STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)}, + { { STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)}, + { { STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)}, + { { STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)}, + { { STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)}, + { { STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)}, + { { STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)}, + { { STRING_WITH_LEN("DECODE_ORACLE") }, BUILDER(Create_func_decode_oracle)}, + { { STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)}, + { { STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)}, + { { STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, + { { STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, + { { STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)}, + { { STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)}, + { { STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)}, + { { STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)}, + { { STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)}, + { { STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)}, + { { STRING_WITH_LEN("EXP") }, BUILDER(Create_func_exp)}, + { { STRING_WITH_LEN("EXPORT_SET") }, BUILDER(Create_func_export_set)}, + { { STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)}, + { { STRING_WITH_LEN("EXTRACTVALUE") }, BUILDER(Create_func_xml_extractvalue)}, + { { STRING_WITH_LEN("FIELD") }, BUILDER(Create_func_field)}, + { { STRING_WITH_LEN("FIND_IN_SET") }, BUILDER(Create_func_find_in_set)}, + { { STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)}, + { { STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)}, + { { STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)}, + { { STRING_WITH_LEN("FROM_BASE64") }, BUILDER(Create_func_from_base64)}, + { { STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)}, + { { STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)}, + { { STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)}, + { { STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)}, + { { STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("GET_LOCK") }, BUILDER(Create_func_get_lock)}, + { { STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)}, + { { STRING_WITH_LEN("GREATEST") }, BUILDER(Create_func_greatest)}, + { { STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)}, + { { STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)}, + { { STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)}, + { { STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)}, + { { STRING_WITH_LEN("INET6_ATON") }, BUILDER(Create_func_inet6_aton)}, + { { STRING_WITH_LEN("INET6_NTOA") }, BUILDER(Create_func_inet6_ntoa)}, + { { STRING_WITH_LEN("IS_IPV4") }, BUILDER(Create_func_is_ipv4)}, + { { STRING_WITH_LEN("IS_IPV6") }, BUILDER(Create_func_is_ipv6)}, + { { STRING_WITH_LEN("IS_IPV4_COMPAT") }, BUILDER(Create_func_is_ipv4_compat)}, + { { STRING_WITH_LEN("IS_IPV4_MAPPED") }, BUILDER(Create_func_is_ipv4_mapped)}, + { { STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)}, + { { STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, + { { STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, + { { STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, + { { STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, + { { STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)}, + { { STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)}, + { { STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, + { { STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)}, + { { STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)}, + { { STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)}, + { { STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)}, + { { STRING_WITH_LEN("JSON_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)}, + { { STRING_WITH_LEN("JSON_COMPACT") }, BUILDER(Create_func_json_compact)}, + { { STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)}, + { { STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)}, + { { STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)}, + { { STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)}, + { { STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)}, + { { STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)}, + { { STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)}, + { { STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)}, + { { STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)}, + { { STRING_WITH_LEN("JSON_LOOSE") }, BUILDER(Create_func_json_loose)}, + { { STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)}, + { { STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)}, + { { STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)}, + { { STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)}, + { { STRING_WITH_LEN("JSON_REMOVE") }, BUILDER(Create_func_json_remove)}, + { { STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)}, + { { STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)}, + { { STRING_WITH_LEN("JSON_SEARCH") }, BUILDER(Create_func_json_search)}, + { { STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)}, + { { STRING_WITH_LEN("JSON_UNQUOTE") }, BUILDER(Create_func_json_unquote)}, + { { STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)}, + { { STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)}, + { { STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)}, + { { STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)}, + { { STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)}, + { { STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)}, + { { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)}, + { { STRING_WITH_LEN("LENGTHB") }, BUILDER(Create_func_octet_length)}, #ifndef DBUG_OFF - { { C_STRING_WITH_LEN("LIKE_RANGE_MIN") }, BUILDER(Create_func_like_range_min)}, - { { C_STRING_WITH_LEN("LIKE_RANGE_MAX") }, BUILDER(Create_func_like_range_max)}, + { { STRING_WITH_LEN("LIKE_RANGE_MIN") }, BUILDER(Create_func_like_range_min)}, + { { STRING_WITH_LEN("LIKE_RANGE_MAX") }, BUILDER(Create_func_like_range_max)}, #endif - { { C_STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("LN") }, BUILDER(Create_func_ln)}, - { { C_STRING_WITH_LEN("LOAD_FILE") }, BUILDER(Create_func_load_file)}, - { { C_STRING_WITH_LEN("LOCATE") }, BUILDER(Create_func_locate)}, - { { C_STRING_WITH_LEN("LOG") }, BUILDER(Create_func_log)}, - { { C_STRING_WITH_LEN("LOG10") }, BUILDER(Create_func_log10)}, - { { C_STRING_WITH_LEN("LOG2") }, BUILDER(Create_func_log2)}, - { { C_STRING_WITH_LEN("LOWER") }, BUILDER(Create_func_lcase)}, - { { C_STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad)}, - { { C_STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim)}, - { { C_STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)}, - { { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)}, - { { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)}, - { { C_STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)}, - { { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)}, - { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)}, - { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, - { { C_STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)}, - { { C_STRING_WITH_LEN("MBREQUALS") }, GEOM_BUILDER(Create_func_mbr_equals)}, - { { C_STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, - { { C_STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, - { { C_STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)}, - { { C_STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)}, - { { C_STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)}, - { { C_STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)}, - { { C_STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)}, - { { C_STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)}, - { { C_STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)}, - { { C_STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)}, - { { C_STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, - { { C_STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, - { { C_STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, - { { C_STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)}, - { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)}, - { { C_STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)}, - { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, - { { C_STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)}, - { { C_STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)}, - { { C_STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)}, - { { C_STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)}, - { { C_STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)}, - { { C_STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)}, - { { C_STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)}, - { { C_STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)}, - { { C_STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)}, - { { C_STRING_WITH_LEN("REGEXP_REPLACE") }, BUILDER(Create_func_regexp_replace)}, - { { C_STRING_WITH_LEN("REGEXP_SUBSTR") }, BUILDER(Create_func_regexp_substr)}, - { { C_STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)}, - { { C_STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)}, - { { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)}, - { { C_STRING_WITH_LEN("REPLACE_ORACLE") }, + { { STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("LN") }, BUILDER(Create_func_ln)}, + { { STRING_WITH_LEN("LOAD_FILE") }, BUILDER(Create_func_load_file)}, + { { STRING_WITH_LEN("LOCATE") }, BUILDER(Create_func_locate)}, + { { STRING_WITH_LEN("LOG") }, BUILDER(Create_func_log)}, + { { STRING_WITH_LEN("LOG10") }, BUILDER(Create_func_log10)}, + { { STRING_WITH_LEN("LOG2") }, BUILDER(Create_func_log2)}, + { { STRING_WITH_LEN("LOWER") }, BUILDER(Create_func_lcase)}, + { { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad)}, + { { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim)}, + { { STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)}, + { { STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)}, + { { STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)}, + { { STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)}, + { { STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)}, + { { STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)}, + { { STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)}, + { { STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)}, + { { STRING_WITH_LEN("MBREQUALS") }, GEOM_BUILDER(Create_func_mbr_equals)}, + { { STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)}, + { { STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, + { { STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)}, + { { STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)}, + { { STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)}, + { { STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)}, + { { STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)}, + { { STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)}, + { { STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)}, + { { STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)}, + { { STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, + { { STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, + { { STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, + { { STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)}, + { { STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)}, + { { STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)}, + { { STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, + { { STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)}, + { { STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)}, + { { STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)}, + { { STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)}, + { { STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)}, + { { STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)}, + { { STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)}, + { { STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)}, + { { STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)}, + { { STRING_WITH_LEN("REGEXP_REPLACE") }, BUILDER(Create_func_regexp_replace)}, + { { STRING_WITH_LEN("REGEXP_SUBSTR") }, BUILDER(Create_func_regexp_substr)}, + { { STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)}, + { { STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)}, + { { STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)}, + { { STRING_WITH_LEN("REPLACE_ORACLE") }, BUILDER(Create_func_replace_oracle)}, - { { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)}, - { { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)}, - { { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)}, - { { C_STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)}, - { { C_STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)}, - { { C_STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)}, - { { C_STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)}, - { { C_STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)}, - { { C_STRING_WITH_LEN("SIGN") }, BUILDER(Create_func_sign)}, - { { C_STRING_WITH_LEN("SIN") }, BUILDER(Create_func_sin)}, - { { C_STRING_WITH_LEN("SLEEP") }, BUILDER(Create_func_sleep)}, - { { C_STRING_WITH_LEN("SOUNDEX") }, BUILDER(Create_func_soundex)}, - { { C_STRING_WITH_LEN("SPACE") }, BUILDER(Create_func_space)}, - { { C_STRING_WITH_LEN("SQRT") }, BUILDER(Create_func_sqrt)}, - { { C_STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)}, - { { C_STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, - { { C_STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)}, - { { C_STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)}, - { { C_STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)}, - { { C_STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)}, - { { C_STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)}, - { { C_STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)}, - { { C_STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)}, - { { C_STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, - { { C_STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)}, - { { C_STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, - { { C_STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, - { { C_STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)}, - { { C_STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)}, - { { C_STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, - { { C_STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)}, - { { C_STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, - { { C_STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)}, - { { C_STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)}, - { { C_STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)}, - { { C_STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)}, - { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, - { { C_STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)}, - { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)}, - { { C_STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)}, - { { C_STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)}, - { { C_STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)}, + { { STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)}, + { { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)}, + { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)}, + { { STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)}, + { { STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)}, + { { STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)}, + { { STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)}, + { { STRING_WITH_LEN("SIGN") }, BUILDER(Create_func_sign)}, + { { STRING_WITH_LEN("SIN") }, BUILDER(Create_func_sin)}, + { { STRING_WITH_LEN("SLEEP") }, BUILDER(Create_func_sleep)}, + { { STRING_WITH_LEN("SOUNDEX") }, BUILDER(Create_func_soundex)}, + { { STRING_WITH_LEN("SPACE") }, BUILDER(Create_func_space)}, + { { STRING_WITH_LEN("SQRT") }, BUILDER(Create_func_sqrt)}, + { { STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)}, + { { STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, + { { STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)}, + { { STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)}, + { { STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)}, + { { STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)}, + { { STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)}, + { { STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, + { { STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)}, + { { STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)}, + { { STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, + { { STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)}, + { { STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)}, + { { STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, + { { STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)}, + { { STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, + { { STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)}, + { { STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)}, + { { STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)}, + { { STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)}, + { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, + { { STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)}, + { { STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)}, + { { STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)}, + { { STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)}, + { { STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, #ifndef DBUG_OFF - { { C_STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)}, + { { STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)}, #endif - { { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, - { { C_STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, - { { C_STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)}, - { { C_STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)}, - { { C_STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, - { { C_STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, - { { C_STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)}, - { { C_STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, - { { C_STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)}, - { { C_STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, - { { C_STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, - { { C_STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, - { { C_STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)}, - { { C_STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)}, - { { C_STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)}, - { { C_STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, - { { C_STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, - { { C_STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)}, - { { C_STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)}, - { { C_STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, - { { C_STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)}, - { { C_STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, - { { C_STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)}, - { { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)}, - { { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)}, - { { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)}, - { { C_STRING_WITH_LEN("SUBSTR_ORACLE") }, + { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)}, + { { STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)}, + { { STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)}, + { { STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)}, + { { STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)}, + { { STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)}, + { { STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)}, + { { STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)}, + { { STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)}, + { { STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, + { { STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, + { { STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, + { { STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)}, + { { STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)}, + { { STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)}, + { { STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, + { { STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, + { { STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)}, + { { STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)}, + { { STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)}, + { { STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)}, + { { STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, + { { STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)}, + { { STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)}, + { { STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)}, + { { STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)}, + { { STRING_WITH_LEN("SUBSTR_ORACLE") }, BUILDER(Create_func_substr_oracle)}, - { { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, - { { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)}, - { { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)}, - { { C_STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)}, - { { C_STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)}, - { { C_STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)}, - { { C_STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, - { { C_STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)}, - { { C_STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)}, - { { C_STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)}, - { { C_STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)}, - { { C_STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)}, - { { C_STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)}, - { { C_STRING_WITH_LEN("UNHEX") }, BUILDER(Create_func_unhex)}, - { { C_STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)}, - { { C_STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)}, - { { C_STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)}, - { { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)}, - { { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, - { { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, - { { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, - { { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)}, - { { C_STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)}, - { { C_STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)}, - { { C_STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)}, - { { C_STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)}, + { { STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)}, + { { STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)}, + { { STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)}, + { { STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)}, + { { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)}, + { { STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)}, + { { STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)}, + { { STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)}, + { { STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)}, + { { STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)}, + { { STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)}, + { { STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)}, + { { STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)}, + { { STRING_WITH_LEN("UNHEX") }, BUILDER(Create_func_unhex)}, + { { STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)}, + { { STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)}, + { { STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)}, + { { STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)}, + { { STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, + { { STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, + { { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, + { { STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)}, + { { STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)}, + { { STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)}, + { { STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)}, + { { STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)}, { {0, 0}, NULL} }; @@ -7218,8 +7180,6 @@ get_native_fct_hash_key(const uchar *buff, size_t *length, int item_create_init() { - Native_func_registry *func; - DBUG_ENTER("item_create_init"); if (my_hash_init(& native_functions_hash, @@ -7232,7 +7192,16 @@ int item_create_init() MYF(0))) DBUG_RETURN(1); - for (func= func_array; func->builder != NULL; func++) + DBUG_RETURN(item_create_append(func_array)); +} + +int item_create_append(Native_func_registry array[]) +{ + Native_func_registry *func; + + DBUG_ENTER("item_create_append"); + + for (func= array; func->builder != NULL; func++) { if (my_hash_insert(& native_functions_hash, (uchar*) func)) DBUG_RETURN(1); @@ -7307,7 +7276,7 @@ have_important_literal_warnings(const MYSQL_TIME_STATUS *status) */ Item *create_temporal_literal(THD *thd, - const char *str, uint length, + const char *str, size_t length, CHARSET_INFO *cs, enum_field_types type, bool send_error) diff --git a/sql/item_create.h b/sql/item_create.h index 128a19a1c15..5983a092cdc 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -19,6 +19,8 @@ #ifndef ITEM_CREATE_H #define ITEM_CREATE_H +#include "item_func.h" // Cast_target + typedef struct st_udf_func udf_func; /** @@ -67,6 +69,38 @@ protected: /** + Adapter for native functions with a variable number of arguments. + The main use of this class is to discard the following calls: + <code>foo(expr1 AS name1, expr2 AS name2, ...)</code> + which are syntactically correct (the syntax can refer to a UDF), + but semantically invalid for native functions. +*/ + +class Create_native_func : public Create_func +{ +public: + virtual Item *create_func(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); + + /** + Builder method, with no arguments. + @param thd The current thread + @param name The native function name + @param item_list The function parameters, none of which are named + @return An item representing the function call + */ + virtual Item *create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) = 0; + +protected: + /** Constructor. */ + Create_native_func() {} + /** Destructor. */ + virtual ~Create_native_func() {} +}; + + +/** Function builder for qualified functions. This builder is used with functions call using a qualified function name syntax, as in <code>db.func(expr, expr, ...)</code>. @@ -158,7 +192,7 @@ protected: Item *create_temporal_literal(THD *thd, - const char *str, uint length, + const char *str, size_t length, CHARSET_INFO *cs, enum_field_types type, bool send_error); @@ -172,7 +206,14 @@ Item *create_temporal_literal(THD *thd, const String *str, type, send_error); } +struct Native_func_registry +{ + LEX_CSTRING name; + Create_func *builder; +}; + int item_create_init(); +int item_create_append(Native_func_registry array[]); void item_create_cleanup(); Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list); diff --git a/sql/item_func.cc b/sql/item_func.cc index bea57d6c938..f2dabf74060 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -944,6 +944,7 @@ double Item_func_hybrid_field_type::val_real_from_date_op() ltime.time_type= mysql_timestamp_type(); return TIME_to_double(<ime); } + longlong Item_func_hybrid_field_type::val_int_from_date_op() { MYSQL_TIME ltime; @@ -3219,7 +3220,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) DBUG_RETURN(TRUE); // Fatal error flag is set! - udf_func *tmp_udf=find_udf(u_d->name.str,(uint) u_d->name.length,1); + udf_func *tmp_udf=find_udf(u_d->name.str,u_d->name.length,1); if (!tmp_udf) { @@ -3276,17 +3277,15 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, func->used_tables_and_const_cache_join(item); f_args.arg_type[i]=item->result_type(); } - //TODO: why all following memory is not allocated with 1 thd->alloc() call? - if (!(buffers=new String[arg_count]) || - !(f_args.args= (char**) thd->alloc(arg_count * sizeof(char *))) || - !(f_args.lengths= (ulong*) thd->alloc(arg_count * sizeof(long))) || - !(f_args.maybe_null= (char*) thd->alloc(arg_count * sizeof(char))) || - !(num_buffer= (char*) thd->alloc(arg_count * - ALIGN_SIZE(sizeof(double)))) || - !(f_args.attributes= (const char**) thd->alloc(arg_count * - sizeof(char *))) || - !(f_args.attribute_lengths= (ulong*) thd->alloc(arg_count * - sizeof(long)))) + if (!(buffers=new (thd->mem_root) String[arg_count]) || + !multi_alloc_root(thd->mem_root, + &f_args.args, arg_count * sizeof(char *), + &f_args.lengths, arg_count * sizeof(long), + &f_args.maybe_null, arg_count * sizeof(char), + &num_buffer, arg_count * sizeof(double), + &f_args.attributes, arg_count * sizeof(char *), + &f_args.attribute_lengths, arg_count * sizeof(long), + NullS)) { free_udf(u_d); DBUG_RETURN(TRUE); @@ -3298,6 +3297,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, initid.const_item=func->const_item_cache; initid.decimals=func->decimals; initid.ptr=0; + for (uint i1= 0 ; i1 < arg_count ; i1++) + buffers[i1].set_thread_specific(); if (u_d->func_init) { @@ -3314,7 +3315,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, f_args.lengths[i]= arguments[i]->max_length; f_args.maybe_null[i]= (char) arguments[i]->maybe_null; f_args.attributes[i]= arguments[i]->name.str; - f_args.attribute_lengths[i]= arguments[i]->name.length; + f_args.attribute_lengths[i]= (ulong)arguments[i]->name.length; if (arguments[i]->const_item()) { @@ -3747,7 +3748,9 @@ longlong Item_master_gtid_wait::val_int() timeout_us= (longlong)-1; result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us); -#endif +#else + null_value= 0; +#endif /* REPLICATION */ return result; } @@ -4399,7 +4402,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, name->length)) && create_if_not_exists) { - uint size=ALIGN_SIZE(sizeof(user_var_entry))+name->length+1+extra_size; + size_t size=ALIGN_SIZE(sizeof(user_var_entry))+name->length+1+extra_size; if (!my_hash_inited(hash)) return 0; if (!(entry = (user_var_entry*) my_malloc(size, @@ -4620,7 +4623,7 @@ bool Item_func_set_user_var::register_field_in_bitmap(void *arg) */ static bool -update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, +update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, Item_result type, CHARSET_INFO *cs, bool unsigned_arg) { @@ -4681,7 +4684,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, bool -Item_func_set_user_var::update_hash(void *ptr, uint length, +Item_func_set_user_var::update_hash(void *ptr, size_t length, Item_result res_type, CHARSET_INFO *cs, bool unsigned_arg) @@ -5075,7 +5078,7 @@ bool Item_func_set_user_var::is_null_result() void Item_func_set_user_var::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("@")); - str->append(name.str, name.length); + str->append(&name); str->append(STRING_WITH_LEN(":=")); args[0]->print_parenthesised(str, query_type, precedence()); } @@ -5085,7 +5088,7 @@ void Item_func_set_user_var::print_as_stmt(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("set @")); - str->append(name.str, name.length); + str->append(&name); str->append(STRING_WITH_LEN(":=")); args[0]->print_parenthesised(str, query_type, precedence()); } @@ -5323,8 +5326,8 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command, Item_func_set_user_var(thd, name, new (thd->mem_root) Item_null(thd))), thd->mem_root); - /* Create the variable */ - if (sql_set_variables(thd, &tmp_var_list, false)) + /* Create the variable if the above allocations succeeded */ + if (thd->is_fatal_error || sql_set_variables(thd, &tmp_var_list, false)) { thd->lex= sav_lex; goto err; @@ -5345,7 +5348,7 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command, return 0; } - uint size; + size_t size; /* First we need to store value of var_entry, when the next situation appears: @@ -5412,7 +5415,7 @@ void Item_func_get_user_var::fix_length_and_dec() if (!error && m_var_entry) { unsigned_flag= m_var_entry->unsigned_flag; - max_length= m_var_entry->length; + max_length= (uint32)m_var_entry->length; collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT); set_handler_by_result_type(m_var_entry->type); switch (result_type()) { @@ -5456,7 +5459,7 @@ bool Item_func_get_user_var::const_item() const void Item_func_get_user_var::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("@")); - append_identifier(current_thd, str, name.str, name.length); + append_identifier(current_thd, str, &name); } @@ -5493,7 +5496,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(fixed == 0); DBUG_ASSERT(thd->lex->exchange); if (Item::fix_fields(thd, ref) || - !(entry= get_variable(&thd->user_vars, &name, 1))) + !(entry= get_variable(&thd->user_vars, &org_name, 1))) return TRUE; entry->type= STRING_RESULT; /* @@ -5561,7 +5564,7 @@ bool Item_user_var_as_out_param::get_date(MYSQL_TIME *ltime, ulonglong fuzzy) void Item_user_var_as_out_param::print_for_load(THD *thd, String *str) { str->append('@'); - append_identifier(thd, str, name.str, name.length); + append_identifier(thd, str, &org_name); } @@ -5633,7 +5636,7 @@ void Item_func_get_system_var::fix_length_and_dec() (char*) var->value_ptr(current_thd, var_type, &component) : *(char**) var->value_ptr(current_thd, var_type, &component); if (cptr) - max_length= system_charset_info->cset->numchars(system_charset_info, + max_length= (uint32)system_charset_info->cset->numchars(system_charset_info, cptr, cptr + strlen(cptr)); mysql_mutex_unlock(&LOCK_global_system_variables); @@ -5645,7 +5648,7 @@ void Item_func_get_system_var::fix_length_and_dec() { mysql_mutex_lock(&LOCK_global_system_variables); LEX_STRING *ls= ((LEX_STRING*)var->value_ptr(current_thd, var_type, &component)); - max_length= system_charset_info->cset->numchars(system_charset_info, + max_length= (uint32)system_charset_info->cset->numchars(system_charset_info, ls->str, ls->str + ls->length); mysql_mutex_unlock(&LOCK_global_system_variables); @@ -5675,7 +5678,7 @@ void Item_func_get_system_var::fix_length_and_dec() void Item_func_get_system_var::print(String *str, enum_query_type query_type) { if (name.length) - str->append(name.str, name.length); + str->append(&name); else { str->append(STRING_WITH_LEN("@@")); @@ -5868,20 +5871,25 @@ void Item_func_get_system_var::cleanup() cached_strval.free(); } +/** + @retval + 0 ok + 1 OOM error +*/ -void Item_func_match::init_search(THD *thd, bool no_order) +bool Item_func_match::init_search(THD *thd, bool no_order) { DBUG_ENTER("Item_func_match::init_search"); if (!table->file->get_table()) // the handler isn't opened yet - DBUG_VOID_RETURN; + DBUG_RETURN(0); /* Check if init_search() has been called before */ if (ft_handler) { if (join_key) table->file->ft_handler= ft_handler; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } if (key == NO_SUCH_KEY) @@ -5893,6 +5901,8 @@ void Item_func_match::init_search(THD *thd, bool no_order) for (uint i= 1; i < arg_count; i++) fields.push_back(args[i]); concat_ws= new (thd->mem_root) Item_func_concat_ws(thd, fields); + if (thd->is_fatal_error) + DBUG_RETURN(1); // OOM /* Above function used only to get value and do not need fix_fields for it: Item_string - basic constant @@ -5905,10 +5915,11 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (master) { join_key= master->join_key= join_key | master->join_key; - master->init_search(thd, no_order); + if (master->init_search(thd, no_order)) + DBUG_RETURN(1); ft_handler= master->ft_handler; join_key= master->join_key; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } String *ft_tmp= 0; @@ -5923,8 +5934,9 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (ft_tmp->charset() != cmp_collation.collation) { uint dummy_errors; - search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(), - cmp_collation.collation, &dummy_errors); + if (search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(), + cmp_collation.collation, &dummy_errors)) + DBUG_RETURN(1); ft_tmp= &search_value; } @@ -5939,7 +5951,7 @@ void Item_func_match::init_search(THD *thd, bool no_order) if (join_key) table->file->ft_handler=ft_handler; - DBUG_VOID_RETURN; + DBUG_RETURN(0); } @@ -6209,39 +6221,41 @@ longlong Item_func_bit_xor::val_int() */ -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, - LEX_CSTRING component) +Item *get_system_var(THD *thd, enum_var_type var_type, + const LEX_CSTRING *name, + const LEX_CSTRING *component) { sys_var *var; - LEX_CSTRING *base_name, *component_name; + LEX_CSTRING base_name, component_name; - if (component.str) + if (component->str) { - base_name= &component; - component_name= &name; + base_name= *component; + component_name= *name; } else { - base_name= &name; - component_name= &component; // Empty string + base_name= *name; + component_name= *component; // Empty string } - if (!(var= find_sys_var(thd, base_name->str, base_name->length))) + if (!(var= find_sys_var(thd, base_name.str, base_name.length))) return 0; - if (component.str) + if (component->str) { if (!var->is_struct()) { - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name->str); + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name.str); return 0; } } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - set_if_smaller(component_name->length, MAX_SYS_VAR_LENGTH); + set_if_smaller(component_name.length, MAX_SYS_VAR_LENGTH); - return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type, component_name, - NULL, 0); + return new (thd->mem_root) Item_func_get_system_var(thd, var, var_type, + &component_name, + NULL, 0); } @@ -6258,35 +6272,24 @@ longlong Item_func_row_count::val_int() Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name): - Item_func(thd), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL) + Item_func(thd), Item_sp(thd, context_arg, name) { maybe_null= 1; - dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); - dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg, List<Item> &list): - Item_func(thd, list), context(context_arg), m_name(name_arg), m_sp(NULL), - sp_result_field(NULL) + Item_func(thd, list), Item_sp(thd, context_arg, name_arg) { maybe_null= 1; - dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); - dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } void Item_func_sp::cleanup() { - if (sp_result_field) - { - delete sp_result_field; - sp_result_field= NULL; - } - m_sp= NULL; - dummy_table->alias.free(); + Item_sp::cleanup(); Item_func::cleanup(); } @@ -6294,25 +6297,7 @@ const char * Item_func_sp::func_name() const { THD *thd= current_thd; - /* Calculate length to avoid reallocation of string for sure */ - uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + - m_name->m_name.length)*2 + //characters*quoting - 2 + // ` and ` - (m_name->m_explicit_name ? - 3 : 0) + // '`', '`' and '.' for the db - 1 + // end of string - ALIGN_SIZE(1)); // to avoid String reallocation - String qname((char *)alloc_root(thd->mem_root, len), len, - system_charset_info); - - qname.length(0); - if (m_name->m_explicit_name) - { - append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length); - qname.append('.'); - } - append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length); - return qname.c_ptr_safe(); + return Item_sp::func_name(thd); } @@ -6326,75 +6311,6 @@ void my_missing_function_error(const LEX_CSTRING &token, const char *func_name) /** - @brief Initialize the result field by creating a temporary dummy table - and assign it to a newly created field object. Meta data used to - create the field is fetched from the sp_head belonging to the stored - proceedure found in the stored procedure functon cache. - - @note This function should be called from fix_fields to init the result - field. It is some what related to Item_field. - - @see Item_field - - @param thd A pointer to the session and thread context. - - @return Function return error status. - @retval TRUE is returned on an error - @retval FALSE is returned on success. -*/ - -bool -Item_func_sp::init_result_field(THD *thd, sp_head *sp) -{ - TABLE_SHARE *share; - DBUG_ENTER("Item_func_sp::init_result_field"); - - DBUG_ASSERT(m_sp == NULL); - DBUG_ASSERT(sp_result_field == NULL); - - if (!(m_sp= sp)) - { - my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); - context->process_error(thd); - DBUG_RETURN(TRUE); - } - - /* - A Field need to be attached to a Table. - Below we "create" a dummy table by initializing - the needed pointers. - */ - - share= dummy_table->s; - dummy_table->alias.set("", 0, table_alias_charset); - dummy_table->maybe_null = maybe_null; - dummy_table->in_use= thd; - dummy_table->copy_blobs= TRUE; - share->table_cache_key= empty_clex_str; - share->table_name= empty_clex_str; - - if (!(sp_result_field= m_sp->create_result_field(max_length, &name, dummy_table))) - { - DBUG_RETURN(TRUE); - } - - if (sp_result_field->pack_length() > sizeof(result_buf)) - { - void *tmp; - if (!(tmp= thd->alloc(sp_result_field->pack_length()))) - DBUG_RETURN(TRUE); - sp_result_field->move_field((uchar*) tmp); - } - else - sp_result_field->move_field(result_buf); - - sp_result_field->null_ptr= (uchar *) &null_value; - sp_result_field->null_bit= 1; - DBUG_RETURN(FALSE); -} - - -/** @note Deterministic stored procedures are considered inexpensive. Consequently such procedures may be evaluated during optimization, @@ -6428,95 +6344,11 @@ void Item_func_sp::fix_length_and_dec() } -/** - @brief Execute function & store value in field. - - @return Function returns error status. - @retval FALSE on success. - @retval TRUE if an error occurred. -*/ - bool Item_func_sp::execute() { - THD *thd= current_thd; - /* Execute function and store the return value in the field. */ - - if (execute_impl(thd)) - { - null_value= 1; - context->process_error(thd); - if (thd->killed) - thd->send_kill_message(); - return TRUE; - } - - /* Check that the field (the value) is not NULL. */ - - null_value= sp_result_field->is_null(); - - return null_value; -} - - -/** - @brief Execute function and store the return value in the field. - - @note This function was intended to be the concrete implementation of - the interface function execute. This was never realized. - - @return The error state. - @retval FALSE on success - @retval TRUE if an error occurred. -*/ -bool -Item_func_sp::execute_impl(THD *thd) -{ - bool err_status= TRUE; - Sub_statement_state statement_state; - Security_context *save_security_ctx= thd->security_ctx; - enum enum_sp_data_access access= - (m_sp->daccess() == SP_DEFAULT_ACCESS) ? - SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess(); - - DBUG_ENTER("Item_func_sp::execute_impl"); - - if (context->security_ctx) - { - /* Set view definer security context */ - thd->security_ctx= context->security_ctx; - } - if (sp_check_access(thd)) - goto error; - - /* - Throw an error if a non-deterministic function is called while - statement-based replication (SBR) is active. - */ - - if (!m_sp->detistic() && !trust_function_creators && - (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) && - (mysql_bin_log.is_open() && - thd->variables.binlog_format == BINLOG_FORMAT_STMT)) - { - my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0)); - goto error; - } - - /* - Disable the binlogging if this is not a SELECT statement. If this is a - SELECT, leave binlogging on, so execute_function() code writes the - function call into binlog. - */ - thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); - err_status= m_sp->execute_function(thd, args, arg_count, sp_result_field); - thd->restore_sub_statement_state(&statement_state); - -error: - thd->security_ctx= save_security_ctx; - - DBUG_RETURN(err_status); + return Item_sp::execute(current_thd, &null_value, args, arg_count); } @@ -6581,29 +6413,6 @@ longlong Item_func_sqlcode::val_int() } -/** - @brief Checks if requested access to function can be granted to user. - If function isn't found yet, it searches function first. - If function can't be found or user don't have requested access - error is raised. - - @param thd thread handler - - @return Indication if the access was granted or not. - @retval FALSE Access is granted. - @retval TRUE Requested access can't be granted or function doesn't exists. - -*/ - -bool -Item_func_sp::sp_check_access(THD *thd) -{ - DBUG_ENTER("Item_func_sp::sp_check_access"); - DBUG_ASSERT(m_sp); - DBUG_RETURN(m_sp->check_execute_access(thd)); -} - - bool Item_func_sp::fix_fields(THD *thd, Item **ref) { @@ -6628,8 +6437,8 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) whether to return "Access denied" or "Routine does not exist". */ res= sp ? sp->check_execute_access(thd) : - check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, - m_name->m_name.str, + check_routine_access(thd, EXECUTE_ACL, &m_name->m_db, + &m_name->m_name, &sp_handler_function, false); thd->security_ctx= save_security_ctx; @@ -6640,20 +6449,66 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) } } + + /* Custom aggregates are transformed into an Item_sum_sp. We can not do this + earlier as we have no way of knowing what kind of Item we should create + when parsing the query. + + TODO(cvicentiu): See if this limitation can be lifted. + */ + + DBUG_ASSERT(m_sp == NULL); + if (!(m_sp= sp)) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + context->process_error(thd); + DBUG_RETURN(TRUE); + } + /* - We must call init_result_field before Item_func::fix_fields() + We must call init_result_field before Item_func::fix_fields() to make m_sp and result_field members available to fix_length_and_dec(), which is called from Item_func::fix_fields(). */ - res= init_result_field(thd, sp); + res= init_result_field(thd, max_length, maybe_null, &null_value, &name); if (res) - DBUG_RETURN(res); + DBUG_RETURN(TRUE); + + if (m_sp->agg_type() == GROUP_AGGREGATE) + { + List<Item> list; + list.empty(); + for (uint i=0; i < arg_count; i++) + list.push_back(*(args+i)); + + Item_sum_sp *item_sp; + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (arg_count) + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp, list); + else + item_sp= new (thd->mem_root) Item_sum_sp(thd, context, m_name, sp); + + if (arena) + thd->restore_active_arena(arena, &backup); + if (!item_sp) + DBUG_RETURN(TRUE); + *ref= item_sp; + item_sp->name= name; + bool err= item_sp->fix_fields(thd, ref); + if (err) + DBUG_RETURN(TRUE); + + list.empty(); + DBUG_RETURN(FALSE); + } res= Item_func::fix_fields(thd, ref); if (res) - DBUG_RETURN(res); + DBUG_RETURN(TRUE); if (thd->lex->is_view_context_analysis()) { @@ -6810,7 +6665,7 @@ void Item_func_last_value::fix_length_and_dec() void Cursor_ref::print_func(String *str, const char *func_name) { - append_identifier(current_thd, str, m_cursor_name.str, m_cursor_name.length); + append_identifier(current_thd, str, &m_cursor_name); str->append(func_name); } @@ -6867,14 +6722,22 @@ longlong Item_func_nextval::val_int() longlong value; int error; const char *key; - TABLE *table= table_list->table; uint length= get_table_def_key(table_list, &key); - THD *thd= table->in_use; + THD *thd; SEQUENCE_LAST_VALUE *entry; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); - DBUG_ASSERT(table && table->s->sequence); DBUG_ENTER("Item_func_nextval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } if (table->s->tmp_table != NO_TMP_TABLE) { @@ -6924,9 +6787,10 @@ longlong Item_func_nextval::val_int() void Item_func_nextval::print(String *str, enum_query_type query_type) { char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; - const char *d_name= table_list->db, *t_name= table_list->table_name; - bool use_db_name= d_name && d_name[0]; - THD *thd= current_thd; + LEX_CSTRING d_name= table_list->db; + LEX_CSTRING t_name= table_list->table_name; + bool use_db_name= d_name.str && d_name.str[0]; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); @@ -6938,23 +6802,23 @@ void Item_func_nextval::print(String *str, enum_query_type query_type) if (lower_case_table_names > 0) { - strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); - my_casedn_str(files_charset_info, t_name_buff); - t_name= t_name_buff; + strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1); + t_name.length= my_casedn_str(files_charset_info, t_name_buff); + t_name.str= t_name_buff; if (use_db_name) { - strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); - my_casedn_str(files_charset_info, d_name_buff); - d_name= d_name_buff; + strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1); + d_name.length= my_casedn_str(files_charset_info, d_name_buff); + d_name.str= d_name_buff; } } if (use_db_name) { - append_identifier(thd, str, d_name, (uint)strlen(d_name)); + append_identifier(thd, str, &d_name); str->append('.'); } - append_identifier(thd, str, t_name, (uint) strlen(t_name)); + append_identifier(thd, str, &t_name); str->append(')'); } @@ -6966,12 +6830,14 @@ longlong Item_func_lastval::val_int() const char *key; SEQUENCE_LAST_VALUE *entry; uint length= get_table_def_key(table_list, &key); - THD *thd= table_list->table->in_use; + THD *thd; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); DBUG_ENTER("Item_func_lastval::val_int"); + update_table(); + thd= table->in_use; - if (table_list->table->s->tmp_table != NO_TMP_TABLE) + if (table->s->tmp_table != NO_TMP_TABLE) { /* Temporary tables has an extra \0 at end to distinguish it from @@ -6990,7 +6856,7 @@ longlong Item_func_lastval::val_int() null_value= 1; DBUG_RETURN(0); } - if (entry->check_version(table_list->table)) + if (entry->check_version(table)) { /* Table droped and re-created, remove current version */ my_hash_delete(&thd->sequences, (uchar*) entry); @@ -7015,10 +6881,20 @@ longlong Item_func_setval::val_int() { longlong value; int error; - TABLE *table= table_list->table; - DBUG_ASSERT(table && table->s->sequence); + THD *thd; DBUG_ENTER("Item_func_setval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } + value= nextval; error= table->s->sequence->set_value(table, nextval, round, is_used); if (error) @@ -7035,9 +6911,10 @@ longlong Item_func_setval::val_int() void Item_func_setval::print(String *str, enum_query_type query_type) { char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; - const char *d_name= table_list->db, *t_name= table_list->table_name; - bool use_db_name= d_name && d_name[0]; - THD *thd= table_list->table->in_use; + LEX_CSTRING d_name= table_list->db; + LEX_CSTRING t_name= table_list->table_name; + bool use_db_name= d_name.str && d_name.str[0]; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); @@ -7049,23 +6926,23 @@ void Item_func_setval::print(String *str, enum_query_type query_type) if (lower_case_table_names > 0) { - strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); - my_casedn_str(files_charset_info, t_name_buff); - t_name= t_name_buff; + strmake(t_name_buff, t_name.str, MAX_ALIAS_NAME-1); + t_name.length= my_casedn_str(files_charset_info, t_name_buff); + t_name.str= t_name_buff; if (use_db_name) { - strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); - my_casedn_str(files_charset_info, d_name_buff); - d_name= d_name_buff; + strmake(d_name_buff, d_name.str, MAX_ALIAS_NAME-1); + d_name.length= my_casedn_str(files_charset_info, d_name_buff); + d_name.str= d_name_buff; } } if (use_db_name) { - append_identifier(thd, str, d_name, (uint)strlen(d_name)); + append_identifier(thd, str, &d_name); str->append('.'); } - append_identifier(thd, str, t_name, (uint) strlen(t_name)); + append_identifier(thd, str, &t_name); str->append(','); str->append_longlong(nextval); str->append(','); diff --git a/sql/item_func.h b/sql/item_func.h index adc5c238abd..b10223289b9 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -30,6 +30,9 @@ extern "C" /* Bug in BSDI include file */ } #endif +#include "sql_udf.h" // udf_handler +#include "my_decimal.h" // string2my_decimal + class Item_func :public Item_func_or_sum { @@ -823,8 +826,8 @@ public: { return Cursor_ref::print_func(str, func_name()); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cursor_rowcount>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cursor_rowcount>(thd, this); } }; @@ -843,8 +846,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_connection_id>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_connection_id>(thd, this); } }; @@ -900,8 +903,8 @@ public: virtual void print(String *str, enum_query_type query_type); uint decimal_precision() const { return args[0]->decimal_precision(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_signed>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_signed>(thd, this); } }; @@ -931,8 +934,8 @@ public: } uint decimal_precision() const { return max_length; } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unsigned>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unsigned>(thd, this); } }; @@ -963,8 +966,8 @@ public: const char *func_name() const { return "decimal_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_decimal_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_decimal_typecast>(thd, this); } }; @@ -986,8 +989,32 @@ public: const char *func_name() const { return "double_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_double_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_double_typecast>(thd, this); } +}; + + +class Item_longlong_typecast :public Item_int_func +{ +public: + Item_longlong_typecast(THD *thd, Item *a): Item_int_func(thd, a) + { + } + const char *func_name() const { return "cast_as_longlong"; } + const char *cast_type() const { return "longlong"; } + const Type_handler *type_handler() const { return &type_handler_longlong; } + longlong val_int() + { + return args[0]->val_int(); + } + void fix_length_and_dec_generic() {} + void fix_length_and_dec() + { + args[0]->type_handler()->Item_longlong_typecast_fix_length_and_dec(this); + } + bool need_parentheses_in_default() { return true; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_longlong_typecast>(thd, this); } }; @@ -1013,8 +1040,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_plus>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_plus>(thd, this); } }; class Item_func_minus :public Item_func_additive_op @@ -1044,8 +1071,8 @@ public: Item_func_additive_op::fix_length_and_dec_int(); fix_unsigned_flag(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_minus>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_minus>(thd, this); } }; @@ -1063,8 +1090,8 @@ public: void fix_length_and_dec(); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_mul>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_mul>(thd, this); } }; @@ -1082,8 +1109,8 @@ public: void fix_length_and_dec_double(); void fix_length_and_dec_int(); void result_precision(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_div>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_div>(thd, this); } }; @@ -1106,8 +1133,8 @@ public: bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_int_div>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_int_div>(thd, this); } }; @@ -1141,8 +1168,8 @@ public: } bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_mod>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_mod>(thd, this); } }; @@ -1167,8 +1194,8 @@ public: void fix_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_neg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_neg>(thd, this); } }; @@ -1184,8 +1211,8 @@ public: void fix_length_and_dec_double(); void fix_length_and_dec_decimal(); void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_abs>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_abs>(thd, this); } }; // A class to handle logarithmic and trigonometric functions @@ -1210,8 +1237,8 @@ public: Item_func_exp(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "exp"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_exp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_exp>(thd, this); } }; @@ -1221,8 +1248,8 @@ public: Item_func_ln(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "ln"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ln>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ln>(thd, this); } }; @@ -1233,8 +1260,8 @@ public: Item_func_log(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "log"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log>(thd, this); } }; @@ -1244,8 +1271,8 @@ public: Item_func_log2(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "log2"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log2>(thd, this); } }; @@ -1255,8 +1282,8 @@ public: Item_func_log10(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "log10"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_log10>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_log10>(thd, this); } }; @@ -1266,8 +1293,8 @@ public: Item_func_sqrt(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "sqrt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqrt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqrt>(thd, this); } }; @@ -1277,8 +1304,8 @@ public: Item_func_pow(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "pow"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_pow>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_pow>(thd, this); } }; @@ -1288,8 +1315,8 @@ public: Item_func_acos(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "acos"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_acos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_acos>(thd, this); } }; class Item_func_asin :public Item_dec_func @@ -1298,8 +1325,8 @@ public: Item_func_asin(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "asin"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_asin>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_asin>(thd, this); } }; class Item_func_atan :public Item_dec_func @@ -1309,8 +1336,8 @@ public: Item_func_atan(THD *thd, Item *a, Item *b): Item_dec_func(thd, a, b) {} double val_real(); const char *func_name() const { return "atan"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_atan>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_atan>(thd, this); } }; class Item_func_cos :public Item_dec_func @@ -1319,8 +1346,8 @@ public: Item_func_cos(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "cos"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cos>(thd, this); } }; class Item_func_sin :public Item_dec_func @@ -1329,8 +1356,8 @@ public: Item_func_sin(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "sin"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sin>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sin>(thd, this); } }; class Item_func_tan :public Item_dec_func @@ -1339,8 +1366,8 @@ public: Item_func_tan(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "tan"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_tan>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_tan>(thd, this); } }; class Item_func_cot :public Item_dec_func @@ -1349,8 +1376,8 @@ public: Item_func_cot(THD *thd, Item *a): Item_dec_func(thd, a) {} double val_real(); const char *func_name() const { return "cot"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_cot>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_cot>(thd, this); } }; @@ -1372,8 +1399,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ceiling>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ceiling>(thd, this); } }; @@ -1385,8 +1412,8 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_floor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_floor>(thd, this); } }; /* This handles round and truncate */ @@ -1410,8 +1437,8 @@ public: { args[0]->type_handler()->Item_func_round_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_round>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_round>(thd, this); } }; @@ -1435,8 +1462,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rand>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rand>(thd, this); } private: void seed_random (Item * val); }; @@ -1452,8 +1479,8 @@ public: uint decimal_precision() const { return 1; } void fix_length_and_dec() { fix_char_length(2); } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sign>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sign>(thd, this); } }; @@ -1471,8 +1498,8 @@ public: const char *func_name() const { return name; } void fix_length_and_dec() { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_units>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_units>(thd, this); } }; @@ -1567,8 +1594,8 @@ class Item_func_min :public Item_func_min_max public: Item_func_min(THD *thd, List<Item> &list): Item_func_min_max(thd, list, 1) {} const char *func_name() const { return "least"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_min>(thd, this); } }; class Item_func_max :public Item_func_min_max @@ -1576,8 +1603,8 @@ class Item_func_max :public Item_func_min_max public: Item_func_max(THD *thd, List<Item> &list): Item_func_min_max(thd, list, -1) {} const char *func_name() const { return "greatest"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_max>(thd, this); } }; @@ -1612,8 +1639,8 @@ public: /* The item could be a NULL constant. */ null_value= args[0]->is_null(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rollup_const>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rollup_const>(thd, this); } }; @@ -1634,8 +1661,8 @@ public: Item_func_octet_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); const char *func_name() const { return "octet_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_octet_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_octet_length>(thd, this); } }; class Item_func_bit_length :public Item_longlong_func @@ -1649,8 +1676,8 @@ public: } longlong val_int(); const char *func_name() const { return "bit_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_length>(thd, this); } }; class Item_func_char_length :public Item_long_func_length @@ -1660,8 +1687,8 @@ public: Item_func_char_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); const char *func_name() const { return "char_length"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_char_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_char_length>(thd, this); } }; class Item_func_coercibility :public Item_long_func @@ -1681,8 +1708,8 @@ public: Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { return this; } bool const_item() const { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_coercibility>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_coercibility>(thd, this); } }; @@ -1714,8 +1741,8 @@ public: agg_arg_charsets_for_comparison(cmp_collation, args, 2); } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_locate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_locate>(thd, this); } }; @@ -1729,8 +1756,8 @@ public: longlong val_int(); const char *func_name() const { return "field"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_field>(thd, this); } }; @@ -1744,8 +1771,8 @@ public: longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ascii>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ascii>(thd, this); } }; class Item_func_ord :public Item_long_func @@ -1758,8 +1785,8 @@ public: void fix_length_and_dec() { fix_char_length(7); } longlong val_int(); const char *func_name() const { return "ord"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ord>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ord>(thd, this); } }; class Item_func_find_in_set :public Item_long_func @@ -1776,8 +1803,8 @@ public: longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_find_in_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_find_in_set>(thd, this); } }; /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ @@ -1805,8 +1832,8 @@ public: longlong val_int(); const char *func_name() const { return "|"; } enum precedence precedence() const { return BITOR_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_or>(thd, this); } }; class Item_func_bit_and :public Item_func_bit @@ -1816,8 +1843,8 @@ public: longlong val_int(); const char *func_name() const { return "&"; } enum precedence precedence() const { return BITAND_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_and>(thd, this); } }; class Item_func_bit_count :public Item_long_func @@ -1829,8 +1856,8 @@ public: longlong val_int(); const char *func_name() const { return "bit_count"; } void fix_length_and_dec() { max_length=2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_count>(thd, this); } }; class Item_func_shift_left :public Item_func_bit @@ -1840,8 +1867,8 @@ public: longlong val_int(); const char *func_name() const { return "<<"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_shift_left>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_shift_left>(thd, this); } }; class Item_func_shift_right :public Item_func_bit @@ -1851,8 +1878,8 @@ public: longlong val_int(); const char *func_name() const { return ">>"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_shift_right>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_shift_right>(thd, this); } }; class Item_func_bit_neg :public Item_func_bit @@ -1867,8 +1894,8 @@ public: str->append(func_name()); args[0]->print_parenthesised(str, query_type, precedence()); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_neg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_neg>(thd, this); } }; @@ -1892,8 +1919,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_insert_id>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_insert_id>(thd, this); } }; @@ -1916,8 +1943,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_benchmark>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_benchmark>(thd, this); } }; @@ -1943,8 +1970,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sleep>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sleep>(thd, this); } }; @@ -2075,8 +2102,8 @@ class Item_func_udf_float :public Item_udf_func String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_float>(thd, this); } }; @@ -2093,8 +2120,8 @@ public: String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals= 0; max_length= 21; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_int>(thd, this); } }; @@ -2111,8 +2138,8 @@ public: String *val_str(String *str); const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_decimal>(thd, this); } }; @@ -2150,8 +2177,8 @@ public: } const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_udf_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_udf_str>(thd, this); } }; #else /* Dummy functions to get sql_yacc.cc compiled */ @@ -2233,8 +2260,8 @@ class Item_func_get_lock :public Item_long_func { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_lock>(thd, this); } }; class Item_func_release_lock :public Item_long_func @@ -2257,8 +2284,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_release_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_release_lock>(thd, this); } }; /* replication functions */ @@ -2288,8 +2315,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_master_pos_wait>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_master_pos_wait>(thd, this); } }; @@ -2313,8 +2340,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_master_gtid_wait>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_master_gtid_wait>(thd, this); } }; @@ -2397,7 +2424,7 @@ public: String *str_result(String *str); my_decimal *val_decimal_result(my_decimal *); bool is_null_result(); - bool update_hash(void *ptr, uint length, enum Item_result type, + bool update_hash(void *ptr, size_t length, enum Item_result type, CHARSET_INFO *cs, bool unsigned_arg); bool send(Protocol *protocol, st_value *buffer); void make_field(THD *thd, Send_field *tmp_field); @@ -2423,8 +2450,8 @@ public: bool register_field_in_bitmap(void *arg); bool set_entry(THD *thd, bool create_if_not_exists); void cleanup(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_set_user_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_set_user_var>(thd, this); } }; @@ -2451,8 +2478,8 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_user_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_user_var>(thd, this); } private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); @@ -2475,14 +2502,15 @@ public: */ class Item_user_var_as_out_param :public Item { - LEX_CSTRING name; + LEX_CSTRING org_name; user_var_entry *entry; public: Item_user_var_as_out_param(THD *thd, const LEX_CSTRING *a) - :Item(thd), name(*a) + :Item(thd) { DBUG_ASSERT(a->length < UINT_MAX32); - set_name(thd, a->str, (uint) a->length, system_charset_info); + org_name= *a; + set_name(thd, a->str, a->length, system_charset_info); } /* We should return something different from FIELD_ITEM here */ enum Type type() const { return STRING_ITEM;} @@ -2497,8 +2525,8 @@ public: void set_null_value(CHARSET_INFO* cs); void set_value(const char *str, uint length, CHARSET_INFO* cs); const Type_handler *type_handler() const { return &type_handler_double; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_user_var_as_out_param>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_user_var_as_out_param>(thd, this); } }; @@ -2556,8 +2584,8 @@ public: void cleanup(); bool check_vcol_func_processor(void *arg); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_system_var>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_system_var>(thd, this); } }; @@ -2606,14 +2634,14 @@ public: virtual void print(String *str, enum_query_type query_type); bool fix_index(); - void init_search(THD *thd, bool no_order); + bool init_search(THD *thd, bool no_order); bool check_vcol_func_processor(void *arg) { return mark_unsupported_function("match ... against()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_match>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_match>(thd, this); } + Item *build_clone(THD *thd) { return 0; } private: /** Check whether storage engine for given table, @@ -2658,8 +2686,8 @@ public: longlong val_int(); const char *func_name() const { return "^"; } enum precedence precedence() const { return BITXOR_PRECEDENCE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_bit_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_bit_xor>(thd, this); } }; class Item_func_is_free_lock :public Item_long_func @@ -2676,8 +2704,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_free_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_free_lock>(thd, this); } }; class Item_func_is_used_lock :public Item_long_func @@ -2694,8 +2722,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_used_lock>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_used_lock>(thd, this); } }; @@ -2743,8 +2771,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_row_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_row_count>(thd, this); } }; @@ -2754,26 +2782,12 @@ public: * */ -class sp_head; -class sp_name; -struct st_sp_security_context; - -class Item_func_sp :public Item_func +class Item_func_sp :public Item_func, + public Item_sp { private: - Name_resolution_context *context; - sp_name *m_name; - mutable sp_head *m_sp; - TABLE *dummy_table; - uchar result_buf[64]; - /* - The result field of the concrete stored function. - */ - Field *sp_result_field; bool execute(); - bool execute_impl(THD *thd); - bool init_result_field(THD *thd, sp_head *sp); protected: bool is_expensive_processor(void *arg) @@ -2865,7 +2879,6 @@ public: virtual bool change_context_processor(void *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } - bool sp_check_access(THD * thd); virtual enum Functype functype() const { return FUNC_SP; } bool fix_fields(THD *thd, Item **ref); @@ -2886,11 +2899,11 @@ public: { return TRUE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sp>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) + 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, mem_root); + Item_func_sp *clone= (Item_func_sp *) Item_func::build_clone(thd); if (clone) clone->sp_result_field= NULL; return clone; @@ -2914,8 +2927,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_found_rows>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_found_rows>(thd, this); } }; @@ -2933,8 +2946,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, this); } }; @@ -2957,8 +2970,8 @@ public: maybe_null= null_value= false; max_length= 11; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqlcode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqlcode>(thd, this); } }; @@ -2977,8 +2990,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uuid_short>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uuid_short>(thd, this); } }; @@ -3008,8 +3021,8 @@ public: Item_func::update_used_tables(); maybe_null= last_value->maybe_null; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_value>(thd, this); } }; @@ -3019,9 +3032,10 @@ class Item_func_nextval :public Item_longlong_func { protected: TABLE_LIST *table_list; + TABLE *table; public: - Item_func_nextval(THD *thd, TABLE_LIST *table): - Item_longlong_func(thd), table_list(table) {} + Item_func_nextval(THD *thd, TABLE_LIST *table_list_arg): + Item_longlong_func(thd), table_list(table_list_arg) {} longlong val_int(); const char *func_name() const { return "nextval"; } void fix_length_and_dec() @@ -3030,14 +3044,31 @@ public: max_length= MAX_BIGINT_WIDTH; maybe_null= 1; /* In case of errors */ } + /* + update_table() function must be called during the value function + as in case of DEFAULT the sequence table may not yet be open + while fix_fields() are called + */ + void update_table() + { + if (!(table= table_list->table)) + { + /* + If nextval was used in DEFAULT then next_local points to + the table_list used by to open the sequence table + */ + table= table_list->next_local->table; + } + } bool const_item() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_nextval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_nextval>(thd, this); } void print(String *str, enum_query_type query_type); bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(func_name(), "()", arg, - VCOL_NON_DETERMINISTIC); + (VCOL_NON_DETERMINISTIC | + VCOL_NOT_VIRTUAL)); } }; @@ -3047,12 +3078,12 @@ public: class Item_func_lastval :public Item_func_nextval { public: - Item_func_lastval(THD *thd, TABLE_LIST *table): - Item_func_nextval(thd, table) {} + Item_func_lastval(THD *thd, TABLE_LIST *table_list_arg): + Item_func_nextval(thd, table_list_arg) {} longlong val_int(); const char *func_name() const { return "lastval"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lastval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lastval>(thd, this); } }; @@ -3064,21 +3095,21 @@ class Item_func_setval :public Item_func_nextval ulonglong round; bool is_used; public: - Item_func_setval(THD *thd, TABLE_LIST *table, longlong nextval_arg, + Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, longlong nextval_arg, ulonglong round_arg, bool is_used_arg) - : Item_func_nextval(thd, table), + : Item_func_nextval(thd, table_list_arg), nextval(nextval_arg), round(round_arg), is_used(is_used_arg) {} longlong val_int(); const char *func_name() const { return "setval"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_setval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_setval>(thd, this); } }; -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, - LEX_CSTRING component); +Item *get_system_var(THD *thd, enum_var_type var_type, + const LEX_CSTRING *name, const LEX_CSTRING *component); extern bool check_reserved_words(const LEX_CSTRING *name); Item *find_date_time_item(Item **args, uint nargs, uint col); double my_double_round(double value, longlong dec, bool dec_unsigned, diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 9fd246bbe4c..aee44a7a01f 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index d332d067c37..8101433abb5 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -202,8 +202,8 @@ public: Item_geometry_func(thd, a, srid) {} const char *func_name() const { return "st_geometryfromtext"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_text>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_text>(thd, this); } }; class Item_func_geometry_from_wkb: public Item_geometry_func @@ -219,8 +219,8 @@ public: Item_geometry_func(thd, a, srid) {} const char *func_name() const { return "st_geometryfromwkb"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_wkb>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_wkb>(thd, this); } }; @@ -241,8 +241,8 @@ public: Item_geometry_func(thd, js, opt, srid) {} const char *func_name() const { return "st_geomfromgeojson"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_from_json>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_from_json>(thd, this); } }; @@ -254,8 +254,8 @@ public: const char *func_name() const { return "st_astext"; } String *val_str_ascii(String *); void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_wkt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_wkt>(thd, this); } }; class Item_func_as_wkb: public Item_binary_func_args_geometry @@ -273,8 +273,8 @@ public: max_length= (uint32) UINT_MAX32; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_wkb>(thd, this); } }; @@ -296,8 +296,8 @@ public: const char *func_name() const { return "st_asgeojson"; } void fix_length_and_dec(); String *val_str_ascii(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_as_geojson>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_as_geojson>(thd, this); } }; @@ -314,8 +314,8 @@ public: fix_length_and_charset(20, default_charset()); maybe_null= 1; }; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_geometry_type>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_geometry_type>(thd, this); } }; @@ -349,8 +349,8 @@ public: {} const char *func_name() const { return "st_convexhull"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_convexhull>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_convexhull>(thd, this); } }; @@ -362,8 +362,8 @@ public: const char *func_name() const { return "st_centroid"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_centroid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_centroid>(thd, this); } }; class Item_func_envelope: public Item_geometry_func_args_geometry @@ -374,8 +374,8 @@ public: const char *func_name() const { return "st_envelope"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_envelope>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_envelope>(thd, this); } }; @@ -408,8 +408,8 @@ public: :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_boundary"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_boundary>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_boundary>(thd, this); } }; @@ -424,8 +424,8 @@ public: const char *func_name() const { return "point"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_point>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_point>(thd, this); } }; class Item_func_spatial_decomp: public Item_geometry_func_args_geometry @@ -450,8 +450,8 @@ public: } } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_decomp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_decomp>(thd, this); } }; class Item_func_spatial_decomp_n: public Item_geometry_func_args_geometry @@ -483,8 +483,8 @@ public: } } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_decomp_n>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_decomp_n>(thd, this); } }; class Item_func_spatial_collection: public Item_geometry_func @@ -521,8 +521,8 @@ public: } const char *func_name() const { return "geometrycollection"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_collection>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_collection>(thd, this); } }; @@ -571,7 +571,7 @@ public: usable_tables, sargables, false); } bool need_parentheses_in_default() { return false; } - Item *build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *build_clone(THD *thd) { return 0; } }; @@ -583,8 +583,8 @@ public: { } longlong val_int(); const char *func_name() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_mbr_rel>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_mbr_rel>(thd, this); } }; @@ -599,8 +599,8 @@ public: { } longlong val_int(); const char *func_name() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_precise_rel>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_precise_rel>(thd, this); } }; @@ -622,8 +622,8 @@ public: longlong val_int(); const char *func_name() const { return "st_relate"; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_relate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_relate>(thd, this); } }; @@ -658,8 +658,8 @@ public: { Item_func::print(str, query_type); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_spatial_operation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_spatial_operation>(thd, this); } }; @@ -715,8 +715,8 @@ public: :Item_geometry_func_args_geometry(thd, obj, distance) {} const char *func_name() const { return "st_buffer"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_buffer>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_buffer>(thd, this); } }; @@ -729,8 +729,8 @@ public: const char *func_name() const { return "st_isempty"; } void fix_length_and_dec() { maybe_null= 1; } bool need_parentheses_in_default() { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isempty>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isempty>(thd, this); } }; class Item_func_issimple: public Item_long_func_args_geometry @@ -746,8 +746,8 @@ public: const char *func_name() const { return "st_issimple"; } void fix_length_and_dec() { decimals=0; max_length=2; } uint decimal_precision() const { return 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_issimple>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_issimple>(thd, this); } }; class Item_func_isclosed: public Item_long_func_args_geometry @@ -759,8 +759,8 @@ public: const char *func_name() const { return "st_isclosed"; } void fix_length_and_dec() { decimals=0; max_length=2; } uint decimal_precision() const { return 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isclosed>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isclosed>(thd, this); } }; class Item_func_isring: public Item_func_issimple @@ -769,8 +769,8 @@ public: Item_func_isring(THD *thd, Item *a): Item_func_issimple(thd, a) {} longlong val_int(); const char *func_name() const { return "st_isring"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_isring>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_isring>(thd, this); } }; class Item_func_dimension: public Item_long_func_args_geometry @@ -781,8 +781,8 @@ public: longlong val_int(); const char *func_name() const { return "st_dimension"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dimension>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dimension>(thd, this); } }; @@ -797,8 +797,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_x>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_x>(thd, this); } }; @@ -813,8 +813,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_y>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_y>(thd, this); } }; @@ -826,8 +826,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numgeometries"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numgeometries>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numgeometries>(thd, this); } }; @@ -839,8 +839,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numinteriorrings"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numinteriorring>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numinteriorring>(thd, this); } }; @@ -852,8 +852,8 @@ public: longlong val_int(); const char *func_name() const { return "st_numpoints"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_numpoints>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_numpoints>(thd, this); } }; @@ -868,8 +868,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_area>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_area>(thd, this); } }; @@ -886,8 +886,8 @@ public: Item_real_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_glength>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_glength>(thd, this); } }; @@ -899,8 +899,8 @@ public: longlong val_int(); const char *func_name() const { return "srid"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_srid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_srid>(thd, this); } }; @@ -916,8 +916,8 @@ public: :Item_real_func_args_geometry_geometry(thd, a, b) {} double val_real(); const char *func_name() const { return "st_distance"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_distance>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_distance>(thd, this); } }; @@ -933,8 +933,8 @@ public: const char *func_name() const { return "st_pointonsurface"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_pointonsurface>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_pointonsurface>(thd, this); } }; @@ -951,8 +951,8 @@ class Item_func_gis_debug: public Item_long_func { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_gis_debug>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_gis_debug>(thd, this); } }; #endif diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc index ff671536002..d4788a39d5e 100644 --- a/sql/item_inetfunc.cc +++ b/sql/item_inetfunc.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "item_inetfunc.h" #include "my_net.h" diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h index bd0a95b5270..d934cef43dd 100644 --- a/sql/item_inetfunc.h +++ b/sql/item_inetfunc.h @@ -39,8 +39,8 @@ public: maybe_null= 1; unsigned_flag= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet_aton>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet_aton>(thd, this); } }; @@ -61,8 +61,8 @@ public: fix_length_and_charset(3 * 8 + 7, default_charset()); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet_ntoa>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet_ntoa>(thd, this); } }; @@ -130,8 +130,8 @@ public: fix_length_and_charset(16, &my_charset_bin); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet6_aton>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet6_aton>(thd, this); } protected: virtual bool calc_value(const String *arg, String *buffer); @@ -164,8 +164,8 @@ public: maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_inet6_ntoa>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_inet6_ntoa>(thd, this); } protected: virtual bool calc_value(const String *arg, String *buffer); @@ -186,8 +186,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -208,8 +208,8 @@ public: public: virtual const char *func_name() const { return "is_ipv6"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv6>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv6>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -230,8 +230,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4_compat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4_compat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4_compat>(thd, this); } protected: virtual bool calc_value(const String *arg); @@ -252,8 +252,8 @@ public: public: virtual const char *func_name() const { return "is_ipv4_mapped"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_is_ipv4_mapped>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); } protected: virtual bool calc_value(const String *arg); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 069bdf2e079..4b2de7cf28b 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -14,7 +14,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" #include "item.h" diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 77e7588be25..354de69eee4 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -54,8 +54,8 @@ public: Item_bool_func::fix_length_and_dec(); maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_valid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_valid>(thd, this); } }; @@ -70,8 +70,8 @@ public: Item_bool_func(thd, js, i_path) {} const char *func_name() const { return "json_exists"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_exists>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_exists>(thd, this); } longlong val_int(); }; @@ -89,8 +89,8 @@ public: void fix_length_and_dec(); String *val_str(String *); virtual bool check_and_get_value(json_engine_t *je, String *res, int *error); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_value>(thd, this); } }; @@ -102,8 +102,8 @@ public: bool is_json_type() { return true; } const char *func_name() const { return "json_query"; } bool check_and_get_value(json_engine_t *je, String *res, int *error); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_query>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_query>(thd, this); } }; @@ -117,8 +117,8 @@ public: const char *func_name() const { return "json_quote"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_quote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_quote>(thd, this); } }; @@ -132,8 +132,8 @@ public: const char *func_name() const { return "json_unquote"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_unquote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_unquote>(thd, this); } }; @@ -168,8 +168,8 @@ public: longlong val_int(); double val_real(); uint get_n_paths() const { return arg_count - 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_extract>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_extract>(thd, this); } }; @@ -187,8 +187,8 @@ public: const char *func_name() const { return "json_contains"; } void fix_length_and_dec(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_contains>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_contains>(thd, this); } }; @@ -210,8 +210,8 @@ public: void fix_length_and_dec(); void cleanup(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_contains_path>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_contains_path>(thd, this); } }; @@ -229,8 +229,8 @@ public: bool is_json_type() { return true; } void fix_length_and_dec(); const char *func_name() const { return "json_array"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array>(thd, this); } }; @@ -246,8 +246,8 @@ public: String *val_str(String *); uint get_n_paths() const { return arg_count/2; } const char *func_name() const { return "json_array_append"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array_append>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array_append>(thd, this); } }; @@ -258,8 +258,8 @@ public: Item_func_json_array_append(thd, list) {} String *val_str(String *); const char *func_name() const { return "json_array_insert"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_array_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_array_insert>(thd, this); } }; @@ -273,8 +273,8 @@ public: String *val_str(String *); bool is_json_type() { return true; } const char *func_name() const { return "json_object"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_object>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_object>(thd, this); } }; @@ -288,8 +288,8 @@ public: String *val_str(String *); bool is_json_type() { return true; } const char *func_name() const { return "json_merge"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_merge>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_merge>(thd, this); } }; @@ -311,8 +311,8 @@ public: const char *func_name() const { return "json_length"; } void fix_length_and_dec(); longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_length>(thd, this); } }; @@ -327,8 +327,8 @@ public: const char *func_name() const { return "json_depth"; } void fix_length_and_dec() { max_length= 10; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_depth>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_depth>(thd, this); } }; @@ -341,8 +341,8 @@ public: const char *func_name() const { return "json_type"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_type>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_type>(thd, this); } }; @@ -364,8 +364,8 @@ public: return mode_insert ? (mode_replace ? "json_set" : "json_insert") : "json_update"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_insert>(thd, this); } }; @@ -380,8 +380,8 @@ public: String *val_str(String *); uint get_n_paths() const { return arg_count - 1; } const char *func_name() const { return "json_remove"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_remove>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_remove>(thd, this); } }; @@ -397,8 +397,8 @@ public: const char *func_name() const { return "json_keys"; } void fix_length_and_dec(); String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_keys>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_keys>(thd, this); } }; @@ -422,8 +422,8 @@ public: void fix_length_and_dec(); String *val_str(String *); uint get_n_paths() const { return arg_count > 4 ? arg_count - 4 : 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_search>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_search>(thd, this); } }; @@ -451,8 +451,8 @@ public: String *val_str(String *str); String *val_json(String *str); bool is_json_type() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_json_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_json_format>(thd, this); } }; diff --git a/sql/item_row.cc b/sql/item_row.cc index c7e0f164af9..64794093bec 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -163,15 +163,15 @@ void Item_row::bring_value() } -Item* Item_row::build_clone(THD *thd, MEM_ROOT *mem_root) +Item* Item_row::build_clone(THD *thd) { - Item_row *copy= (Item_row *) get_copy(thd, mem_root); + Item_row *copy= (Item_row *) get_copy(thd); if (!copy) return 0; - copy->args= (Item**) alloc_root(mem_root, sizeof(Item*) * arg_count); + copy->args= (Item**) alloc_root(thd->mem_root, sizeof(Item*) * arg_count); for (uint i= 0; i < arg_count; i++) { - Item *arg_clone= args[i]->build_clone(thd, mem_root); + Item *arg_clone= args[i]->build_clone(thd); if (!arg_clone) return 0; copy->args[i]= arg_clone; diff --git a/sql/item_row.h b/sql/item_row.h index 83f6ba9c5b7..064cb0782b1 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -135,9 +135,9 @@ public: } bool check_vcol_func_processor(void *arg) {return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_row>(thd, mem_root, this); } - Item *build_clone(THD *thd, MEM_ROOT *mem_root); + Item *get_copy(THD *thd) + { return get_item_copy<Item_row>(thd, this); } + Item *build_clone(THD *thd); }; #endif /* ITEM_ROW_INCLUDED */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 64fc84a49e8..90d762c0191 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -31,7 +31,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> // HAVE_* +#include "mariadb.h" // HAVE_* #include "sql_priv.h" /* @@ -527,14 +527,14 @@ String *Item_func_decode_histogram::val_str(String *str) DBUG_ASSERT(0); } /* show delta with previous value */ - int size= my_snprintf(numbuf, sizeof(numbuf), + size_t size= my_snprintf(numbuf, sizeof(numbuf), representation_by_type[type], val - prev); str->append(numbuf, size); str->append(","); prev= val; } /* show delta with max */ - int size= my_snprintf(numbuf, sizeof(numbuf), + size_t size= my_snprintf(numbuf, sizeof(numbuf), representation_by_type[type], 1.0 - prev); str->append(numbuf, size); @@ -1564,7 +1564,7 @@ String *Item_str_conv::val_str(String *str) null_value=0; if (multiply == 1) { - uint len; + 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()); @@ -1573,7 +1573,7 @@ String *Item_str_conv::val_str(String *str) } else { - uint len= res->length() * multiply; + 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(), @@ -2296,13 +2296,13 @@ String *Item_func_database::val_str(String *str) { DBUG_ASSERT(fixed == 1); THD *thd= current_thd; - if (thd->db == NULL) + if (thd->db.str == NULL) { null_value= 1; return 0; } else - str->copy(thd->db, thd->db_length, system_charset_info); + str->copy(thd->db.str, thd->db.length, system_charset_info); null_value= 0; return str; } @@ -2321,7 +2321,7 @@ String *Item_func_sqlerrm::val_str(String *str) system_charset_info); return str; } - str->copy(C_STRING_WITH_LEN("normal, successful completition"), + str->copy(STRING_WITH_LEN("normal, successful completition"), system_charset_info); return str; } @@ -2838,7 +2838,7 @@ void Item_func_char::print(String *str, enum_query_type query_type) print_args(str, 0, query_type); if (collation.collation != &my_charset_bin) { - str->append(C_STRING_WITH_LEN(" using ")); + str->append(STRING_WITH_LEN(" using ")); str->append(collation.collation->csname); } str->append(')'); @@ -3531,10 +3531,10 @@ void Item_func_weight_string::fix_length_and_dec() */ if (!(max_length= result_length)) { - uint char_length; + size_t char_length; char_length= ((cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS) || !nweights) ? args[0]->max_char_length() : nweights * cs->levels_for_order; - max_length= cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen); + max_length= (uint32)cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen); } maybe_null= 1; } @@ -3545,7 +3545,7 @@ String *Item_func_weight_string::val_str(String *str) { String *res; CHARSET_INFO *cs= args[0]->collation.collation; - uint tmp_length, frm_length; + size_t tmp_length, frm_length; DBUG_ASSERT(fixed == 1); if (args[0]->result_type() != STRING_RESULT || @@ -3559,7 +3559,7 @@ String *Item_func_weight_string::val_str(String *str) */ if (!(tmp_length= result_length)) { - uint char_length; + size_t char_length; if (cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS) { /* @@ -3605,7 +3605,7 @@ String *Item_func_weight_string::val_str(String *str) frm_length= cs->coll->strnxfrm(cs, (uchar *) str->ptr(), tmp_length, - nweights ? nweights : tmp_length, + nweights ? nweights : (uint)tmp_length, (const uchar *) res->ptr(), res->length(), flags); DBUG_ASSERT(frm_length <= tmp_length); @@ -4426,6 +4426,9 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) case MYSQL_TYPE_GEOMETRY: type= DYN_COL_STRING; break; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + case MYSQL_TYPE_BLOB_COMPRESSED: + DBUG_ASSERT(0); } } if (type == DYN_COL_STRING && diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index a357c03bda5..08601b9d9d4 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -149,8 +149,8 @@ public: fix_length_and_charset(32, default_charset()); } const char *func_name() const { return "md5"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_md5>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_md5>(thd, this); } }; @@ -161,8 +161,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sha>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sha>(thd, this); } }; class Item_func_sha2 :public Item_str_ascii_checksum_func @@ -173,8 +173,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "sha2"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sha2>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sha2>(thd, this); } }; class Item_func_to_base64 :public Item_str_ascii_checksum_func @@ -186,8 +186,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "to_base64"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_base64>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_base64>(thd, this); } }; class Item_func_from_base64 :public Item_str_binary_checksum_func @@ -199,8 +199,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "from_base64"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_base64>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_base64>(thd, this); } }; #include <my_crypt.h> @@ -225,8 +225,8 @@ public: Item_aes_crypt(thd, a, b) {} void fix_length_and_dec(); const char *func_name() const { return "aes_encrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_aes_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_aes_encrypt>(thd, this); } }; class Item_func_aes_decrypt :public Item_aes_crypt @@ -236,8 +236,8 @@ public: Item_aes_crypt(thd, a, b) {} void fix_length_and_dec(); const char *func_name() const { return "aes_decrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_aes_decrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_aes_decrypt>(thd, this); } }; @@ -260,8 +260,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "concat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_concat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_concat>(thd, this); } }; @@ -280,9 +280,9 @@ public: { } String *val_str(String *); const char *func_name() const { return "concat_operator_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) + Item *get_copy(THD *thd) { - return get_item_copy<Item_func_concat_operator_oracle>(thd, mem_root, this); + return get_item_copy<Item_func_concat_operator_oracle>(thd, this); } }; @@ -300,8 +300,8 @@ public: maybe_null= 1; } const char *func_name() const { return "decode_histogram"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode_histogram>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode_histogram>(thd, this); } }; class Item_func_concat_ws :public Item_str_func @@ -313,8 +313,8 @@ public: void fix_length_and_dec(); const char *func_name() const { return "concat_ws"; } table_map not_null_tables() const { return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_concat_ws>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_concat_ws>(thd, this); } }; class Item_func_reverse :public Item_str_func @@ -325,8 +325,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "reverse"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_reverse>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_reverse>(thd, this); } }; @@ -340,8 +340,8 @@ public: void fix_length_and_dec(); String *val_str_internal(String *str, String *empty_string_for_null); const char *func_name() const { return "replace"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_replace>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_replace>(thd, this); } }; @@ -353,8 +353,8 @@ public: Item_func_replace(thd, org, find, replace) {} String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); }; const char *func_name() const { return "replace_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_replace_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_replace_oracle>(thd, this); } }; @@ -379,7 +379,7 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_replace"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0;} + Item *get_copy(THD *thd) { return 0;} }; @@ -401,7 +401,7 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); const char *func_name() const { return "regexp_substr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } }; @@ -415,8 +415,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "insert"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_insert>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_insert>(thd, this); } }; @@ -438,8 +438,8 @@ public: Item_func_lcase(THD *thd, Item *item): Item_str_conv(thd, item) {} const char *func_name() const { return "lcase"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lcase>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lcase>(thd, this); } }; class Item_func_ucase :public Item_str_conv @@ -448,8 +448,8 @@ public: Item_func_ucase(THD *thd, Item *item): Item_str_conv(thd, item) {} const char *func_name() const { return "ucase"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ucase>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ucase>(thd, this); } }; @@ -461,8 +461,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "left"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_left>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_left>(thd, this); } }; @@ -474,8 +474,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "right"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_right>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_right>(thd, this); } }; @@ -491,8 +491,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr>(thd, this); } }; class Item_func_substr_oracle :public Item_func_substr @@ -506,8 +506,8 @@ public: Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c): Item_func_substr(thd, a, b, c) {} const char *func_name() const { return "substr_oracle"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr_oracle>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr_oracle>(thd, this); } }; class Item_func_substr_index :public Item_str_func @@ -519,8 +519,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substring_index"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_substr_index>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_substr_index>(thd, this); } }; @@ -553,8 +553,8 @@ public: const char *func_name() const { return "trim"; } void print(String *str, enum_query_type query_type); virtual const char *mode_name() const { return "both"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_trim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_trim>(thd, this); } }; @@ -566,8 +566,8 @@ public: String *val_str(String *); const char *func_name() const { return "ltrim"; } const char *mode_name() const { return "leading"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_ltrim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_ltrim>(thd, this); } }; @@ -579,8 +579,8 @@ public: String *val_str(String *); const char *func_name() const { return "rtrim"; } const char *mode_name() const { return "trailing"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rtrim>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rtrim>(thd, this); } }; @@ -618,8 +618,8 @@ public: "password" : "old_password"); } static char *alloc(THD *thd, const char *password, size_t pass_len, enum PW_Alg al); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_password>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_password>(thd, this); } }; @@ -640,8 +640,8 @@ public: max_length = args[0]->max_length + 9; } const char *func_name() const { return "des_encrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_des_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_des_encrypt>(thd, this); } }; class Item_func_des_decrypt :public Item_str_binary_checksum_func @@ -662,8 +662,8 @@ public: max_length-= 9U; } const char *func_name() const { return "des_decrypt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_des_decrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_des_decrypt>(thd, this); } }; @@ -698,8 +698,8 @@ public: { return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_encrypt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_encrypt>(thd, this); } }; #include "sql_crypt.h" @@ -718,8 +718,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "encode"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_encode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_encode>(thd, this); } protected: virtual void crypto_transform(String *); private: @@ -733,8 +733,8 @@ class Item_func_decode :public Item_func_encode public: Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {} const char *func_name() const { return "decode"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_decode>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_decode>(thd, this); } protected: void crypto_transform(String *); }; @@ -773,8 +773,8 @@ public: } const char *func_name() const { return "database"; } const char *fully_qualified_func_name() const { return "database()"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_database>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_database>(thd, this); } }; @@ -794,8 +794,8 @@ public: max_length= 512 * system_charset_info->mbmaxlen; null_value= maybe_null= false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sqlerrm>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sqlerrm>(thd, this); } }; @@ -826,8 +826,8 @@ public: { return save_str_value_in_field(field, &str_value); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_user>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_user>(thd, this); } }; @@ -876,8 +876,8 @@ public: return mark_unsupported_function(fully_qualified_func_name(), arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_current_role>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_current_role>(thd, this); } }; @@ -889,8 +889,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "soundex"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_soundex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_soundex>(thd, this); } }; @@ -903,8 +903,8 @@ public: String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "elt"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_elt>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_elt>(thd, this); } }; @@ -917,8 +917,8 @@ public: String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "make_set"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_make_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_make_set>(thd, this); } }; @@ -934,8 +934,8 @@ public: String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "format"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_format>(thd, this); } }; @@ -958,8 +958,8 @@ public: } const char *func_name() const { return "char"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_char>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_char>(thd, this); } }; class Item_func_chr :public Item_func_char @@ -973,8 +973,8 @@ public: max_length= 4; } const char *func_name() const { return "chr"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_chr>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_chr>(thd, this); } }; class Item_func_repeat :public Item_str_func @@ -986,8 +986,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "repeat"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_repeat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_repeat>(thd, this); } }; @@ -998,8 +998,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "space"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_space>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_space>(thd, this); } }; @@ -1015,8 +1015,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_binlog_gtid_pos>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_binlog_gtid_pos>(thd, this); } }; @@ -1042,8 +1042,8 @@ public: Item_func_pad(thd, arg1, arg2) {} String *val_str(String *); const char *func_name() const { return "rpad"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_rpad>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_rpad>(thd, this); } }; @@ -1056,8 +1056,8 @@ public: Item_func_pad(thd, arg1, arg2) {} String *val_str(String *); const char *func_name() const { return "lpad"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_lpad>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_lpad>(thd, this); } }; @@ -1074,8 +1074,8 @@ public: max_length=64; maybe_null= 1; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_conv>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_conv>(thd, this); } }; @@ -1109,8 +1109,8 @@ public: fix_char_length(args[0]->max_length * 2); m_arg0_type_handler= args[0]->type_handler(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_hex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_hex>(thd, this); } }; class Item_func_unhex :public Item_str_func @@ -1130,8 +1130,8 @@ public: decimals=0; max_length=(1+args[0]->max_length)/2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unhex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unhex>(thd, this); } }; @@ -1162,8 +1162,8 @@ public: Item_func_like_range_min(THD *thd, Item *a, Item *b): Item_func_like_range(thd, a, b, true) { } const char *func_name() const { return "like_range_min"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like_range_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like_range_min>(thd, this); } }; @@ -1173,8 +1173,8 @@ public: Item_func_like_range_max(THD *thd, Item *a, Item *b): Item_func_like_range(thd, a, b, false) { } const char *func_name() const { return "like_range_max"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_like_range_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_like_range_max>(thd, this); } }; #endif @@ -1200,8 +1200,8 @@ public: void print(String *str, enum_query_type query_type); const char *func_name() const { return "cast_as_binary"; } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_binary>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_binary>(thd, this); } }; @@ -1222,8 +1222,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_load_file>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_load_file>(thd, this); } }; @@ -1239,8 +1239,8 @@ class Item_func_export_set: public Item_str_func String *val_str(String *str); void fix_length_and_dec(); const char *func_name() const { return "export_set"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_export_set>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_export_set>(thd, this); } }; @@ -1258,8 +1258,8 @@ public: 2 * collation.collation->mbmaxlen; max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_quote>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_quote>(thd, this); } }; class Item_func_conv_charset :public Item_str_func @@ -1342,8 +1342,8 @@ public: void fix_length_and_dec(); const char *func_name() const { return "convert"; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_conv_charset>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_conv_charset>(thd, this); } }; class Item_func_set_collation :public Item_str_func @@ -1364,8 +1364,8 @@ public: return args[0]->field_for_view_update(); } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_set_collation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_set_collation>(thd, this); } }; @@ -1393,8 +1393,8 @@ public: :Item_func_expr_str_metadata(thd, a) { } String *val_str(String *); const char *func_name() const { return "charset"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_charset>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_charset>(thd, this); } }; @@ -1405,8 +1405,8 @@ public: :Item_func_expr_str_metadata(thd, a) {} String *val_str(String *); const char *func_name() const { return "collation"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_collation>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_collation>(thd, this); } }; @@ -1440,8 +1440,8 @@ public: Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { return this; } void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_weight_string>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_weight_string>(thd, this); } }; class Item_func_crc32 :public Item_long_func @@ -1455,8 +1455,8 @@ public: const char *func_name() const { return "crc32"; } void fix_length_and_dec() { max_length=10; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_crc32>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_crc32>(thd, this); } }; class Item_func_uncompressed_length : public Item_long_func_length @@ -1468,8 +1468,8 @@ public: const char *func_name() const{return "uncompressed_length";} void fix_length_and_dec() { max_length=10; maybe_null= true; } longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uncompressed_length>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uncompressed_length>(thd, this); } }; #ifdef HAVE_COMPRESS @@ -1487,8 +1487,8 @@ public: void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} const char *func_name() const{return "compress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_compress>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_compress>(thd, this); } }; class Item_func_uncompress: public Item_str_binary_checksum_func @@ -1500,8 +1500,8 @@ public: void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; } const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uncompress>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uncompress>(thd, this); } }; @@ -1521,8 +1521,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_uuid>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_uuid>(thd, this); } }; @@ -1544,8 +1544,8 @@ public: String *val_str(String *); void print(String *str, enum_query_type query_type); enum Functype functype() const { return DYNCOL_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_create>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_create>(thd, this); } }; @@ -1558,8 +1558,8 @@ public: const char *func_name() const{ return "column_add"; } String *val_str(String *); void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_add>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_add>(thd, this); } }; class Item_func_dyncol_json: public Item_str_func @@ -1575,8 +1575,8 @@ public: maybe_null= 1; decimals= 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_json>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_json>(thd, this); } }; /* @@ -1617,8 +1617,8 @@ public: bool get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, String *tmp); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_dyncol_get>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_dyncol_get>(thd, this); } }; @@ -1630,8 +1630,8 @@ public: void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }; const char *func_name() const{ return "column_list"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dyncol_list>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dyncol_list>(thd, this); } }; #endif /* ITEM_STRFUNC_INCLUDED */ diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 92691907e7c..a8051ccd469 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -29,7 +29,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -115,9 +115,10 @@ void Item_subselect::init(st_select_lex *select_lex, do not take into account expression inside aggregate functions because they can access original table fields */ - parsing_place= (outer_select->in_sum_expr ? NO_MATTER - : outer_select->parsing_place); - if (unit->is_unit_op()) + parsing_place= (outer_select->in_sum_expr ? + NO_MATTER : + outer_select->parsing_place); + if (unit->is_unit_op() && unit->first_select()->next_select()) engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); @@ -263,6 +264,13 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) return TRUE; + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + if (sl->tvc) + { + wrap_tvc_in_derived_table(thd, sl); + } + } if (!(res= engine->prepare(thd))) { @@ -950,7 +958,7 @@ void Item_subselect::print(String *str, enum_query_type query_type) { if (query_type & QT_ITEM_SUBSELECT_ID_ONLY) { - str->append("(subquery#"); + str->append(STRING_WITH_LEN("(subquery#")); if (unit && unit->first_select()) { char buf[64]; @@ -3093,7 +3101,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg) new (thd->mem_root) Item_direct_ref(thd, &unit->outer_select()->context, - optimizer->arguments()[0]->addr(i), + optimizer->arguments()[0]->addr((int)i), (char *)"<no matter>", &exists_outer_expr_name)), thd->mem_root); @@ -3841,8 +3849,8 @@ int subselect_single_select_engine::exec() { /* Change the access method to full table scan */ tab->save_read_first_record= tab->read_first_record; - tab->save_read_record= tab->read_record.read_record; - tab->read_record.read_record= rr_sequential; + tab->save_read_record= tab->read_record.read_record_func; + tab->read_record.read_record_func= rr_sequential; tab->read_first_record= read_first_record_seq; tab->read_record.record= tab->table->record[0]; tab->read_record.thd= join->thd; @@ -3864,8 +3872,8 @@ int subselect_single_select_engine::exec() JOIN_TAB *tab= *ptab; tab->read_record.record= 0; tab->read_record.ref_length= 0; - tab->read_first_record= tab->save_read_first_record; - tab->read_record.read_record= tab->save_read_record; + tab->read_first_record= tab->save_read_first_record; + tab->read_record.read_record_func= tab->save_read_record; } executed= 1; if (!(uncacheable() & ~UNCACHEABLE_EXPLAIN) && @@ -4376,7 +4384,6 @@ void subselect_union_engine::print(String *str, enum_query_type query_type) void subselect_uniquesubquery_engine::print(String *str, enum_query_type query_type) { - const char *table_name= tab->table->s->table_name.str; str->append(STRING_WITH_LEN("<primary_index_lookup>(")); tab->ref.items[0]->print(str, query_type); str->append(STRING_WITH_LEN(" in ")); @@ -4389,10 +4396,10 @@ void subselect_uniquesubquery_engine::print(String *str, str->append(STRING_WITH_LEN("<temporary table>")); } else - str->append(table_name, tab->table->s->table_name.length); + str->append(&tab->table->s->table_name); KEY *key_info= tab->table->key_info+ tab->ref.key; str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (cond) { str->append(STRING_WITH_LEN(" where ")); @@ -4413,9 +4420,9 @@ void subselect_uniquesubquery_engine::print(String *str) for (uint i= 0; i < key_info->user_defined_key_parts; i++) tab->ref.items[i]->print(str); str->append(STRING_WITH_LEN(" in ")); - str->append(tab->table->s->table_name.str, tab->table->s->table_name.length); + str->append(&tab->table->s->table_name); str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (cond) { str->append(STRING_WITH_LEN(" where ")); @@ -4434,7 +4441,7 @@ void subselect_indexsubquery_engine::print(String *str, str->append(tab->table->s->table_name.str, tab->table->s->table_name.length); KEY *key_info= tab->table->key_info+ tab->ref.key; str->append(STRING_WITH_LEN(" on ")); - str->append(key_info->name); + str->append(&key_info->name); if (check_null) str->append(STRING_WITH_LEN(" checking NULL")); if (cond) @@ -4933,11 +4940,10 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) DBUG_RETURN(TRUE); char buf[32]; - uint len= my_snprintf(buf, sizeof(buf), "<subquery%u>", subquery_id); - char *name; - if (!(name= (char*)thd->alloc(len + 1))) + LEX_CSTRING name; + name.length= my_snprintf(buf, sizeof(buf), "<subquery%u>", subquery_id); + if (!(name.str= (char*) thd->memdup(buf, name.length + 1))) DBUG_RETURN(TRUE); - memcpy(name, buf, len+1); result_sink->get_tmp_table_param()->materialized_subquery= true; if (item->substype() == Item_subselect::IN_SUBS && @@ -4947,7 +4953,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) } if (result_sink->create_result_table(thd, tmp_columns, TRUE, tmp_create_options, - name, TRUE, TRUE, FALSE, 0)) + &name, TRUE, TRUE, FALSE, 0)) DBUG_RETURN(TRUE); tmp_table= result_sink->table; @@ -5033,7 +5039,7 @@ bool subselect_hash_sj_engine::make_semi_join_conds() /* Name resolution context for all tmp_table columns created below. */ Name_resolution_context *context; Item_in_subselect *item_in= (Item_in_subselect *) item; - + LEX_CSTRING table_name; DBUG_ENTER("subselect_hash_sj_engine::make_semi_join_conds"); DBUG_ASSERT(semi_join_conds == NULL); @@ -5043,10 +5049,9 @@ bool subselect_hash_sj_engine::make_semi_join_conds() if (!(tmp_table_ref= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)))) DBUG_RETURN(TRUE); - tmp_table_ref->init_one_table(STRING_WITH_LEN(""), - tmp_table->alias.c_ptr(), - tmp_table->alias.length(), - NULL, TL_READ); + table_name.str= tmp_table->alias.c_ptr(); + table_name.length= tmp_table->alias.length(), + tmp_table_ref->init_one_table(&empty_clex_str, &table_name, NULL, TL_READ); tmp_table_ref->table= tmp_table; context= new Name_resolution_context; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index fdb980efc80..dfe3287615b 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -264,9 +264,10 @@ public: void register_as_with_rec_ref(With_element *with_elem); void init_expr_cache_tracker(THD *thd); - Item* build_clone(THD *thd, MEM_ROOT *mem_root) { return 0; } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* build_clone(THD *thd) { return 0; } + Item* get_copy(THD *thd) { return 0; } + bool wrap_tvc_in_derived_table(THD *thd, st_select_lex *tvc_sl); friend class select_result_interceptor; friend class Item_in_optimizer; @@ -881,6 +882,7 @@ public: virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; } int get_identifier(); void force_reexecution(); + void change_select(st_select_lex *new_select) { select_lex= new_select; } friend class subselect_hash_sj_engine; friend class Item_in_subselect; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 234e9dad0b3..25a0f68f575 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -26,10 +26,14 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "uniques.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "sql_parse.h" +#include "sp_head.h" /** Calculate the affordable RAM limit for structures like TREE or Unique @@ -790,7 +794,7 @@ bool Aggregator_distinct::setup(THD *thd) if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, 0, (select_lex->options | thd->variables.option_bits), - HA_POS_ERROR, const_cast<char*>("")))) + HA_POS_ERROR, &empty_clex_str))) return TRUE; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows table->no_rows=1; @@ -1146,17 +1150,19 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) bool Item_sum_hybrid::fix_fields(THD *thd, Item **ref) { + DBUG_ENTER("Item_sum_hybrid::fix_fields"); DBUG_ASSERT(fixed == 0); Item *item= args[0]; if (init_sum_func_check(thd)) - return TRUE; + DBUG_RETURN(TRUE); // 'item' can be changed during fix_fields if ((!item->fixed && item->fix_fields(thd, args)) || (item= args[0])->check_cols(1)) - return TRUE; + DBUG_RETURN(TRUE); + m_with_subquery= args[0]->with_subquery(); with_window_func|= args[0]->with_window_func; @@ -1166,11 +1172,11 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) result_field=0; if (check_sum_func(thd, ref)) - return TRUE; + DBUG_RETURN(TRUE); orig_args[0]= args[0]; fixed= 1; - return FALSE; + DBUG_RETURN(FALSE); } @@ -1201,15 +1207,16 @@ void Item_sum_hybrid::fix_length_and_dec() void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) { + DBUG_ENTER("Item_sum_hybrid::setup_hybrid"); if (!(value= item->get_cache(thd))) - return; + DBUG_VOID_RETURN; value->setup(thd, item); value->store(value_arg); /* Don't cache value, as it will change */ if (!item->const_item()) value->set_used_tables(RAND_TABLE_BIT); if (!(arg_cache= item->get_cache(thd))) - return; + DBUG_VOID_RETURN; arg_cache->setup(thd, item); /* Don't cache value, as it will change */ if (!item->const_item()) @@ -1217,23 +1224,178 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) cmp= new Arg_comparator(); if (cmp) cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); + DBUG_VOID_RETURN; } Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) { + DBUG_ENTER("Item_sum_hybrid::create_tmp_field"); + if (args[0]->type() == Item::FIELD_ITEM) { Field *field= ((Item_field*) args[0])->field; if ((field= create_tmp_field_from_field(table->in_use, field, &name, table, NULL))) field->flags&= ~NOT_NULL_FLAG; - return field; + DBUG_RETURN(field); } - return tmp_table_field_from_field_type(table); + DBUG_RETURN(tmp_table_field_from_field_type(table)); +} + +/*********************************************************************** +** Item_sum_sp class +***********************************************************************/ + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp, List<Item> &list) + :Item_sum(thd, list), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; +} + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp) + :Item_sum(thd), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; } +bool +Item_sum_sp::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + if (init_sum_func_check(thd)) + return TRUE; + decimals= 0; + + m_sp= m_sp ? m_sp : sp_handler_function.sp_find_routine(thd, m_name, true); + + if (!m_sp) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + context->process_error(thd); + return TRUE; + } + + if (init_result_field(thd, max_length, maybe_null, &null_value, &name)) + return TRUE; + + for (uint i= 0 ; i < arg_count ; i++) + { + if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) + return TRUE; + set_if_bigger(decimals, args[i]->decimals); + m_with_subquery|= args[i]->with_subquery(); + with_window_func|= args[i]->with_window_func; + } + result_field= NULL; + max_length= float_length(decimals); + null_value= 1; + fix_length_and_dec(); + + if (check_sum_func(thd, ref)) + return TRUE; + + memcpy(orig_args, args, sizeof(Item *) * arg_count); + fixed= 1; + return FALSE; +} + +/** + Execute function to store value in result field. + This is called when we need the value to be returned for the function. + Here we send a signal in form of the server status that all rows have been + fetched and now we have to exit from the function with the return value. + @return Function returns error status. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::execute() +{ + THD *thd= current_thd; + bool res; + uint old_server_status= thd->server_status; + + /* We set server status so we can send a signal to exit from the + function with the return value. */ + + thd->server_status= SERVER_STATUS_LAST_ROW_SENT; + res= Item_sp::execute(thd, &null_value, args, arg_count); + thd->server_status= old_server_status; + return res; +} + +/** + Handles the aggregation of the values. + @note: See class description for more details on how and why this is done. + @return The error state. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::add() +{ + return execute_impl(current_thd, args, arg_count); +} + + +void +Item_sum_sp::clear() +{ + delete func_ctx; + func_ctx= NULL; + sp_query_arena->free_items(); + free_root(&sp_mem_root, MYF(0)); +} + +const Type_handler *Item_sum_sp::type_handler() const +{ + DBUG_ENTER("Item_sum_sp::type_handler"); + DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp)); + DBUG_ASSERT(sp_result_field); + // This converts ENUM/SET to STRING + const Type_handler *handler= sp_result_field->type_handler(); + DBUG_RETURN(handler->type_handler_for_item_field()); +} + +void +Item_sum_sp::cleanup() +{ + Item_sp::cleanup(); + Item_sum::cleanup(); +} + +/** + Initialize local members with values from the Field interface. + @note called from Item::fix_fields. +*/ + +void +Item_sum_sp::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_sp::fix_length_and_dec"); + DBUG_ASSERT(sp_result_field); + Type_std_attributes::set(sp_result_field); + Item_sum::fix_length_and_dec(); + DBUG_VOID_RETURN; +} + +const char * +Item_sum_sp::func_name() const +{ + THD *thd= current_thd; + return Item_sp::func_name(thd); +} + /*********************************************************************** ** reset and add of sum_func ***********************************************************************/ @@ -1245,6 +1407,7 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) :Item_sum_num(thd, item), Type_handler_hybrid_field_type(item), + direct_added(FALSE), direct_reseted_field(FALSE), curr_dec_buff(item->curr_dec_buff), count(item->count) { @@ -1264,6 +1427,15 @@ Item *Item_sum_sum::copy_or_same(THD* thd) } +void Item_sum_sum::cleanup() +{ + DBUG_ENTER("Item_sum_sum::cleanup"); + direct_added= direct_reseted_field= FALSE; + Item_sum_num::cleanup(); + DBUG_VOID_RETURN; +} + + void Item_sum_sum::clear() { DBUG_ENTER("Item_sum_sum::clear"); @@ -1313,6 +1485,38 @@ void Item_sum_sum::fix_length_and_dec() } +void Item_sum_sum::direct_add(my_decimal *add_sum_decimal) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_decimal: %p", add_sum_decimal)); + direct_added= TRUE; + direct_reseted_field= FALSE; + if (add_sum_decimal) + { + direct_sum_is_null= FALSE; + direct_sum_decimal= *add_sum_decimal; + } + else + { + direct_sum_is_null= TRUE; + direct_sum_decimal= decimal_zero; + } + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::direct_add(double add_sum_real, bool add_sum_is_null) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_real: %f", add_sum_real)); + direct_added= TRUE; + direct_reseted_field= FALSE; + direct_sum_is_null= add_sum_is_null; + direct_sum_real= add_sum_real; + DBUG_VOID_RETURN; +} + + bool Item_sum_sum::add() { DBUG_ENTER("Item_sum_sum::add"); @@ -1326,50 +1530,84 @@ void Item_sum_sum::add_helper(bool perform_removal) if (result_type() == DECIMAL_RESULT) { - my_decimal value; - const my_decimal *val= aggr->arg_val_decimal(&value); - if (!aggr->arg_is_null(true)) + if (unlikely(direct_added)) { - if (perform_removal) + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (likely(!direct_sum_is_null)) { - if (count > 0) + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), + &direct_sum_decimal, dec_buffs + curr_dec_buff); + curr_dec_buff^= 1; + null_value= 0; + } + } + else + { + direct_reseted_field= FALSE; + my_decimal value; + const my_decimal *val= aggr->arg_val_decimal(&value); + if (!aggr->arg_is_null(true)) + { + if (perform_removal) { - my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), - dec_buffs + curr_dec_buff, val); - count--; + if (count > 0) + { + my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + dec_buffs + curr_dec_buff, val); + count--; + } + else + DBUG_VOID_RETURN; } else - DBUG_VOID_RETURN; - } - else - { - count++; - my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), - val, dec_buffs + curr_dec_buff); + { + count++; + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + val, dec_buffs + curr_dec_buff); + } + curr_dec_buff^= 1; + null_value= (count > 0) ? 0 : 1; } - curr_dec_buff^= 1; - null_value= (count > 0) ? 0 : 1; } } else { - if (perform_removal && count > 0) - sum-= aggr->arg_val_real(); + if (unlikely(direct_added)) + { + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (!direct_sum_is_null) + { + sum+= direct_sum_real; + null_value= 0; + } + } else - sum+= aggr->arg_val_real(); - if (!aggr->arg_is_null(true)) { - if (perform_removal) + direct_reseted_field= FALSE; + if (perform_removal && count > 0) + sum-= aggr->arg_val_real(); + else + sum+= aggr->arg_val_real(); + if (!aggr->arg_is_null(true)) { - if (count > 0) + if (perform_removal) { - count--; + if (count > 0) + { + count--; + } } - } - else - count++; + else + count++; - null_value= (count > 0) ? 0 : 1; + null_value= (count > 0) ? 0 : 1; + } } } DBUG_VOID_RETURN; @@ -1556,22 +1794,46 @@ bool Aggregator_distinct::arg_is_null(bool use_null_value) Item *Item_sum_count::copy_or_same(THD* thd) { - return new (thd->mem_root) Item_sum_count(thd, this); + DBUG_ENTER("Item_sum_count::copy_or_same"); + DBUG_RETURN(new (thd->mem_root) Item_sum_count(thd, this)); +} + + +void Item_sum_count::direct_add(longlong add_count) +{ + DBUG_ENTER("Item_sum_count::direct_add"); + DBUG_PRINT("info", ("add_count: %lld", add_count)); + direct_counted= TRUE; + direct_reseted_field= FALSE; + direct_count= add_count; + DBUG_VOID_RETURN; } void Item_sum_count::clear() { + DBUG_ENTER("Item_sum_count::clear"); count= 0; + DBUG_VOID_RETURN; } bool Item_sum_count::add() { - if (aggr->arg_is_null(false)) - return 0; - count++; - return 0; + DBUG_ENTER("Item_sum_count::add"); + if (direct_counted) + { + direct_counted= FALSE; + count+= direct_count; + } + else + { + direct_reseted_field= FALSE; + if (aggr->arg_is_null(false)) + DBUG_RETURN(0); + count++; + } + DBUG_RETURN(0); } @@ -1590,10 +1852,11 @@ void Item_sum_count::remove() longlong Item_sum_count::val_int() { + DBUG_ENTER("Item_sum_count::val_int"); DBUG_ASSERT(fixed == 1); if (aggr) aggr->endup(); - return (longlong) count; + DBUG_RETURN((longlong)count); } @@ -1601,6 +1864,8 @@ void Item_sum_count::cleanup() { DBUG_ENTER("Item_sum_count::cleanup"); count= 0; + direct_counted= FALSE; + direct_reseted_field= FALSE; Item_sum_int::cleanup(); DBUG_VOID_RETURN; } @@ -2015,10 +2280,13 @@ Item *Item_sum_variance::result_item(THD *thd, Field *field) void Item_sum_hybrid::clear() { + DBUG_ENTER("Item_sum_hybrid::clear"); value->clear(); null_value= 1; + DBUG_VOID_RETURN; } + bool Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { @@ -2031,51 +2299,66 @@ Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) return retval; } + +void Item_sum_hybrid::direct_add(Item *item) +{ + DBUG_ENTER("Item_sum_hybrid::direct_add"); + DBUG_PRINT("info", ("item: %p", item)); + direct_added= TRUE; + direct_item= item; + DBUG_VOID_RETURN; +} + + double Item_sum_hybrid::val_real() { + DBUG_ENTER("Item_sum_hybrid::val_real"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0.0; + DBUG_RETURN(0.0); double retval= value->val_real(); if ((null_value= value->null_value)) DBUG_ASSERT(retval == 0.0); - return retval; + DBUG_RETURN(retval); } longlong Item_sum_hybrid::val_int() { + DBUG_ENTER("Item_sum_hybrid::val_int"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); longlong retval= value->val_int(); if ((null_value= value->null_value)) DBUG_ASSERT(retval == 0); - return retval; + DBUG_RETURN(retval); } my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val) { + DBUG_ENTER("Item_sum_hybrid::val_decimal"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); my_decimal *retval= value->val_decimal(val); if ((null_value= value->null_value)) DBUG_ASSERT(retval == NULL); - return retval; + DBUG_RETURN(retval); } String * Item_sum_hybrid::val_str(String *str) { + DBUG_ENTER("Item_sum_hybrid::val_str"); DBUG_ASSERT(fixed == 1); if (null_value) - return 0; + DBUG_RETURN(0); String *retval= value->val_str(str); if ((null_value= value->null_value)) DBUG_ASSERT(retval == NULL); - return retval; + DBUG_RETURN(retval); } @@ -2099,6 +2382,7 @@ void Item_sum_hybrid::cleanup() void Item_sum_hybrid::no_rows_in_result() { + DBUG_ENTER("Item_sum_hybrid::no_rows_in_result"); /* We may be called here twice in case of ref field in function */ if (was_values) { @@ -2106,6 +2390,7 @@ void Item_sum_hybrid::no_rows_in_result() was_null_value= value->null_value; clear(); } + DBUG_VOID_RETURN; } void Item_sum_hybrid::restore_to_before_no_rows_in_result() @@ -2120,14 +2405,26 @@ void Item_sum_hybrid::restore_to_before_no_rows_in_result() Item *Item_sum_min::copy_or_same(THD* thd) { + DBUG_ENTER("Item_sum_min::copy_or_same"); Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this); item->setup_hybrid(thd, args[0], value); - return item; + DBUG_RETURN(item); } bool Item_sum_min::add() { + Item *tmp_item; + DBUG_ENTER("Item_sum_min::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); /* args[0] < value */ arg_cache->cache_value(); if (!arg_cache->null_value && @@ -2137,7 +2434,13 @@ bool Item_sum_min::add() value->cache_value(); null_value= 0; } - return 0; + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); } @@ -2151,8 +2454,19 @@ Item *Item_sum_max::copy_or_same(THD* thd) bool Item_sum_max::add() { + Item *tmp_item; + DBUG_ENTER("Item_sum_max::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } /* args[0] > value */ arg_cache->cache_value(); + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); if (!arg_cache->null_value && (null_value || cmp->compare() > 0)) { @@ -2160,7 +2474,13 @@ bool Item_sum_max::add() value->cache_value(); null_value= 0; } - return 0; + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); } @@ -2339,14 +2659,26 @@ void Item_sum_num::reset_field() void Item_sum_hybrid::reset_field() { + Item *tmp_item, *arg0; + DBUG_ENTER("Item_sum_hybrid::reset_field"); + + arg0= args[0]; + if (unlikely(direct_added)) + { + /* Switch to use direct item */ + tmp_item= value->get_item(); + value->store(direct_item); + arg0= direct_item; + } + switch(result_type()) { case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),result_field->charset()),*res; - res=args[0]->val_str(&tmp); - if (args[0]->null_value) + res= arg0->val_str(&tmp); + if (arg0->null_value) { result_field->set_null(); result_field->reset(); @@ -2360,11 +2692,11 @@ void Item_sum_hybrid::reset_field() } case INT_RESULT: { - longlong nr=args[0]->val_int(); + longlong nr= arg0->val_int(); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) { nr=0; result_field->set_null(); @@ -2372,16 +2704,17 @@ void Item_sum_hybrid::reset_field() else result_field->set_notnull(); } + DBUG_PRINT("info", ("nr: %lld", nr)); result_field->store(nr, unsigned_flag); break; } case REAL_RESULT: { - double nr= args[0]->val_real(); + double nr= arg0->val_real(); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) { nr=0.0; result_field->set_null(); @@ -2394,11 +2727,11 @@ void Item_sum_hybrid::reset_field() } case DECIMAL_RESULT: { - my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff); + my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff); if (maybe_null) { - if (args[0]->null_value) + if (arg0->null_value) result_field->set_null(); else result_field->set_notnull(); @@ -2416,26 +2749,49 @@ void Item_sum_hybrid::reset_field() case TIME_RESULT: DBUG_ASSERT(0); } + + if (unlikely(direct_added)) + { + direct_added= FALSE; + value->store(tmp_item); + } + DBUG_VOID_RETURN; } void Item_sum_sum::reset_field() { + my_bool null_flag; DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!arg_val) // Null - arg_val= &decimal_zero; + my_decimal value, *arg_val; + if (unlikely(direct_added)) + arg_val= &direct_sum_decimal; + else + { + if (!(arg_val= args[0]->val_decimal(&value))) + arg_val= &decimal_zero; // Null + } result_field->store_decimal(arg_val); } else { DBUG_ASSERT(result_type() == REAL_RESULT); - double nr= args[0]->val_real(); // Nulls also return 0 + double nr= likely(!direct_added) ? args[0]->val_real() : direct_sum_real; float8store(result_field->ptr, nr); } - if (args[0]->null_value) + + if (unlikely(direct_added)) + { + direct_added= FALSE; + direct_reseted_field= TRUE; + null_flag= direct_sum_is_null; + } + else + null_flag= args[0]->null_value; + + if (null_flag) result_field->set_null(); else result_field->set_notnull(); @@ -2444,13 +2800,22 @@ void Item_sum_sum::reset_field() void Item_sum_count::reset_field() { + DBUG_ENTER("Item_sum_count::reset_field"); uchar *res=result_field->ptr; longlong nr=0; DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (!args[0]->maybe_null || !args[0]->is_null()) - nr=1; + if (unlikely(direct_counted)) + { + nr= direct_count; + direct_counted= FALSE; + direct_reseted_field= TRUE; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) + nr= 1; + DBUG_PRINT("info", ("nr: %lld", nr)); int8store(res,nr); + DBUG_VOID_RETURN; } @@ -2518,13 +2883,26 @@ void Item_sum_sum::update_field() DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (result_type() == DECIMAL_RESULT) { - my_decimal value, *arg_val= args[0]->val_decimal(&value); - if (!args[0]->null_value) + my_decimal value, *arg_val; + my_bool null_flag; + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + arg_val= &direct_sum_decimal; + null_flag= direct_sum_is_null; + } + else + { + arg_val= args[0]->val_decimal(&value); + null_flag= args[0]->null_value; + } + + if (!null_flag) { if (!result_field->is_null()) { - my_decimal field_value, - *field_val= result_field->val_decimal(&field_value); + my_decimal field_value; + my_decimal *field_val= result_field->val_decimal(&field_value); my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val); result_field->store_decimal(dec_buffs); } @@ -2538,11 +2916,22 @@ void Item_sum_sum::update_field() else { double old_nr,nr; - uchar *res=result_field->ptr; + uchar *res= result_field->ptr; + my_bool null_flag; float8get(old_nr,res); - nr= args[0]->val_real(); - if (!args[0]->null_value) + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + null_flag= direct_sum_is_null; + nr= direct_sum_real; + } + else + { + nr= args[0]->val_real(); + null_flag= args[0]->null_value; + } + if (!null_flag) { old_nr+=nr; result_field->set_notnull(); @@ -2554,13 +2943,21 @@ void Item_sum_sum::update_field() void Item_sum_count::update_field() { + DBUG_ENTER("Item_sum_count::update_field"); longlong nr; uchar *res=result_field->ptr; nr=sint8korr(res); - if (!args[0]->maybe_null || !args[0]->is_null()) + if (unlikely(direct_counted || direct_reseted_field)) + { + direct_counted= direct_reseted_field= FALSE; + nr+= direct_count; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) nr++; + DBUG_PRINT("info", ("nr: %lld", nr)); int8store(res,nr); + DBUG_VOID_RETURN; } @@ -2618,6 +3015,13 @@ Item *Item_sum_avg::result_item(THD *thd, Field *field) void Item_sum_hybrid::update_field() { + DBUG_ENTER("Item_sum_hybrid::update_field"); + Item *tmp_item; + if (unlikely(direct_added)) + { + tmp_item= args[0]; + args[0]= direct_item; + } switch (result_type()) { case STRING_RESULT: min_max_update_str_field(); @@ -2631,12 +3035,19 @@ void Item_sum_hybrid::update_field() default: min_max_update_real_field(); } + if (unlikely(direct_added)) + { + direct_added= FALSE; + args[0]= tmp_item; + } + DBUG_VOID_RETURN; } void Item_sum_hybrid::min_max_update_str_field() { + DBUG_ENTER("Item_sum_hybrid::min_max_update_str_field"); DBUG_ASSERT(cmp); String *res_str=args[0]->val_str(&cmp->value1); @@ -2649,6 +3060,7 @@ Item_sum_hybrid::min_max_update_str_field() result_field->store(res_str->ptr(),res_str->length(),res_str->charset()); result_field->set_notnull(); } + DBUG_VOID_RETURN; } @@ -2657,6 +3069,7 @@ Item_sum_hybrid::min_max_update_real_field() { double nr,old_nr; + DBUG_ENTER("Item_sum_hybrid::min_max_update_real_field"); old_nr=result_field->val_real(); nr= args[0]->val_real(); if (!args[0]->null_value) @@ -2669,6 +3082,7 @@ Item_sum_hybrid::min_max_update_real_field() else if (result_field->is_null(0)) result_field->set_null(); result_field->store(old_nr); + DBUG_VOID_RETURN; } @@ -2677,6 +3091,7 @@ Item_sum_hybrid::min_max_update_int_field() { longlong nr,old_nr; + DBUG_ENTER("Item_sum_hybrid::min_max_update_int_field"); old_nr=result_field->val_int(); nr=args[0]->val_int(); if (!args[0]->null_value) @@ -2696,7 +3111,9 @@ Item_sum_hybrid::min_max_update_int_field() } else if (result_field->is_null(0)) result_field->set_null(); + DBUG_PRINT("info", ("nr: %lld", old_nr)); result_field->store(old_nr, unsigned_flag); + DBUG_VOID_RETURN; } @@ -2707,6 +3124,7 @@ Item_sum_hybrid::min_max_update_int_field() void Item_sum_hybrid::min_max_update_decimal_field() { + DBUG_ENTER("Item_sum_hybrid::min_max_update_decimal_field"); my_decimal old_val, nr_val; const my_decimal *old_nr; const my_decimal *nr= args[0]->val_decimal(&nr_val); @@ -2727,6 +3145,7 @@ Item_sum_hybrid::min_max_update_decimal_field() } else if (result_field->is_null(0)) result_field->set_null(); + DBUG_VOID_RETURN; } @@ -2850,7 +3269,7 @@ double Item_sum_udf_float::val_real() double res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_float::val"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val(&tmp_null_value); null_value= tmp_null_value; @@ -2894,7 +3313,7 @@ my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) my_bool tmp_null_value; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_decimal::val_decimal"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val_decimal(&tmp_null_value, dec_buf); @@ -2920,7 +3339,7 @@ longlong Item_sum_udf_int::val_int() longlong res; DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_sum_udf_int::val_int"); - DBUG_PRINT("info",("result_type: %d arg_count: %d", + DBUG_PRINT("enter",("result_type: %d arg_count: %d", args[0]->result_type(), arg_count)); res= udf.val_int(&tmp_null_value); null_value= tmp_null_value; @@ -3114,6 +3533,11 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), Item **arg= item->args, **arg_end= item->args + item->arg_count_field; uint old_length= result->length(); + ulonglong *offset_limit= &item->copy_offset_limit; + ulonglong *row_limit = &item->copy_row_limit; + if (item->limit_clause && !(*row_limit)) + return 1; + if (item->no_appended) item->no_appended= FALSE; else @@ -3121,6 +3545,14 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), tmp.length(0); + if (item->limit_clause && (*offset_limit)) + { + item->row_count++; + item->no_appended= TRUE; + (*offset_limit)--; + return 0; + } + for (; arg < arg_end; arg++) { String *res; @@ -3150,6 +3582,8 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), result->append(*res); } + if (item->limit_clause) + (*row_limit)--; item->row_count++; /* stop if length of result more than max_length */ @@ -3163,7 +3597,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), as this is never used to limit the length of the data. Cut is done with the third argument. */ - uint add_length= Well_formed_prefix(cs, + size_t add_length= Well_formed_prefix(cs, ptr + old_length, ptr + max_length, result->length()).length(); @@ -3198,7 +3632,8 @@ Item_func_group_concat:: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool distinct_arg, List<Item> *select_list, const SQL_I_List<ORDER> &order_list, - String *separator_arg) + String *separator_arg, bool limit_clause, + Item *row_limit_arg, Item *offset_limit_arg) :Item_sum(thd), tmp_table_param(0), separator(separator_arg), tree(0), unique_filter(NULL), table(0), order(0), context(context_arg), @@ -3207,7 +3642,9 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, row_count(0), distinct(distinct_arg), warning_for_row(FALSE), - force_copy_fields(0), original(0) + force_copy_fields(0), row_limit(NULL), + offset_limit(NULL), limit_clause(limit_clause), + copy_offset_limit(0), copy_row_limit(0), original(0) { Item *item_select; Item **arg_ptr; @@ -3249,6 +3686,11 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, /* orig_args is only used for print() */ orig_args= (Item**) (order + arg_count_order); memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (limit_clause) + { + row_limit= row_limit_arg; + offset_limit= offset_limit_arg; + } } @@ -3268,7 +3710,9 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, warning_for_row(item->warning_for_row), always_null(item->always_null), force_copy_fields(item->force_copy_fields), - original(item) + row_limit(item->row_limit), offset_limit(item->offset_limit), + limit_clause(item->limit_clause),copy_offset_limit(item->copy_offset_limit), + copy_row_limit(item->copy_row_limit), original(item) { quick_group= item->quick_group; result.set_charset(collation.collation); @@ -3322,7 +3766,7 @@ void Item_func_group_concat::cleanup() table= 0; if (tree) { - delete_tree(tree); + delete_tree(tree, 0); tree= 0; } if (unique_filter) @@ -3363,6 +3807,10 @@ void Item_func_group_concat::clear() null_value= TRUE; warning_for_row= FALSE; no_appended= TRUE; + if (offset_limit) + copy_offset_limit= offset_limit->val_int(); + if (row_limit) + copy_row_limit= row_limit->val_int(); if (tree) reset_tree(tree); if (unique_filter) @@ -3578,7 +4026,7 @@ bool Item_func_group_concat::setup(THD *thd) (ORDER*) 0, 0, TRUE, (select_lex->options | thd->variables.option_bits), - HA_POS_ERROR, (char*) ""))) + HA_POS_ERROR, &empty_clex_str))) DBUG_RETURN(TRUE); table->file->extra(HA_EXTRA_NO_ROWS); table->no_rows= 1; @@ -3617,6 +4065,12 @@ bool Item_func_group_concat::setup(THD *thd) (void*)this, tree_key_length, ram_limitation(thd)); + if ((row_limit && row_limit->cmp_type() != INT_RESULT) || + (offset_limit && offset_limit->cmp_type() != INT_RESULT)) + { + my_error(ER_INVALID_VALUE_TO_LIMIT, MYF(0)); + DBUG_RETURN(TRUE); + } DBUG_RETURN(FALSE); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 1f7d89e8a17..1ed3d870bcc 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -354,7 +354,8 @@ public: VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC, ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC, CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC, - NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC + NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC, PERCENTILE_CONT_FUNC, + PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC }; Item **ref_by; /* pointer to a ref to the object used to register it */ @@ -520,6 +521,7 @@ public: Item *get_arg(uint i) const { return args[i]; } Item *set_arg(uint i, THD *thd, Item *new_val); uint get_arg_count() const { return arg_count; } + virtual Item **get_args() { return fixed ? orig_args : args; } /* Initialization of distinct related members */ void init_aggregator() @@ -760,14 +762,20 @@ class Item_sum_sum :public Item_sum_num, public Type_handler_hybrid_field_type { protected: + bool direct_added; + bool direct_reseted_field; + bool direct_sum_is_null; + double direct_sum_real; double sum; + my_decimal direct_sum_decimal; my_decimal dec_buffs[2]; uint curr_dec_buff; void fix_length_and_dec(); public: Item_sum_sum(THD *thd, Item *item_par, bool distinct): - Item_sum_num(thd, item_par) + Item_sum_num(thd, item_par), direct_added(FALSE), + direct_reseted_field(FALSE) { set_distinct(distinct); } @@ -776,6 +784,9 @@ public: { return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC; } + void cleanup(); + void direct_add(my_decimal *add_sum_decimal); + void direct_add(double add_sum_real, bool add_sum_is_null); void clear(); bool add(); double val_real(); @@ -795,8 +806,8 @@ public: } Item *copy_or_same(THD* thd); void remove(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_sum>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_sum>(thd, this); } bool supports_removal() const { @@ -811,6 +822,9 @@ private: class Item_sum_count :public Item_sum_int { + bool direct_counted; + bool direct_reseted_field; + longlong direct_count; longlong count; friend class Aggregator_distinct; @@ -820,9 +834,10 @@ class Item_sum_count :public Item_sum_int void cleanup(); void remove(); - public: +public: Item_sum_count(THD *thd, Item *item_par): - Item_sum_int(thd, item_par), count(0) + Item_sum_int(thd, item_par), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) {} /** @@ -834,12 +849,14 @@ class Item_sum_count :public Item_sum_int */ Item_sum_count(THD *thd, List<Item> &list): - Item_sum_int(thd, list), count(0) + Item_sum_int(thd, list), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) { set_distinct(TRUE); } Item_sum_count(THD *thd, Item_sum_count *item): - Item_sum_int(thd, item), count(item->count) + Item_sum_int(thd, item), direct_counted(FALSE), + direct_reseted_field(FALSE), count(item->count) {} enum Sumfunctype sum_func () const { @@ -854,13 +871,14 @@ class Item_sum_count :public Item_sum_int longlong val_int(); void reset_field(); void update_field(); + void direct_add(longlong add_count); const char *func_name() const { return has_with_distinct() ? "count(distinct " : "count("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_count>(thd, this); } bool supports_removal() const { @@ -915,8 +933,8 @@ public: count= 0; Item_sum_sum::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_avg>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_avg>(thd, this); } bool supports_removal() const { @@ -981,8 +999,8 @@ public: count= 0; Item_sum_num::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_variance>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_variance>(thd, this); } }; /* @@ -1002,8 +1020,8 @@ class Item_sum_std :public Item_sum_variance Item *result_item(THD *thd, Field *field); const char *func_name() const { return "std("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_std>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_std>(thd, this); } }; // This class is a string or number function depending on num_func @@ -1012,6 +1030,8 @@ class Item_cache; class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type { protected: + bool direct_added; + Item *direct_item; Item_cache *value, *arg_cache; Arg_comparator *cmp; int cmp_sign; @@ -1022,19 +1042,20 @@ protected: Item_sum_hybrid(THD *thd, Item *item_par,int sign): Item_sum(thd, item_par), Type_handler_hybrid_field_type(&type_handler_longlong), - value(0), arg_cache(0), cmp(0), + direct_added(FALSE), value(0), arg_cache(0), cmp(0), cmp_sign(sign), was_values(TRUE) { collation.set(&my_charset_bin); } Item_sum_hybrid(THD *thd, Item_sum_hybrid *item) :Item_sum(thd, item), Type_handler_hybrid_field_type(item), - value(item->value), arg_cache(0), + direct_added(FALSE), value(item->value), arg_cache(0), cmp_sign(item->cmp_sign), was_values(item->was_values) { } bool fix_fields(THD *, Item **); void fix_length_and_dec(); void setup_hybrid(THD *thd, Item *item, Item *value_arg); void clear(); + void direct_add(Item *item); double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); @@ -1072,8 +1093,8 @@ public: bool add(); const char *func_name() const { return "min("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_min>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_min>(thd, this); } }; @@ -1087,8 +1108,8 @@ public: bool add(); const char *func_name() const { return "max("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_max>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_max>(thd, this); } }; @@ -1168,8 +1189,8 @@ public: bool add(); const char *func_name() const { return "bit_or("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_or>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_or>(thd, this); } private: void set_bits_from_counters(); @@ -1185,8 +1206,8 @@ public: bool add(); const char *func_name() const { return "bit_and("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_and>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_and>(thd, this); } private: void set_bits_from_counters(); @@ -1200,13 +1221,143 @@ public: bool add(); const char *func_name() const { return "bit_xor("; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_xor>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_xor>(thd, this); } private: void set_bits_from_counters(); }; +class sp_head; +class sp_name; +class Query_arena; +struct st_sp_security_context; + +/* + Item_sum_sp handles STORED AGGREGATE FUNCTIONS + + Each Item_sum_sp represents a custom aggregate function. Inside the + function's body, we require at least one occurence of FETCH GROUP NEXT ROW + instruction. This cursor is what makes custom stored aggregates possible. + + During computation the function's add method is called. This in turn performs + an execution of the function. The function will execute from the current + function context (and instruction), if one exists, or from the start if not. + See Item_sp for more details. + + Upon encounter of FETCH GROUP NEXT ROW instruction, the function will pause + execution. We assume that the user has performed the necessary additions for + a row, between two encounters of FETCH GROUP NEXT ROW. + + Example: + create aggregate function f1(x INT) returns int + begin + declare continue handler for not found return s; + declare s int default 0 + loop + fetch group next row; + set s = s + x; + end loop; + end + + The function will always stop after an encounter of FETCH GROUP NEXT ROW, + except (!) on first encounter, as the value for the first row in the + group is already set in the argument x. This behaviour is done so when + a user writes a function, he should "logically" include FETCH GROUP NEXT ROW + before any "add" instructions in the stored function. This means however that + internally, the first occurence doesn't stop the function. See the + implementation of FETCH GROUP NEXT ROW for details as to how it happens. + + Either way, one should assume that after calling "Item_sum_sp::add()" that + the values for that particular row have been added to the aggregation. + + To produce values for val_xxx methods we need an extra syntactic construct. + We require a continue handler when "no more rows are available". val_xxx + methods force a function return by executing the function again, while + setting a server flag that no more rows have been found. This implies + that val_xxx methods should only be called once per group however. + + Example: + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ret_val; +*/ +class Item_sum_sp :public Item_sum, + public Item_sp +{ + private: + bool execute(); + +public: + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp); + + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp, List<Item> &list); + + enum Sumfunctype sum_func () const + { + return SP_AGGREGATE_FUNC; + } + void fix_length_and_dec(); + bool fix_fields(THD *thd, Item **ref); + const char *func_name() const; + const Type_handler *type_handler() const; + bool add(); + + /* val_xx functions */ + longlong val_int() + { + if(execute()) + return 0; + return sp_result_field->val_int(); + } + + double val_real() + { + if(execute()) + return 0.0; + return sp_result_field->val_real(); + } + + my_decimal *val_decimal(my_decimal *dec_buf) + { + if(execute()) + return NULL; + return sp_result_field->val_decimal(dec_buf); + } + + String *val_str(String *str) + { + String buf; + char buff[20]; + buf.set(buff, 20, str->charset()); + buf.length(0); + if (execute()) + return NULL; + /* + result_field will set buf pointing to internal buffer + of the resul_field. Due to this it will change any time + when SP is executed. In order to prevent occasional + corruption of returned value, we make here a copy. + */ + sp_result_field->val_str(&buf); + str->copy(buf); + return str; + } + void reset_field(){DBUG_ASSERT(0);} + void update_field(){DBUG_ASSERT(0);} + void clear(); + void cleanup(); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + return execute() || sp_result_field->get_date(ltime, fuzzydate); + } + inline Field *get_sp_result_field() + { + return sp_result_field; + } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_sp>(thd, this); } +}; /* Items to get the value of a stored sum function */ @@ -1262,8 +1413,8 @@ public: my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); } String *val_str(String *str) { return val_string_from_real(str); } double val_real(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_avg_field_double>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_avg_field_double>(thd, this); } }; @@ -1282,8 +1433,8 @@ public: longlong val_int() { return val_int_from_decimal(); } String *val_str(String *str) { return val_string_from_decimal(str); } my_decimal *val_decimal(my_decimal *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_avg_field_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_avg_field_decimal>(thd, this); } }; @@ -1303,8 +1454,8 @@ public: { return val_decimal_from_real(dec_buf); } bool is_null() { update_null_value(); return null_value; } const Type_handler *type_handler() const { return &type_handler_double; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_variance_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_variance_field>(thd, this); } }; @@ -1316,8 +1467,8 @@ public: { } enum Type type() const { return FIELD_STD_ITEM; } double val_real(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_std_field>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_std_field>(thd, this); } }; @@ -1407,8 +1558,8 @@ class Item_sum_udf_float :public Item_udf_sum const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_float>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_float>(thd, this); } }; @@ -1429,8 +1580,8 @@ public: const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals=0; max_length=21; } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_int>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_int>(thd, this); } }; @@ -1470,8 +1621,8 @@ public: const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_str>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_str>(thd, this); } }; @@ -1491,8 +1642,8 @@ public: const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_udf_decimal>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_udf_decimal>(thd, this); } }; #else /* Dummy functions to get sql_yacc.cc compiled */ @@ -1612,6 +1763,16 @@ class Item_func_group_concat : public Item_sum bool always_null; bool force_copy_fields; bool no_appended; + /** Limits the rows in the result */ + Item *row_limit; + /** Skips a particular number of rows in from the result*/ + Item *offset_limit; + bool limit_clause; + /* copy of the offset limit */ + ulonglong copy_offset_limit; + /*copy of the row limit */ + ulonglong copy_row_limit; + /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -1628,7 +1789,8 @@ class Item_func_group_concat : public Item_sum public: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool is_distinct, List<Item> *is_select, - const SQL_I_List<ORDER> &is_order, String *is_separator); + const SQL_I_List<ORDER> &is_order, String *is_separator, + bool limit_clause, Item *row_limit, Item *offset_limit); Item_func_group_concat(THD *thd, Item_func_group_concat *item); ~Item_func_group_concat(); @@ -1683,8 +1845,8 @@ public: virtual void print(String *str, enum_query_type query_type); virtual bool change_context_processor(void *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_group_concat>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_group_concat>(thd, this); } }; #endif /* ITEM_SUM_INCLUDED */ diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index a0fdd4bb8fc..a048f4d933d 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -30,7 +30,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -709,8 +709,7 @@ static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, For example, '1.1' -> '1.100000' */ -static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, - uint count, ulonglong *values, +static bool get_interval_info(const char *str, size_t length,CHARSET_INFO *cs, size_t count, ulonglong *values, bool transform_msec) { const char *end=str+length; @@ -2427,7 +2426,7 @@ void Item_char_typecast::print(String *str, enum_query_type query_type) } -void Item_char_typecast::check_truncation_with_warn(String *src, uint dstlen) +void Item_char_typecast::check_truncation_with_warn(String *src, size_t dstlen) { if (dstlen < src->length()) { @@ -2448,7 +2447,7 @@ void Item_char_typecast::check_truncation_with_warn(String *src, uint dstlen) } -String *Item_char_typecast::reuse(String *src, uint32 length) +String *Item_char_typecast::reuse(String *src, size_t length) { DBUG_ASSERT(length <= src->length()); check_truncation_with_warn(src, length); @@ -2652,6 +2651,27 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) } +void Item_datetime_from_unixtime_typecast::fix_length_and_dec() +{ + Item_datetime_typecast::fix_length_and_dec(); + + switch (args[0]->result_type()) + { + case INT_RESULT: + case REAL_RESULT: + case DECIMAL_RESULT: + { + Query_arena_stmt on_stmt_arena(thd); + Item_func_from_unixtime *a= new (thd->mem_root) Item_func_from_unixtime(thd, args[0]); + a->fix_length_and_dec(); + args[0]= a; + break; + } + default:; + } +} + + /** MAKEDATE(a,b) is a date function that creates a date value from a year and day value. diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 085c14ba3da..761f619def8 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -66,8 +66,8 @@ public: { max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_period_add>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_period_add>(thd, this); } }; @@ -84,8 +84,8 @@ public: decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_period_diff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_period_diff>(thd, this); } }; @@ -109,8 +109,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_days>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_days>(thd, this); } }; @@ -137,8 +137,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_to_seconds>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_to_seconds>(thd, this); } }; @@ -160,8 +160,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dayofmonth>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dayofmonth>(thd, this); } }; @@ -199,8 +199,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_month>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_month>(thd, this); } }; @@ -221,8 +221,8 @@ public: { return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_monthname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_monthname>(thd, this); } }; @@ -244,8 +244,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_dayofyear>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_dayofyear>(thd, this); } }; @@ -267,8 +267,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_hour>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_hour>(thd, this); } }; @@ -290,8 +290,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_minute>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_minute>(thd, this); } }; @@ -313,8 +313,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_quarter>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_quarter>(thd, this); } }; @@ -336,8 +336,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_second>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_second>(thd, this); } }; @@ -369,8 +369,8 @@ public: { return arg_count == 2; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_week>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_week>(thd, this); } }; class Item_func_yearweek :public Item_long_func @@ -397,8 +397,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_yearweek>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_yearweek>(thd, this); } }; @@ -422,8 +422,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_year>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_year>(thd, this); } }; @@ -462,8 +462,8 @@ public: { return !has_date_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_weekday>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_weekday>(thd, this); } }; class Item_func_dayname :public Item_func_weekday @@ -541,8 +541,8 @@ public: } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_unix_timestamp>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_unix_timestamp>(thd, this); } }; @@ -564,8 +564,8 @@ public: } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_time_to_sec>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_time_to_sec>(thd, this); } }; @@ -692,8 +692,8 @@ public: Item_func_curtime_local(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "curtime"; } virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curtime_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curtime_local>(thd, this); } }; @@ -703,8 +703,8 @@ public: Item_func_curtime_utc(THD *thd, uint dec): Item_func_curtime(thd, dec) {} const char *func_name() const { return "utc_time"; } virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curtime_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curtime_utc>(thd, this); } }; @@ -731,8 +731,8 @@ public: Item_func_curdate_local(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "curdate"; } void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curdate_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curdate_local>(thd, this); } }; @@ -742,8 +742,8 @@ public: Item_func_curdate_utc(THD *thd): Item_func_curdate(thd) {} const char *func_name() const { return "utc_date"; } void store_now_in_TIME(THD* thd, MYSQL_TIME *now_time); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_curdate_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_curdate_utc>(thd, this); } }; @@ -780,8 +780,8 @@ public: int save_in_field(Field *field, bool no_conversions); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); virtual enum Functype functype() const { return NOW_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_now_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_now_local>(thd, this); } }; @@ -797,8 +797,8 @@ public: return mark_unsupported_function(func_name(), "()", arg, VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_now_utc>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_now_utc>(thd, this); } }; @@ -821,8 +821,8 @@ public: VCOL_TIME_FUNC | VCOL_NON_DETERMINISTIC); } virtual enum Functype functype() const { return SYSDATE_FUNC; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sysdate_local>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sysdate_local>(thd, this); } }; @@ -840,8 +840,8 @@ public: { return has_date_args() || has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_days>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_days>(thd, this); } }; @@ -868,8 +868,8 @@ public: return false; return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_date_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_date_format>(thd, this); } }; class Item_func_time_format: public Item_func_date_format @@ -879,8 +879,8 @@ public: Item_func_date_format(thd, a, b) { is_time_format= true; } const char *func_name() const { return "time_format"; } bool check_vcol_func_processor(void *arg) { return false; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_time_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_time_format>(thd, this); } }; @@ -894,8 +894,8 @@ class Item_func_from_unixtime :public Item_datetimefunc const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_from_unixtime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_from_unixtime>(thd, this); } }; @@ -939,8 +939,8 @@ class Item_func_convert_tz :public Item_datetimefunc } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); void cleanup(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_convert_tz>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_convert_tz>(thd, this); } }; @@ -957,8 +957,8 @@ public: maybe_null= true; } const char *func_name() const { return "sec_to_time"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_sec_to_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_sec_to_time>(thd, this); } }; @@ -978,8 +978,8 @@ public: void print(String *str, enum_query_type query_type); enum precedence precedence() const { return ADDINTERVAL_PRECEDENCE; } bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_add_interval>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_add_interval>(thd, this); } }; @@ -1086,8 +1086,8 @@ class Item_extract :public Item_int_func } return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_extract>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_extract>(thd, this); } }; @@ -1099,10 +1099,10 @@ class Item_char_typecast :public Item_str_func String tmp_value; bool m_suppress_warning_to_error_escalation; bool has_explicit_length() const { return cast_length != ~0U; } - String *reuse(String *src, uint32 length); + String *reuse(String *src, size_t length); String *copy(String *src, CHARSET_INFO *cs); uint adjusted_length_with_warn(uint length); - void check_truncation_with_warn(String *src, uint dstlen); + void check_truncation_with_warn(String *src, size_t dstlen); void fix_length_and_dec_internal(CHARSET_INFO *fromcs); public: Item_char_typecast(THD *thd, Item *a, uint length_arg, CHARSET_INFO *cs_arg): @@ -1126,8 +1126,8 @@ public: } void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_char_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_char_typecast>(thd, this); } }; @@ -1151,8 +1151,8 @@ public: { args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_date_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_date_typecast>(thd, this); } }; @@ -1169,8 +1169,8 @@ public: { args[0]->type_handler()->Item_time_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_time_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_time_typecast>(thd, this); } }; @@ -1187,8 +1187,22 @@ public: { args[0]->type_handler()->Item_datetime_typecast_fix_length_and_dec(this); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_datetime_typecast>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_datetime_typecast>(thd, this); } +}; + + +class Item_datetime_from_unixtime_typecast :public Item_datetime_typecast +{ + THD *thd; +public: + Item_datetime_from_unixtime_typecast(THD *_thd, Item *a, uint dec_arg): + Item_datetime_typecast(_thd, a, dec_arg), thd(_thd) {} + const char *func_name() const { return "cast_as_datetime_from_unixtime"; } + const char *cast_type() const { return "datetime"; } + void fix_length_and_dec(); + Item *get_copy(THD *thd) + { return get_item_copy<Item_datetime_from_unixtime_typecast>(thd, this); } }; @@ -1201,8 +1215,8 @@ public: Item_datefunc(thd, a, b) {} const char *func_name() const { return "makedate"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_makedate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_makedate>(thd, this); } }; @@ -1219,8 +1233,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); void print(String *str, enum_query_type query_type); const char *func_name() const { return "add_time"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_add_time>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_add_time>(thd, this); } }; class Item_func_timediff :public Item_timefunc @@ -1237,8 +1251,8 @@ public: maybe_null= true; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_timediff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_timediff>(thd, this); } }; class Item_func_maketime :public Item_timefunc @@ -1259,8 +1273,8 @@ public: } const char *func_name() const { return "maketime"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_maketime>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_maketime>(thd, this); } }; @@ -1282,8 +1296,8 @@ public: { return !has_time_args(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_microsecond>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_microsecond>(thd, this); } }; @@ -1303,8 +1317,8 @@ public: maybe_null=1; } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_timestamp_diff>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_timestamp_diff>(thd, this); } }; @@ -1329,8 +1343,8 @@ public: fix_length_and_charset(17, default_charset()); } virtual void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_get_format>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_get_format>(thd, this); } }; @@ -1349,8 +1363,8 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); const char *func_name() const { return "str_to_date"; } void fix_length_and_dec(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_str_to_date>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_str_to_date>(thd, this); } }; @@ -1362,8 +1376,8 @@ 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); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_last_day>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_last_day>(thd, this); } }; #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/item_vers.cc b/sql/item_vers.cc new file mode 100644 index 00000000000..32a6c67f2fe --- /dev/null +++ b/sql/item_vers.cc @@ -0,0 +1,182 @@ +/* Copyright (c) 2017, 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 */ + + +/** + @brief + System Versioning items +*/ + +#include "mariadb.h" +#include "sql_priv.h" + +#include "sql_class.h" +#include "tztime.h" +#include "item.h" + +Item_func_vtq_ts::Item_func_vtq_ts(THD *thd, Item* a, TR_table::field_id_t _vtq_field) : + Item_datetimefunc(thd, a), + vtq_field(_vtq_field) +{ + decimals= 6; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + + +bool +Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + DBUG_ASSERT(args[0]); + if (args[0]->result_type() != INT_RESULT) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + func_name()); + return true; + } + ulonglong trx_id= args[0]->val_uint(); + if (trx_id == ULONGLONG_MAX) + { + null_value= false; + thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); + res->second_part= TIME_MAX_SECOND_PART; + return false; + } + + TR_table trt(thd); + + null_value= !trt.query(trx_id); + if (null_value) + { + my_error(ER_VERS_NO_TRX_ID, MYF(0), (longlong) trx_id); + return true; + } + + return trt[vtq_field]->get_date(res, fuzzy_date); +} + + +Item_func_vtq_id::Item_func_vtq_id(THD *thd, Item* a, TR_table::field_id_t _vtq_field, + bool _backwards) : + Item_longlong_func(thd, a), + vtq_field(_vtq_field), + backwards(_backwards) +{ + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +Item_func_vtq_id::Item_func_vtq_id(THD *thd, Item* a, Item* b, TR_table::field_id_t _vtq_field) : + Item_longlong_func(thd, a, b), + vtq_field(_vtq_field), + backwards(false) +{ + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_id::get_by_trx_id(ulonglong trx_id) +{ + THD *thd= current_thd; + DBUG_ASSERT(thd); + + if (trx_id == ULONGLONG_MAX) + { + null_value= true; + return 0; + } + + TR_table trt(thd); + null_value= !trt.query(trx_id); + if (null_value) + return 0; + + return trt[vtq_field]->val_int(); +} + +longlong +Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) +{ + THD *thd= current_thd; + DBUG_ASSERT(thd); + + TR_table trt(thd); + null_value= !trt.query(commit_ts, backwards); + if (null_value) + return 0; + + return trt[vtq_field]->val_int(); +} + +longlong +Item_func_vtq_id::val_int() +{ + if (args[0]->is_null()) + { + if (arg_count < 2 || vtq_field == TR_table::FLD_TRX_ID) + { + null_value= true; + return 0; + } + return get_by_trx_id(args[1]->val_uint()); + } + else + { + MYSQL_TIME commit_ts; + if (args[0]->get_date(&commit_ts, 0)) + { + null_value= true; + return 0; + } + if (arg_count > 1) + { + backwards= args[1]->val_bool(); + DBUG_ASSERT(arg_count == 2); + } + return get_by_commit_ts(commit_ts, backwards); + } +} + +Item_func_vtq_trx_sees::Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b) : + Item_bool_func(thd, a, b), + accept_eq(false) +{ + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_trx_sees::val_int() +{ + THD *thd= current_thd; + DBUG_ASSERT(thd); + + DBUG_ASSERT(arg_count > 1); + ulonglong trx_id1= args[0]->val_uint(); + ulonglong trx_id0= args[1]->val_uint(); + bool result= accept_eq; + + TR_table trt(thd); + null_value= trt.query_sees(result, trx_id1, trx_id0); + return result; +} diff --git a/sql/item_vers.h b/sql/item_vers.h new file mode 100644 index 00000000000..39ed4ecda1f --- /dev/null +++ b/sql/item_vers.h @@ -0,0 +1,114 @@ +#ifndef ITEM_VERS_INCLUDED +#define ITEM_VERS_INCLUDED +/* Copyright (c) 2017, 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 */ + + +/* System Versioning items */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +class Item_func_vtq_ts: public Item_datetimefunc +{ + TR_table::field_id_t vtq_field; +public: + Item_func_vtq_ts(THD *thd, Item* a, TR_table::field_id_t _vtq_field); + const char *func_name() const + { + if (vtq_field == TR_table::FLD_BEGIN_TS) + { + return "vtq_begin_ts"; + } + return "vtq_commit_ts"; + } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_vtq_ts>(thd, this); } + void fix_length_and_dec() { fix_attributes_datetime(decimals); } +}; + +class Item_func_vtq_id : public Item_longlong_func +{ + TR_table::field_id_t vtq_field; + bool backwards; + + longlong get_by_trx_id(ulonglong trx_id); + longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards); + +public: + Item_func_vtq_id(THD *thd, Item* a, TR_table::field_id_t _vtq_field, bool _backwards= false); + Item_func_vtq_id(THD *thd, Item* a, Item* b, TR_table::field_id_t _vtq_field); + + const char *func_name() const + { + switch (vtq_field) + { + case TR_table::FLD_TRX_ID: + return "vtq_trx_id"; + case TR_table::FLD_COMMIT_ID: + return "vtq_commit_id"; + case TR_table::FLD_ISO_LEVEL: + return "vtq_iso_level"; + default: + DBUG_ASSERT(0); + } + return NULL; + } + + void fix_length_and_dec() + { + Item_int_func::fix_length_and_dec(); + max_length= 20; + } + + longlong val_int(); + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_vtq_id>(thd, this); } +}; + +class Item_func_vtq_trx_sees : public Item_bool_func +{ +protected: + bool accept_eq; + +public: + Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b); + const char *func_name() const + { + return "vtq_trx_sees"; + } + longlong val_int(); + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_vtq_trx_sees>(thd, this); } +}; + +class Item_func_vtq_trx_sees_eq : + public Item_func_vtq_trx_sees +{ +public: + Item_func_vtq_trx_sees_eq(THD *thd, Item* a, Item* b) : + Item_func_vtq_trx_sees(thd, a, b) + { + accept_eq= true; + } + const char *func_name() const + { + return "vtq_trx_sees_eq"; + } +}; + +#endif /* ITEM_VERS_INCLUDED */ diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc index 6b703d5244f..85ca8b0624e 100644 --- a/sql/item_windowfunc.cc +++ b/sql/item_windowfunc.cc @@ -1,6 +1,21 @@ +/* + Copyright (c) 2016,2017 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 */ + +#include "mariadb.h" #include "item_windowfunc.h" -#include "my_dbug.h" -#include "my_global.h" #include "sql_select.h" // test if group changed @@ -93,6 +108,7 @@ Item_window_func::fix_fields(THD *thd, Item **ref) my_error(ER_NO_ORDER_LIST_IN_WINDOW_SPEC, MYF(0), window_func()->func_name()); return true; } + /* TODO: why the last parameter is 'ref' in this call? What if window_func decides to substitute itself for something else and does *ref=.... ? @@ -153,10 +169,25 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, window_func()->setup_caches(thd); } +bool Item_window_func::check_result_type_of_order_item() +{ + if (only_single_element_order_list()) + { + Item_result rtype= window_spec->order_list->first->item[0]->cmp_type(); + // TODO (varun) : support date type in percentile_cont function + if (rtype != REAL_RESULT && rtype != INT_RESULT && + rtype != DECIMAL_RESULT && rtype != TIME_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name()); + return TRUE; + } + setting_handler_for_percentile_functions(rtype); + } + return FALSE; +} /* This must be called before attempting to compute the window function values. - @detail If we attempt to do it in fix_fields(), partition_fields will refer to the original window function arguments. @@ -179,6 +210,71 @@ void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec) clear(); } +void Item_sum_percentile_disc::setup_window_func(THD *thd, Window_spec *window_spec) +{ + order_item= window_spec->order_list->first->item[0]; + if (!(value= order_item->get_cache(thd))) + return; + value->setup(thd, order_item); + value->store(order_item); +} + +void Item_sum_percentile_cont::setup_window_func(THD *thd, Window_spec *window_spec) +{ + order_item= window_spec->order_list->first->item[0]; + /* TODO(varun): need to discuss and finalise what type should we + return for percentile cont functions + */ + if (!(ceil_value= order_item->get_cache(thd))) + return; + ceil_value->setup(thd, order_item); + ceil_value->store(order_item); + + if (!(floor_value= order_item->get_cache(thd))) + return; + floor_value->setup(thd, order_item); + floor_value->store(order_item); +} +bool Item_sum_percentile_cont::fix_fields(THD *thd, Item **ref) +{ + bool res; + res= Item_sum_num::fix_fields(thd, ref); + if (res) + return res; + + switch(args[0]->cmp_type()) + { + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + break; + default: + my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name()); + return TRUE; + } + return res; +} +bool Item_sum_percentile_disc::fix_fields(THD *thd, Item **ref) +{ + bool res; + res= Item_sum_num::fix_fields(thd, ref); + if (res) + return res; + + switch(args[0]->cmp_type()) + { + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + break; + default: + my_error(ER_WRONG_TYPE_OF_ARGUMENT, MYF(0), func_name()); + return TRUE; + } + return res; + +} + bool Item_sum_dense_rank::add() { if (peer_tracker->check_if_next_group() || first_add) diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index 717958d479c..cb85a2c06d1 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -1,7 +1,22 @@ +/* + Copyright (c) 2016,2017 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 ITEM_WINDOWFUNC_INCLUDED #define ITEM_WINDOWFUNC_INCLUDED -#include "my_global.h" #include "item.h" class Window_spec; @@ -9,6 +24,7 @@ class Window_spec; int test_if_group_changed(List<Cached_item> &list); + /* A wrapper around test_if_group_changed */ class Group_bound_tracker { @@ -16,10 +32,10 @@ public: Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list) { - for (ORDER *curr = list->first; curr; curr=curr->next) + for (ORDER *curr = list->first; curr; curr=curr->next) { - Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE); - group_fields.push_back(tmp); + Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE); + group_fields.push_back(tmp); } } @@ -130,8 +146,8 @@ public: return "row_number"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_row_number>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_row_number>(thd, this); } }; @@ -205,8 +221,8 @@ public: } Item_sum_int::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_rank>(thd, this); } }; @@ -275,8 +291,8 @@ class Item_sum_dense_rank: public Item_sum_int } Item_sum_int::cleanup(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_dense_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_dense_rank>(thd, this); } }; class Item_sum_hybrid_simple : public Item_sum, @@ -339,8 +355,8 @@ class Item_sum_first_value : public Item_sum_hybrid_simple return "first_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_first_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_first_value>(thd, this); } }; /* @@ -365,8 +381,8 @@ class Item_sum_last_value : public Item_sum_hybrid_simple return "last_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_last_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_last_value>(thd, this); } }; class Item_sum_nth_value : public Item_sum_hybrid_simple @@ -385,8 +401,8 @@ class Item_sum_nth_value : public Item_sum_hybrid_simple return "nth_value"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_nth_value>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_nth_value>(thd, this); } }; class Item_sum_lead : public Item_sum_hybrid_simple @@ -405,8 +421,8 @@ class Item_sum_lead : public Item_sum_hybrid_simple return "lead"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_lead>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_lead>(thd, this); } }; class Item_sum_lag : public Item_sum_hybrid_simple @@ -425,8 +441,8 @@ class Item_sum_lag : public Item_sum_hybrid_simple return "lag"; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_lag>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_lag>(thd, this); } }; /* @@ -517,8 +533,8 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count } void setup_window_func(THD *thd, Window_spec *window_spec); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_percent_rank>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percent_rank>(thd, this); } private: longlong cur_rank; // Current rank of the current row. @@ -558,6 +574,9 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count Item_sum_cume_dist(THD *thd) : Item_sum_window_with_row_count(thd), current_row_count_(0) {} + Item_sum_cume_dist(THD *thd, Item *arg) : Item_sum_window_with_row_count(thd,arg), + current_row_count_(0) {} + double val_real() { if (get_row_count() == 0) @@ -601,8 +620,13 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count // requires. } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_cume_dist>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_cume_dist>(thd, this); } + + ulonglong get_row_number() + { + return current_row_count_ ; + } private: ulonglong current_row_count_; @@ -671,14 +695,285 @@ class Item_sum_ntile : public Item_sum_window_with_row_count const Type_handler *type_handler() const { return &type_handler_longlong; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_sum_ntile>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_ntile>(thd, this); } private: longlong get_num_quantiles() { return args[0]->val_int(); } ulong current_row_count_; }; +class Item_sum_percentile_disc : public Item_sum_cume_dist, + public Type_handler_hybrid_field_type +{ +public: + Item_sum_percentile_disc(THD *thd, Item* arg) : Item_sum_cume_dist(thd, arg), + Type_handler_hybrid_field_type(&type_handler_longlong), + value(NULL), val_calculated(FALSE), first_call(TRUE), + prev_value(0), order_item(NULL){} + + double val_real() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_real(); + } + + longlong val_int() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_int(); + } + + my_decimal* val_decimal(my_decimal* dec) + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_decimal(dec); + } + + String* val_str(String *str) + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + return value->val_str(str); + } + + bool add() + { + Item *arg= get_arg(0); + if (arg->is_null()) + return false; + + if (first_call) + { + prev_value= arg->val_real(); + if (prev_value > 1 || prev_value < 0) + { + my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name()); + return true; + } + first_call= false; + } + + double arg_val= arg->val_real(); + + if (prev_value != arg_val) + { + my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name()); + return true; + } + + if (val_calculated) + return false; + + value->store(order_item); + value->cache_value(); + if (value->null_value) + return false; + + Item_sum_cume_dist::add(); + double val= Item_sum_cume_dist::val_real(); + + if (val >= prev_value && !val_calculated) + val_calculated= true; + return false; + } + + enum Sumfunctype sum_func() const + { + return PERCENTILE_DISC_FUNC; + } + + void clear() + { + val_calculated= false; + first_call= true; + value->clear(); + Item_sum_cume_dist::clear(); + } + + const char*func_name() const + { + return "percentile_disc"; + } + + void update_field() {} + void set_type_handler(Window_spec *window_spec); + const Type_handler *type_handler() const + {return Type_handler_hybrid_field_type::type_handler();} + + void fix_length_and_dec() + { + decimals = 10; // TODO-cvicentiu find out how many decimals the standard + // requires. + } + + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percentile_disc>(thd, this); } + void setup_window_func(THD *thd, Window_spec *window_spec); + void setup_hybrid(THD *thd, Item *item); + bool fix_fields(THD *thd, Item **ref); + +private: + Item_cache *value; + bool val_calculated; + bool first_call; + double prev_value; + Item *order_item; +}; + +class Item_sum_percentile_cont : public Item_sum_cume_dist, + public Type_handler_hybrid_field_type +{ +public: + Item_sum_percentile_cont(THD *thd, Item* arg) : Item_sum_cume_dist(thd, arg), + Type_handler_hybrid_field_type(&type_handler_double), + floor_value(NULL), ceil_value(NULL), first_call(TRUE),prev_value(0), + ceil_val_calculated(FALSE), floor_val_calculated(FALSE), order_item(NULL){} + + double val_real() + { + if (get_row_count() == 0 || get_arg(0)->is_null()) + { + null_value= true; + return 0; + } + null_value= false; + double val= 1 + prev_value * (get_row_count()-1); + + /* + Applying the formula to get the value + If (CRN = FRN = RN) then the result is (value of expression from row at RN) + Otherwise the result is + (CRN - RN) * (value of expression for row at FRN) + + (RN - FRN) * (value of expression for row at CRN) + */ + + if(ceil(val) == floor(val)) + return floor_value->val_real(); + + double ret_val= ((val - floor(val)) * ceil_value->val_real()) + + ((ceil(val) - val) * floor_value->val_real()); + + return ret_val; + } + + bool add() + { + Item *arg= get_arg(0); + if (arg->is_null()) + return false; + + if (first_call) + { + first_call= false; + prev_value= arg->val_real(); + if (prev_value > 1 || prev_value < 0) + { + my_error(ER_ARGUMENT_OUT_OF_RANGE, MYF(0), func_name()); + return true; + } + } + + double arg_val= arg->val_real(); + if (prev_value != arg_val) + { + my_error(ER_ARGUMENT_NOT_CONSTANT, MYF(0), func_name()); + return true; + } + + if (!floor_val_calculated) + { + floor_value->store(order_item); + floor_value->cache_value(); + if (floor_value->null_value) + return false; + } + if (floor_val_calculated && !ceil_val_calculated) + { + ceil_value->store(order_item); + ceil_value->cache_value(); + if (ceil_value->null_value) + return false; + } + + Item_sum_cume_dist::add(); + double val= 1 + prev_value * (get_row_count()-1); + + if (!floor_val_calculated && get_row_number() == floor(val)) + floor_val_calculated= true; + + if (!ceil_val_calculated && get_row_number() == ceil(val)) + ceil_val_calculated= true; + return false; + } + + enum Sumfunctype sum_func() const + { + return PERCENTILE_CONT_FUNC; + } + + void clear() + { + first_call= true; + floor_value->clear(); + ceil_value->clear(); + floor_val_calculated= false; + ceil_val_calculated= false; + Item_sum_cume_dist::clear(); + } + + const char*func_name() const + { + return "percentile_cont"; + } + void update_field() {} + void set_type_handler(Window_spec *window_spec); + const Type_handler *type_handler() const + {return Type_handler_hybrid_field_type::type_handler();} + + void fix_length_and_dec() + { + decimals = 10; // TODO-cvicentiu find out how many decimals the standard + // requires. + } + + Item *get_copy(THD *thd) + { return get_item_copy<Item_sum_percentile_cont>(thd, this); } + void setup_window_func(THD *thd, Window_spec *window_spec); + void setup_hybrid(THD *thd, Item *item); + bool fix_fields(THD *thd, Item **ref); + +private: + Item_cache *floor_value; + Item_cache *ceil_value; + bool first_call; + double prev_value; + bool ceil_val_calculated; + bool floor_val_calculated; + Item *order_item; +}; + + + class Item_window_func : public Item_func_or_sum { @@ -733,6 +1028,8 @@ public: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: case Item_sum::NTILE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; @@ -759,6 +1056,8 @@ public: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: case Item_sum::NTILE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; @@ -782,12 +1081,40 @@ public: case Item_sum::DENSE_RANK_FUNC: case Item_sum::PERCENT_RANK_FUNC: case Item_sum::CUME_DIST_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return true; default: return false; } } + bool only_single_element_order_list() const + { + switch (window_func()->sum_func()){ + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: + return true; + default: + return false; + } + } + + void setting_handler_for_percentile_functions(Item_result rtype) const + { + switch (window_func()->sum_func()){ + case Item_sum::PERCENTILE_DISC_FUNC: + ((Item_sum_percentile_disc* ) window_func())->set_handler_by_cmp_type(rtype); + break; + default: + return; + } + } + + bool check_result_type_of_order_item(); + + + /* Computation functions. TODO: consoder merging these with class Group_bound_tracker. @@ -978,7 +1305,7 @@ public: void print(String *str, enum_query_type query_type); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item *get_copy(THD *thd) { return 0; } }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index af32bf9a46d..5156bd08ef2 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -247,8 +247,8 @@ public: Item_nodeset_func(thd, pxml) {} const char *func_name() const { return "xpath_rootelement"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_rootelement>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_rootelement>(thd, this); } }; @@ -260,8 +260,8 @@ public: Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_union"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_union>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_union>(thd, this); } }; @@ -294,8 +294,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_selfbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_selfbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); } }; @@ -308,8 +308,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_childbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_childbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_childbyname>(thd, this); } }; @@ -324,8 +324,8 @@ public: need_self(need_self_arg) {} const char *func_name() const { return "xpath_descendantbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); } }; @@ -340,8 +340,8 @@ public: need_self(need_self_arg) {} const char *func_name() const { return "xpath_ancestorbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); } }; @@ -354,8 +354,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_parentbyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_parentbyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); } }; @@ -368,8 +368,8 @@ public: Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {} const char *func_name() const { return "xpath_attributebyname"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_attributebyname>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); } }; @@ -385,8 +385,8 @@ public: Item_nodeset_func(thd, a, b, pxml) {} const char *func_name() const { return "xpath_predicate"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_predicate>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_predicate>(thd, this); } }; @@ -398,8 +398,8 @@ public: Item_nodeset_func(thd, a, b, pxml) { } const char *func_name() const { return "xpath_elementbyindex"; } String *val_nodeset(String *nodeset); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); } }; @@ -426,8 +426,8 @@ public: } return args[0]->val_real() ? 1 : 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_xpath_cast_bool>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_xpath_cast_bool>(thd, this); } }; @@ -440,8 +440,8 @@ public: Item_xpath_cast_number(THD *thd, Item *a): Item_real_func(thd, a) {} const char *func_name() const { return "xpath_cast_number"; } virtual double val_real() { return args[0]->val_real(); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_xpath_cast_number>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_xpath_cast_number>(thd, this); } }; @@ -457,8 +457,8 @@ public: String *val_nodeset(String *res) { return string_cache; } void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_context_cache>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_context_cache>(thd, this); } }; @@ -478,8 +478,8 @@ public: return ((MY_XPATH_FLT*)flt->ptr())->pos + 1; return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_position>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_position>(thd, this); } }; @@ -501,8 +501,8 @@ public: return predicate_supplied_context_size; return res->length() / sizeof(MY_XPATH_FLT); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_count>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_count>(thd, this); } }; @@ -546,8 +546,8 @@ public: } return sum; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xpath_sum>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xpath_sum>(thd, this); } }; @@ -624,8 +624,8 @@ public: } return 0; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_nodeset_to_const_comparator>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_nodeset_to_const_comparator>(thd, this); } }; @@ -2638,7 +2638,7 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) { Item_splocal *splocal= new (thd->mem_root) Item_splocal(thd, &name, spv->offset, spv->type_handler(), 0); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (splocal) splocal->m_sp= lex->sphead; #endif diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index d281e076ec8..33e85ba5628 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -97,8 +97,8 @@ public: Item_xml_str_func(thd, a, b) {} const char *func_name() const { return "extractvalue"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xml_extractvalue>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xml_extractvalue>(thd, this); } }; @@ -113,8 +113,8 @@ public: Item_xml_str_func(thd, a, b, c) {} const char *func_name() const { return "updatexml"; } String *val_str(String *); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_xml_update>(thd, mem_root, this); } + Item *get_copy(THD *thd) + { return get_item_copy<Item_func_xml_update>(thd, this); } }; #endif /* ITEM_XMLFUNC_INCLUDED */ diff --git a/sql/key.cc b/sql/key.cc index 6369faff9d0..5c1afb1f82d 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -17,7 +17,7 @@ /* Functions to handle keys and fields in forms */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // key_rec_cmp #include "field.h" // Field @@ -319,7 +319,7 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length) FIELDFLAG_PACK))) { CHARSET_INFO *cs= key_part->field->charset(); - uint char_length= key_part->length / cs->mbmaxlen; + size_t char_length= key_part->length / cs->mbmaxlen; const uchar *pos= table->record[0] + key_part->offset; if (length > char_length) { @@ -385,7 +385,7 @@ void field_unpack(String *to, Field *field, const uchar *rec, uint max_length, which can break a multi-byte characters in the middle. Align, returning not more than "char_length" characters. */ - uint charpos, char_length= max_length / cs->mbmaxlen; + size_t charpos, char_length= max_length / cs->mbmaxlen; if ((charpos= my_charpos(cs, tmp.ptr(), tmp.ptr() + tmp.length(), char_length)) < tmp.length()) @@ -697,7 +697,7 @@ ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key) { uchar *pos= (uchar*)key; CHARSET_INFO *UNINIT_VAR(cs); - uint UNINIT_VAR(length), UNINIT_VAR(pack_length); + size_t UNINIT_VAR(length), UNINIT_VAR(pack_length); bool is_string= TRUE; key+= key_part->length; @@ -754,7 +754,7 @@ ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key) { if (cs->mbmaxlen > 1) { - uint char_length= my_charpos(cs, pos + pack_length, + size_t char_length= my_charpos(cs, pos + pack_length, pos + pack_length + length, length / cs->mbmaxlen); set_if_smaller(length, char_length); @@ -801,7 +801,7 @@ bool key_buf_cmp(KEY *key_info, uint used_key_parts, uchar *pos1= (uchar*)key1; uchar *pos2= (uchar*)key2; CHARSET_INFO *UNINIT_VAR(cs); - uint UNINIT_VAR(length1), UNINIT_VAR(length2), UNINIT_VAR(pack_length); + size_t UNINIT_VAR(length1), UNINIT_VAR(length2), UNINIT_VAR(pack_length); bool is_string= TRUE; key1+= key_part->length; @@ -865,13 +865,13 @@ bool key_buf_cmp(KEY *key_info, uint used_key_parts, Compare the strings taking into account length in characters and collation */ - uint byte_len1= length1, byte_len2= length2; + size_t byte_len1= length1, byte_len2= length2; if (cs->mbmaxlen > 1) { - uint char_length1= my_charpos(cs, pos1 + pack_length, + size_t char_length1= my_charpos(cs, pos1 + pack_length, pos1 + pack_length + length1, length1 / cs->mbmaxlen); - uint char_length2= my_charpos(cs, pos2 + pack_length, + size_t char_length2= my_charpos(cs, pos2 + pack_length, pos2 + pack_length + length2, length2 / cs->mbmaxlen); set_if_smaller(length1, char_length1); diff --git a/sql/key.h b/sql/key.h index 7b83d74c901..523d0b7f10b 100644 --- a/sql/key.h +++ b/sql/key.h @@ -16,8 +16,6 @@ #ifndef KEY_INCLUDED #define KEY_INCLUDED -#include "my_global.h" /* uchar */ - class Field; class String; struct TABLE; diff --git a/sql/keycaches.cc b/sql/keycaches.cc index 336f2611a7e..9db51ee1801 100644 --- a/sql/keycaches.cc +++ b/sql/keycaches.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "keycaches.h" /**************************************************************************** @@ -29,17 +30,17 @@ class NAMED_ILINK :public ilink { public: const char *name; - uint name_length; + size_t name_length; uchar* data; NAMED_ILINK(I_List<NAMED_ILINK> *links, const char *name_arg, - uint name_length_arg, uchar* data_arg) + size_t name_length_arg, uchar* data_arg) :name_length(name_length_arg), data(data_arg) { name= my_strndup(name_arg, name_length, MYF(MY_WME)); links->push_back(this); } - inline bool cmp(const char *name_cmp, uint length) + inline bool cmp(const char *name_cmp, size_t length) { return length == name_length && !memcmp(name, name_cmp, length); } @@ -49,7 +50,7 @@ public: } }; -uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length, +uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, size_t length, NAMED_ILINK **found) { I_List_iterator<NAMED_ILINK> it(*list); @@ -67,7 +68,7 @@ uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length, } -bool NAMED_ILIST::delete_element(const char *name, uint length, void (*free_element)(const char *name, uchar*)) +bool NAMED_ILIST::delete_element(const char *name, size_t length, void (*free_element)(const char *name, uchar*)) { I_List_iterator<NAMED_ILINK> it(*this); NAMED_ILINK *element; @@ -99,7 +100,7 @@ void NAMED_ILIST::delete_elements(void (*free_element)(const char *name, uchar*) /* Key cache functions */ -LEX_CSTRING default_key_cache_base= {C_STRING_WITH_LEN("default")}; +LEX_CSTRING default_key_cache_base= {STRING_WITH_LEN("default")}; KEY_CACHE zero_key_cache; ///< @@nonexistent_cache.param->value_ptr() points here @@ -111,11 +112,11 @@ KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name) cache_name->str, cache_name->length, 0)); } -KEY_CACHE *create_key_cache(const char *name, uint length) +KEY_CACHE *create_key_cache(const char *name, size_t length) { KEY_CACHE *key_cache; DBUG_ENTER("create_key_cache"); - DBUG_PRINT("enter",("name: %.*s", length, name)); + DBUG_PRINT("enter",("name: %.*s", (int)length, name)); if ((key_cache= (KEY_CACHE*) my_malloc(sizeof(KEY_CACHE), MYF(MY_ZEROFILL | MY_WME)))) @@ -143,7 +144,7 @@ KEY_CACHE *create_key_cache(const char *name, uint length) } -KEY_CACHE *get_or_create_key_cache(const char *name, uint length) +KEY_CACHE *get_or_create_key_cache(const char *name, size_t length) { LEX_CSTRING key_cache_name; KEY_CACHE *key_cache; @@ -179,9 +180,9 @@ bool process_key_caches(process_key_cache_t func, void *param) /* Rpl_filter functions */ -LEX_STRING default_rpl_filter_base= {C_STRING_WITH_LEN("")}; +LEX_CSTRING default_rpl_filter_base= {STRING_WITH_LEN("")}; -Rpl_filter *get_rpl_filter(LEX_STRING *filter_name) +Rpl_filter *get_rpl_filter(LEX_CSTRING *filter_name) { if (!filter_name->length) filter_name= &default_rpl_filter_base; @@ -189,11 +190,11 @@ Rpl_filter *get_rpl_filter(LEX_STRING *filter_name) filter_name->str, filter_name->length, 0)); } -Rpl_filter *create_rpl_filter(const char *name, uint length) +Rpl_filter *create_rpl_filter(const char *name, size_t length) { Rpl_filter *filter; DBUG_ENTER("create_rpl_filter"); - DBUG_PRINT("enter",("name: %.*s", length, name)); + DBUG_PRINT("enter",("name: %.*s", (int)length, name)); filter= new Rpl_filter; if (filter) @@ -208,9 +209,9 @@ Rpl_filter *create_rpl_filter(const char *name, uint length) } -Rpl_filter *get_or_create_rpl_filter(const char *name, uint length) +Rpl_filter *get_or_create_rpl_filter(const char *name, size_t length) { - LEX_STRING rpl_filter_name; + LEX_CSTRING rpl_filter_name; Rpl_filter *filter; rpl_filter_name.str= (char *) name; diff --git a/sql/keycaches.h b/sql/keycaches.h index 1b58dccfe34..ff0380ba09a 100644 --- a/sql/keycaches.h +++ b/sql/keycaches.h @@ -31,7 +31,7 @@ class NAMED_ILIST: public I_List<NAMED_ILINK> { public: void delete_elements(void (*free_element)(const char*, uchar*)); - bool delete_element(const char *name, uint length, void (*free_element)(const char*, uchar*)); + bool delete_element(const char *name, size_t length, void (*free_element)(const char*, uchar*)); }; /* For key cache */ @@ -39,19 +39,19 @@ extern LEX_CSTRING default_key_cache_base; extern KEY_CACHE zero_key_cache; extern NAMED_ILIST key_caches; -KEY_CACHE *create_key_cache(const char *name, uint length); +KEY_CACHE *create_key_cache(const char *name, size_t length); KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name); -KEY_CACHE *get_or_create_key_cache(const char *name, uint length); +KEY_CACHE *get_or_create_key_cache(const char *name, size_t length); void free_key_cache(const char *name, KEY_CACHE *key_cache); bool process_key_caches(process_key_cache_t func, void *param); /* For Rpl_filter */ -extern LEX_STRING default_rpl_filter_base; +extern LEX_CSTRING default_rpl_filter_base; extern NAMED_ILIST rpl_filters; -Rpl_filter *create_rpl_filter(const char *name, uint length); -Rpl_filter *get_rpl_filter(LEX_STRING *filter_name); -Rpl_filter *get_or_create_rpl_filter(const char *name, uint length); +Rpl_filter *create_rpl_filter(const char *name, size_t length); +Rpl_filter *get_rpl_filter(LEX_CSTRING *filter_name); +Rpl_filter *get_or_create_rpl_filter(const char *name, size_t length); void free_rpl_filter(const char *name, Rpl_filter *filter); void free_all_rpl_filters(void); diff --git a/sql/lex.h b/sql/lex.h index ec2ca7de564..41a81c737f7 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -273,6 +273,7 @@ static SYMBOL symbols[] = { { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY)}, + { "HISTORY", SYM(HISTORY_SYM)}, { "HOST", SYM(HOST_SYM)}, { "HOSTS", SYM(HOSTS_SYM)}, { "HOUR", SYM(HOUR_SYM)}, @@ -308,6 +309,7 @@ static SYMBOL symbols[] = { { "INT8", SYM(BIGINT)}, { "INTEGER", SYM(INT_SYM)}, { "INTERVAL", SYM(INTERVAL_SYM)}, + { "INVISIBLE", SYM(INVISIBLE_SYM)}, { "INTO", SYM(INTO)}, { "IO", SYM(IO_SYM)}, { "IO_THREAD", SYM(RELAY_THREAD)}, @@ -424,6 +426,7 @@ static SYMBOL symbols[] = { { "NOCACHE", SYM(NOCACHE_SYM)}, { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, + { "NOWAIT", SYM(NOWAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, @@ -458,6 +461,7 @@ static SYMBOL symbols[] = { { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, { "PARSER", SYM(PARSER_SYM)}, { "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)}, + { "PERIOD", SYM(PERIOD_SYM)}, { "PARTIAL", SYM(PARTIAL)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, @@ -627,6 +631,8 @@ static SYMBOL symbols[] = { { "SUSPEND", SYM(SUSPEND_SYM)}, { "SWAPS", SYM(SWAPS_SYM)}, { "SWITCHES", SYM(SWITCHES_SYM)}, + { "SYSTEM", SYM(SYSTEM)}, + { "SYSTEM_TIME", SYM(SYSTEM_TIME_SYM)}, { "TABLE", SYM(TABLE_SYM)}, { "TABLE_NAME", SYM(TABLE_NAME_SYM)}, { "TABLES", SYM(TABLES)}, @@ -692,6 +698,7 @@ static SYMBOL symbols[] = { { "VIA", SYM(VIA_SYM)}, { "VIEW", SYM(VIEW_SYM)}, { "VIRTUAL", SYM(VIRTUAL_SYM)}, + { "VERSIONING", SYM(VERSIONING_SYM)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, @@ -701,6 +708,8 @@ static SYMBOL symbols[] = { { "WHILE", SYM(WHILE_SYM)}, { "WINDOW", SYM(WINDOW_SYM)}, { "WITH", SYM(WITH)}, + { "WITHIN", SYM(WITHIN)}, + { "WITHOUT", SYM(WITHOUT)}, { "WORK", SYM(WORK_SYM)}, { "WRAPPER", SYM(WRAPPER_SYM)}, { "WRITE", SYM(WRITE_SYM)}, @@ -736,6 +745,7 @@ static SYMBOL sql_functions[] = { { "LAG", SYM(LAG_SYM)}, { "LEAD", SYM(LEAD_SYM)}, { "MAX", SYM(MAX_SYM)}, + { "MEDIAN", SYM(MEDIAN_SYM)}, { "MID", SYM(SUBSTRING)}, /* unireg function */ { "MIN", SYM(MIN_SYM)}, { "NOW", SYM(NOW_SYM)}, @@ -743,6 +753,8 @@ static SYMBOL sql_functions[] = { { "NTILE", SYM(NTILE_SYM)}, { "POSITION", SYM(POSITION_SYM)}, { "PERCENT_RANK", SYM(PERCENT_RANK_SYM)}, + { "PERCENTILE_CONT", SYM(PERCENTILE_CONT_SYM)}, + { "PERCENTILE_DISC", SYM(PERCENTILE_DISC_SYM)}, { "RANK", SYM(RANK_SYM)}, { "ROW_NUMBER", SYM(ROW_NUMBER_SYM)}, { "SESSION_USER", SYM(USER_SYM)}, diff --git a/sql/lex_string.h b/sql/lex_string.h new file mode 100644 index 00000000000..9df84f67b6a --- /dev/null +++ b/sql/lex_string.h @@ -0,0 +1,52 @@ +/* + 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef LEX_STRING_INCLUDED +#define LEX_STRING_INCLUDED + +typedef struct st_mysql_const_lex_string LEX_CSTRING; + +/* Functions to compare if two lex strings are equal */ +inline bool lex_string_cmp(CHARSET_INFO *charset, + const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + return my_strcasecmp(charset, a->str, b->str); +} + +/* + Compare to LEX_CSTRING's and return 0 if equal +*/ +inline bool cmp(const LEX_CSTRING *a, const LEX_CSTRING *b) +{ + return (a->length != b->length || + memcmp(a->str, b->str, a->length)); +} + +/* + Compare if two LEX_CSTRING are equal. Assumption is that + character set is ASCII (like for plugin names) +*/ +inline bool lex_string_eq(const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + if (a->length != b->length) + return 1; /* Different */ + return strcasecmp(a->str, b->str) != 0; +} + +#endif /* LEX_STRING_INCLUDED */ diff --git a/sql/lock.cc b/sql/lock.cc index c6dd3724eba..864e9f76293 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -70,7 +70,7 @@ in case external_lock() fails. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "debug_sync.h" #include "lock.h" @@ -294,7 +294,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) if (lock_tables_check(thd, tables, count, flags)) DBUG_RETURN(NULL); - if (!(thd->variables.option_bits & OPTION_TABLE_LOCK)) + if (!(thd->variables.option_bits & OPTION_TABLE_LOCK) && + !(flags & MYSQL_LOCK_USE_MALLOC)) gld_flags|= GET_LOCK_ON_THD; if (! (sql_lock= get_lock_data(thd, tables, count, gld_flags))) @@ -415,7 +416,8 @@ static int lock_external(THD *thd, TABLE **tables, uint count) void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock) { mysql_unlock_tables(thd, sql_lock, - thd->variables.option_bits & OPTION_TABLE_LOCK); + (thd->variables.option_bits & OPTION_TABLE_LOCK) || + !(sql_lock->flags & GET_LOCK_ON_THD)); } @@ -433,7 +435,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) if (sql_lock->lock_count) thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0); if (free_lock) + { + DBUG_ASSERT(!(sql_lock->flags & GET_LOCK_ON_THD)); my_free(sql_lock); + } if (!errors) thd->clear_error(); THD_STAGE_INFO(thd, org_stage); @@ -668,6 +673,7 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) sql_lock->table_count=a->table_count+b->table_count; sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1); sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2); + sql_lock->flags= 0; memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks)); memcpy(sql_lock->locks+a->lock_count,b->locks, b->lock_count*sizeof(*b->locks)); @@ -782,6 +788,7 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1); to= table_buf= sql_lock->table= (TABLE**) (locks + lock_count * 2); sql_lock->table_count= table_count; + sql_lock->flags= flags; for (i=0 ; i < count ; i++) { @@ -915,7 +922,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type, MDL_request schema_request; MDL_request mdl_request; - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); if (thd->locked_tables_mode) { @@ -1086,8 +1093,8 @@ void Global_read_lock::unlock_global_read_lock(THD *thd) int ret = wsrep->resync(wsrep); if (ret != WSREP_OK) { - WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s", ret, - (thd->db ? thd->db : "(null)"), thd->query()); + WSREP_WARN("resync failed %d for FTWRL: db: %s, query: %s", + ret, thd->get_db(), thd->query()); DBUG_VOID_RETURN; } } @@ -1173,7 +1180,7 @@ bool Global_read_lock::make_global_read_lock_block_commit(THD *thd) if (rcode != WSREP_OK) { WSREP_WARN("FTWRL desync failed %d for schema: %s, query: %s", - rcode, (thd->db ? thd->db : "(null)"), thd->query()); + rcode, thd->get_db(), thd->query()); my_message(ER_LOCK_DEADLOCK, "wsrep desync failed for FTWRL", MYF(0)); DBUG_RETURN(TRUE); } diff --git a/sql/log.cc b/sql/log.cc index b6867bf6566..8ed53a60195 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -25,7 +25,7 @@ Abort logging when we get an error in reading or writing log files */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "log.h" #include "sql_base.h" // open_log_table @@ -39,11 +39,9 @@ #include "rpl_filter.h" #include "rpl_rli.h" #include "sql_audit.h" -#include "log_slow.h" #include "mysqld.h" #include <my_dir.h> -#include <stdarg.h> #include <m_ctype.h> // For test_if_number #ifdef _WIN32 @@ -51,11 +49,13 @@ #endif #include "sql_plugin.h" -#include "rpl_handler.h" #include "debug_sync.h" #include "sql_show.h" #include "my_pthread.h" +#include "semisync_master.h" #include "wsrep_mysqld.h" +#include "sp_rcontext.h" +#include "sp_head.h" /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 @@ -87,8 +87,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all); static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd); -static LEX_STRING const write_error_msg= - { C_STRING_WITH_LEN("error writing to the binary log") }; +static const LEX_CSTRING write_error_msg= + { STRING_WITH_LEN("error writing to the binary log") }; static my_bool opt_optimize_thread_scheduling= TRUE; ulong binlog_checksum_options; @@ -608,19 +608,19 @@ int check_if_log_table(const TABLE_LIST *table, const char *error_msg) { int result= 0; - if (table->db_length == 5 && - !my_strcasecmp(table_alias_charset, table->db, "mysql")) + if (table->db.length == 5 && + !my_strcasecmp(table_alias_charset, table->db.str, "mysql")) { - const char *table_name= table->table_name; + const char *table_name= table->table_name.str; - if (table->table_name_length == 11 && + if (table->table_name.length == 11 && !my_strcasecmp(table_alias_charset, table_name, "general_log")) { result= QUERY_LOG_GENERAL; goto end; } - if (table->table_name_length == 8 && + if (table->table_name.length == 8 && !my_strcasecmp(table_alias_charset, table_name, "slow_log")) { result= QUERY_LOG_SLOW; @@ -688,10 +688,9 @@ void Log_to_csv_event_handler::cleanup() */ bool Log_to_csv_event_handler:: - log_general(THD *thd, my_hrtime_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id_arg, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len, + log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len, CHARSET_INFO *client_cs) { TABLE_LIST table_list; @@ -716,9 +715,7 @@ bool Log_to_csv_event_handler:: save_thd_options= thd->variables.option_bits; thd->variables.option_bits&= ~OPTION_BIN_LOG; - table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, - GENERAL_LOG_NAME.str, GENERAL_LOG_NAME.length, - GENERAL_LOG_NAME.str, + table_list.init_one_table(&MYSQL_SCHEMA_NAME, &GENERAL_LOG_NAME, 0, TL_WRITE_CONCURRENT_INSERT); /* @@ -854,9 +851,9 @@ err: bool Log_to_csv_event_handler:: log_slow(THD *thd, my_hrtime_t current_time, - const char *user_host, uint user_host_len, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len) + const char *sql_text, size_t sql_text_len) { TABLE_LIST table_list; TABLE *table; @@ -881,9 +878,7 @@ bool Log_to_csv_event_handler:: */ save_time_zone_used= thd->time_zone_used; - table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, - SLOW_LOG_NAME.str, SLOW_LOG_NAME.length, - SLOW_LOG_NAME.str, + table_list.init_one_table(&MYSQL_SCHEMA_NAME, &SLOW_LOG_NAME, 0, TL_WRITE_CONCURRENT_INSERT); if (!(table= open_log_table(thd, &table_list, &open_tables_backup))) @@ -937,9 +932,9 @@ bool Log_to_csv_event_handler:: goto err; /* fill database field */ - if (thd->db) + if (thd->db.str) { - if (table->field[6]->store(thd->db, thd->db_length, client_cs)) + if (table->field[6]->store(thd->db.str, thd->db.length, client_cs)) goto err; table->field[6]->set_notnull(); } @@ -1035,9 +1030,7 @@ int Log_to_csv_event_handler:: log_name= &SLOW_LOG_NAME; } - table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length, - log_name->str, log_name->length, log_name->str, - TL_WRITE_CONCURRENT_INSERT); + table_list.init_one_table(&MYSQL_SCHEMA_NAME, log_name, 0, TL_WRITE_CONCURRENT_INSERT); table= open_log_table(thd, &table_list, &open_tables_backup); if (table) @@ -1077,9 +1070,9 @@ void Log_to_file_event_handler::init_pthread_objects() bool Log_to_file_event_handler:: log_slow(THD *thd, my_hrtime_t current_time, - const char *user_host, uint user_host_len, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len) + const char *sql_text, size_t sql_text_len) { Silence_log_table_errors error_handler; thd->push_internal_handler(&error_handler); @@ -1098,10 +1091,9 @@ bool Log_to_file_event_handler:: */ bool Log_to_file_event_handler:: - log_general(THD *thd, my_hrtime_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id_arg, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len, + log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len, CHARSET_INFO *client_cs) { Silence_log_table_errors error_handler; @@ -1304,7 +1296,7 @@ bool LOGGER::flush_general_log() TRUE error occurred */ -bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, +bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length, ulonglong current_utime) { @@ -1353,7 +1345,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, { is_command= TRUE; query= command_name[thd->get_command()].str; - query_length= command_name[thd->get_command()].length; + query_length= (uint)command_name[thd->get_command()].length; } for (current_handler= slow_log_handler_list; *current_handler ;) @@ -1368,7 +1360,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, } bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, - const char *query, uint query_length) + const char *query, size_t query_length) { bool error= FALSE; Log_event_handler **current_handler= general_log_handler_list; @@ -1385,8 +1377,8 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, mysql_audit_general_log(thd, hrtime_to_time(current_time), user_host_buff, user_host_len, command_name[(uint) command].str, - command_name[(uint) command].length, - query, query_length); + (uint)command_name[(uint) command].length, + query, (uint)query_length); if (opt_log && log_command(thd, command)) { @@ -1408,7 +1400,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, bool LOGGER::general_log_print(THD *thd, enum enum_server_command command, const char *format, va_list args) { - uint message_buff_len= 0; + size_t message_buff_len= 0; char message_buff[MAX_LOG_BUFFER_SIZE]; /* prepare message */ @@ -2263,8 +2255,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) String log_query(buf, sizeof(buf), &my_charset_bin); if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) || - append_identifier(thd, &log_query, - thd->lex->ident.str, thd->lex->ident.length)) + append_identifier(thd, &log_query, &thd->lex->ident)) DBUG_RETURN(1); int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(), @@ -2306,8 +2297,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) char buf[1024]; String log_query(buf, sizeof(buf), &my_charset_bin); if (log_query.copy(STRING_WITH_LEN("ROLLBACK TO "), &my_charset_bin) || - append_identifier(thd, &log_query, - thd->lex->ident.str, thd->lex->ident.length)) + append_identifier(thd, &log_query, &thd->lex->ident)) DBUG_RETURN(1); int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), @@ -2391,7 +2381,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg) *errmsg = "Could not open log file"; goto err; } - if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, + if (init_io_cache(log, file, (size_t)binlog_file_cache_size, READ_CACHE, 0, 0, MYF(MY_WME|MY_DONT_CHECK_FILESIZE))) { sql_print_error("Failed to create a cache on log (file '%s')", @@ -2672,7 +2662,7 @@ bool MYSQL_LOG::open( if (log_type == LOG_NORMAL) { char *end; - int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). " + size_t len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). " #ifdef EMBEDDED_LIBRARY "embedded library\n", my_progname, server_version, MYSQL_COMPILATION_COMMENT @@ -2686,7 +2676,7 @@ bool MYSQL_LOG::open( mysqld_port, mysqld_unix_port #endif ); - end= strnmov(buff + len, "Time Id Command Argument\n", + end= strnmov(buff + len, "Time\t\t Id Command\tArgument\n", sizeof(buff) - len); if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) || flush_io_cache(&log_file)) @@ -2879,15 +2869,14 @@ void MYSQL_QUERY_LOG::reopen_file() TRUE - error occurred */ -bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id_arg, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len) +bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id_arg, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len) { char buff[32]; char local_time_buff[MAX_TIME_SIZE]; struct tm start; - uint time_buff_len= 0; + size_t time_buff_len= 0; mysql_mutex_lock(&LOCK_log); @@ -2898,27 +2887,27 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;); /* Note that my_b_write() assumes it knows the length for this */ - if (event_time != last_time) - { - last_time= event_time; + if (event_time != last_time) + { + last_time= event_time; - localtime_r(&event_time, &start); + localtime_r(&event_time, &start); - time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE, - "%02d%02d%02d %2d:%02d:%02d\t", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); + time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE, + "%02d%02d%02d %2d:%02d:%02d\t", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); - if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len)) - goto err; - } - else - if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0) - goto err; + if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len)) + goto err; + } + else + if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0) + goto err; - /* command_type, thread_id */ - size_t length= my_snprintf(buff, 32, "%5llu ", thread_id_arg); + /* command_type, thread_id */ + size_t length= my_snprintf(buff, 32, "%6llu ", thread_id_arg); if (my_b_write(&log_file, (uchar*) buff, length)) goto err; @@ -2981,12 +2970,12 @@ err: */ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, - const char *user_host, - uint user_host_len, ulonglong query_utime, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len) + const char *sql_text, size_t sql_text_len) { bool error= 0; + char llbuff[22]; DBUG_ENTER("MYSQL_QUERY_LOG::write"); mysql_mutex_lock(&LOCK_log); @@ -3025,22 +3014,38 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0); sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0); if (my_b_printf(&log_file, - "# Thread_id: %lu Schema: %s QC_hit: %s\n" \ - "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n" \ - "# Rows_affected: %lu\n", - (ulong) thd->thread_id, (thd->db ? thd->db : ""), + "# Thread_id: %lu Schema: %s QC_hit: %s\n" + "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n" + "# Rows_affected: %lu Bytes_sent: %lu\n", + (ulong) thd->thread_id, thd->get_db(), ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"), query_time_buff, lock_time_buff, (ulong) thd->get_sent_row_count(), (ulong) thd->get_examined_row_count(), - thd->get_stmt_da()->is_ok() ? - (ulong) thd->get_stmt_da()->affected_rows() : - 0)) + (ulong) thd->get_affected_rows(), + (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old))) + goto err; + + if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) + && thd->tmp_tables_used && + my_b_printf(&log_file, + "# Tmp_tables: %lu Tmp_disk_tables: %lu " + "Tmp_table_sizes: %s\n", + (ulong) thd->tmp_tables_used, + (ulong) thd->tmp_tables_disk_used, + llstr(thd->tmp_tables_size, llbuff))) + goto err; + + if (thd->spcont && + my_b_printf(&log_file, "# Stored_routine: %s\n", + ErrConvDQName(thd->spcont->m_sp).ptr())) goto err; - if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && + + if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && (thd->query_plan_flags & (QPLAN_FULL_SCAN | QPLAN_FULL_JOIN | QPLAN_TMP_TABLE | - QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK)) && + QPLAN_TMP_DISK | QPLAN_FILESORT | QPLAN_FILESORT_DISK | + QPLAN_FILESORT_PRIORITY_QUEUE)) && my_b_printf(&log_file, "# Full_scan: %s Full_join: %s " "Tmp_table: %s Tmp_table_on_disk: %s\n" @@ -3048,8 +3053,8 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, "Priority_queue: %s\n", ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"), - ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"), - ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"), + (thd->tmp_tables_used ? "Yes" : "No"), + (thd->tmp_tables_disk_used ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"), ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ? "Yes" : "No"), @@ -3068,11 +3073,11 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, goto err; thd->free_items(); } - if (thd->db && strcmp(thd->db, db)) + if (thd->db.str && strcmp(thd->db.str, db)) { // Database changed - if (my_b_printf(&log_file,"use %s;\n",thd->db)) + if (my_b_printf(&log_file,"use %s;\n",thd->db.str)) goto err; - strmov(db,thd->db); + strmov(db,thd->db.str); } if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { @@ -3193,7 +3198,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) group_commit_trigger_lock_wait(0), sync_period_ptr(sync_period), sync_counter(0), state_file_deleted(false), binlog_state_recover_done(false), - is_relay_log(0), signal_cnt(0), + is_relay_log(0), relay_signal_cnt(0), checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF), relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), description_event_for_exec(0), description_event_for_queue(0), @@ -3263,7 +3268,8 @@ void MYSQL_BIN_LOG::cleanup() mysql_mutex_destroy(&LOCK_xid_list); mysql_mutex_destroy(&LOCK_binlog_background_thread); mysql_mutex_destroy(&LOCK_binlog_end_pos); - mysql_cond_destroy(&update_cond); + mysql_cond_destroy(&COND_relay_log_updated); + mysql_cond_destroy(&COND_bin_log_updated); mysql_cond_destroy(&COND_queue_busy); mysql_cond_destroy(&COND_xid_list); mysql_cond_destroy(&COND_binlog_background_thread); @@ -3298,7 +3304,8 @@ void MYSQL_BIN_LOG::init_pthread_objects() mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_init(key_BINLOG_LOCK_xid_list, &LOCK_xid_list, MY_MUTEX_INIT_FAST); - mysql_cond_init(m_key_update_cond, &update_cond, 0); + mysql_cond_init(m_key_relay_log_update, &COND_relay_log_updated, 0); + mysql_cond_init(m_key_bin_log_update, &COND_bin_log_updated, 0); mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0); mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0); @@ -3617,8 +3624,8 @@ bool MYSQL_BIN_LOG::open(const char *log_name, Write the current binlog checkpoint into the log, so XA recovery will know from where to start recovery. */ - uint off= dirname_length(log_file_name); - uint len= strlen(log_file_name) - off; + size_t off= dirname_length(log_file_name); + size_t len= strlen(log_file_name) - off; char *entry_mem, *name_mem; if (!(new_xid_list_entry = (xid_count_per_binlog *) my_multi_malloc(MYF(MY_WME), @@ -3628,7 +3635,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, goto err; memcpy(name_mem, log_file_name+off, len); new_xid_list_entry->binlog_name= name_mem; - new_xid_list_entry->binlog_name_len= len; + new_xid_list_entry->binlog_name_len= (int)len; new_xid_list_entry->xid_count= 0; new_xid_list_entry->notify_count= 0; @@ -3648,7 +3655,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, if (!b) b= new_xid_list_entry; strmake(buf, b->binlog_name, b->binlog_name_len); - Binlog_checkpoint_log_event ev(buf, len); + Binlog_checkpoint_log_event ev(buf, (uint)len); DBUG_EXECUTE_IF("crash_before_write_checkpoint_event", flush_io_cache(&log_file); mysql_file_sync(log_file.file, MYF(MY_WME)); @@ -3785,6 +3792,11 @@ bool MYSQL_BIN_LOG::open(const char *log_name, close_purge_index_file(); #endif + /* Notify the io thread that binlog is rotated to a new file */ + if (is_relay_log) + signal_relay_log_update(); + else + update_binlog_end_pos(); DBUG_RETURN(0); err: @@ -3935,7 +3947,7 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, for (;;) { - uint length; + size_t length; my_off_t offset= my_b_tell(&index_file); DBUG_EXECUTE_IF("simulate_find_log_pos_error", @@ -4005,7 +4017,7 @@ end: int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { int error= 0; - uint length; + size_t length; char fname[FN_REFLEN]; char *full_fname= linfo->log_file_name; @@ -4154,14 +4166,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log, mysql_mutex_unlock(&LOCK_xid_list); } - /* - The following mutex is needed to ensure that no threads call - 'delete thd' as we would then risk missing a 'rollback' from this - thread. If the transaction involved MyISAM tables, it should go - into binlog even on rollback. - */ - mysql_mutex_lock(&LOCK_thread_count); - /* Save variables so that we can reopen the log */ save_name=name; name=0; // Protect against free @@ -4268,7 +4272,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log, err: if (error == 1) name= const_cast<char*>(save_name); - mysql_mutex_unlock(&LOCK_thread_count); if (!is_relay_log) { @@ -4704,7 +4707,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space, for (;;) { - uint length; + size_t length; if ((length=my_b_gets(&purge_index_file, log_info.log_file_name, FN_REFLEN)) <= 1) @@ -5051,7 +5054,7 @@ MYSQL_BIN_LOG::is_gtid_cached(THD *thd) void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident) { - uint dir_len = dirname_length(log_file_name); + size_t dir_len = dirname_length(log_file_name); if (dir_len >= FN_REFLEN) dir_len=FN_REFLEN-1; strnmov(buf, log_file_name, dir_len); @@ -5152,7 +5155,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) new file name in the current binary log file. */ if ((error= generate_new_name(new_name, name, 0))) + { +#ifdef ENABLE_AND_FIX_HANG + close_on_error= TRUE; +#endif goto end; + } new_name_ptr=new_name; if (log_type == LOG_BIN) @@ -5183,13 +5191,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) } bytes_written += r.data_written; } - /* - Update needs to be signalled even if there is no rotate event - log rotation should give the waiting thread a signal to - discover EOF and move on to the next log. - */ - signal_update(); } + + /* + Update needs to be signalled even if there is no rotate event + log rotation should give the waiting thread a signal to + discover EOF and move on to the next log. + */ + if ((error= flush_io_cache(&log_file))) + { + close_on_error= TRUE; + goto end; + } + update_binlog_end_pos(); + old_name=name; name=0; // Don't free name close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX; @@ -5320,7 +5335,7 @@ bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev) if (my_b_append_tell(&log_file) > max_size) error= new_file_without_locking(); err: - signal_update(); // Safe as we don't call close + update_binlog_end_pos(); DBUG_RETURN(error); } @@ -5381,7 +5396,7 @@ bool MYSQL_BIN_LOG::write_event_buffer(uchar* buf, uint len) err: my_safe_afree(ebuf, len); if (!error) - signal_update(); + update_binlog_end_pos(); DBUG_RETURN(error); } @@ -5486,13 +5501,14 @@ stmt_has_updated_trans_table(const THD *thd) */ bool use_trans_cache(const THD* thd, bool is_transactional) { + if (is_transactional) + return 1; binlog_cache_mngr *const cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); - return - ((thd->is_current_stmt_binlog_format_row() || - thd->variables.binlog_direct_non_trans_update) ? is_transactional : - (is_transactional || !cache_mngr->trx_cache.empty())); + return ((thd->is_current_stmt_binlog_format_row() || + thd->variables.binlog_direct_non_trans_update) ? 0 : + !cache_mngr->trx_cache.empty()); } /** @@ -5952,6 +5968,8 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, local_server_id= thd->variables.server_id; seq_no= thd->variables.gtid_seq_no; + DBUG_ASSERT(local_server_id != 0); + if (thd->variables.option_bits & OPTION_GTID_BEGIN) { DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. " @@ -6296,7 +6314,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) prev_binlog_id= current_binlog_id; DBUG_EXECUTE_IF("binlog_force_commit_id", { - const LEX_STRING commit_name= { C_STRING_WITH_LEN("commit_id") }; + const LEX_CSTRING commit_name= { STRING_WITH_LEN("commit_id") }; bool null_value; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, @@ -6413,6 +6431,7 @@ err: { my_off_t offset= my_b_tell(file); bool check_purge= false; + DBUG_ASSERT(!is_relay_log); if (!error) { @@ -6427,26 +6446,23 @@ err: mysql_mutex_assert_owner(&LOCK_log); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true; - bool last= true; - if ((error= RUN_HOOK(binlog_storage, after_flush, - (thd, log_file_name, file->pos_in_file, - synced, first, last)))) +#ifdef HAVE_REPLICATION + if (repl_semisync_master.report_binlog_update(thd, log_file_name, + file->pos_in_file)) { sql_print_error("Failed to run 'after_flush' hooks"); error= 1; } else +#endif { - /* update binlog_end_pos so it can be read by dump thread - * - * note: must be _after_ the RUN_HOOK(after_flush) or else - * semi-sync-plugin might not have put the transaction into - * it's list before dump-thread tries to send it - */ + /* + update binlog_end_pos so it can be read by dump thread + note: must be _after_ the RUN_HOOK(after_flush) or else + semi-sync might not have put the transaction into + it's list before dump-thread tries to send it + */ update_binlog_end_pos(offset); - - signal_update(); if ((error= rotate(false, &check_purge))) check_purge= false; } @@ -6463,15 +6479,14 @@ err: mysql_mutex_assert_not_owner(&LOCK_log); mysql_mutex_assert_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true; - bool last= true; - if (RUN_HOOK(binlog_storage, after_sync, - (thd, log_file_name, file->pos_in_file, - first, last))) +#ifdef HAVE_REPLICATION + if (repl_semisync_master.wait_after_sync(log_file_name, + file->pos_in_file)) { error=1; /* error is already printed inside hook */ } +#endif /* Take mutex to protect against a reader seeing partial writes of 64-bit @@ -6513,31 +6528,29 @@ bool slow_log_print(THD *thd, const char *query, uint query_length, } +/** + Decide if we should log the command to general log + + @retval + FALSE No logging + TRUE Ok to log +*/ + bool LOGGER::log_command(THD *thd, enum enum_server_command command) { -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *sctx= thd->security_ctx; -#endif /* Log command if we have at least one log event handler enabled and want to log this king of commands */ - if (*general_log_handler_list && (what_to_log & (1L << (uint) command))) - { - if ((thd->variables.option_bits & OPTION_LOG_OFF) -#ifndef NO_EMBEDDED_ACCESS_CHECKS - && (sctx->master_access & SUPER_ACL) -#endif - ) - { - /* No logging */ - return FALSE; - } - - return TRUE; - } + if (!(*general_log_handler_list && (what_to_log & (1L << (uint) command)))) + return FALSE; - return FALSE; + /* + If LOG_SLOW_DISABLE_SLAVE is set when slave thread starts, then + OPTION_LOG_OFF is set. + Only the super user can set this bit. + */ + return !(thd->variables.option_bits & OPTION_LOG_OFF); } @@ -6547,7 +6560,7 @@ bool general_log_print(THD *thd, enum enum_server_command command, va_list args; uint error= 0; - /* Print the message to the buffer if we want to log this king of commands */ + /* Print the message to the buffer if we want to log this kind of commands */ if (! logger.log_command(thd, command)) return FALSE; @@ -6559,7 +6572,7 @@ bool general_log_print(THD *thd, enum enum_server_command command, } bool general_log_write(THD *thd, enum enum_server_command command, - const char *query, uint query_length) + const char *query, size_t query_length) { /* Write the message to the log if we want to log this king of commands */ if (logger.log_command(thd, command) || mysql_audit_general_enabled()) @@ -6906,7 +6919,7 @@ uint MYSQL_BIN_LOG::next_file_id() class CacheWriter: public Log_event_writer { public: - ulong remains; + size_t remains; CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum, Binlog_crypt_data *cr) @@ -6959,9 +6972,9 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache) mysql_mutex_assert_owner(&LOCK_log); if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) DBUG_RETURN(ER_ERROR_ON_WRITE); - uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs; - long val; - ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t + size_t length= my_b_bytes_in_cache(cache), group, carry, hdr_offs; + size_t val; + size_t end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t uchar header[LOG_EVENT_HEADER_LEN]; CacheWriter writer(thd, &log_file, binlog_checksum_options, &crypto); @@ -6986,7 +6999,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache) split. */ - group= (uint)my_b_tell(&log_file); + group= (size_t)my_b_tell(&log_file); hdr_offs= carry= 0; do @@ -6998,12 +7011,12 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache) if (unlikely(carry > 0)) { DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN); - uint tail= LOG_EVENT_HEADER_LEN - carry; + size_t tail= LOG_EVENT_HEADER_LEN - carry; /* assemble both halves */ memcpy(&header[carry], (char *)cache->read_pos, tail); - ulong len= uint4korr(header + EVENT_LEN_OFFSET); + uint32 len= uint4korr(header + EVENT_LEN_OFFSET); writer.remains= len; /* fix end_log_pos */ @@ -7147,7 +7160,7 @@ bool MYSQL_BIN_LOG::write_incident_already_locked(THD *thd) uint error= 0; DBUG_ENTER("MYSQL_BIN_LOG::write_incident_already_locked"); Incident incident= INCIDENT_LOST_EVENTS; - Incident_log_event ev(thd, incident, write_error_msg); + Incident_log_event ev(thd, incident, &write_error_msg); if (likely(is_open())) { @@ -7174,7 +7187,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd) if (!(error= write_incident_already_locked(thd)) && !(error= flush_and_sync(0))) { - signal_update(); + update_binlog_end_pos(); if ((error= rotate(false, &check_purge))) check_purge= false; } @@ -7215,7 +7228,7 @@ MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name_arg */ if (!write_event(&ev) && !flush_and_sync(0)) { - signal_update(); + update_binlog_end_pos(); } else { @@ -7315,7 +7328,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, if (cache_mngr->stmt_cache.has_incident() || cache_mngr->trx_cache.has_incident()) { - Incident_log_event inc_ev(thd, INCIDENT_LOST_EVENTS, write_error_msg); + Incident_log_event inc_ev(thd, INCIDENT_LOST_EVENTS, &write_error_msg); entry.incident_event= &inc_ev; DBUG_RETURN(write_transaction_to_binlog_events(&entry)); } @@ -7650,7 +7663,11 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) else if (is_leader) trx_group_commit_leader(entry); else if (!entry->queued_by_other) + { + DEBUG_SYNC(entry->thd, "after_semisync_queue"); + entry->thd->wait_for_wakeup_ready(); + } else { /* @@ -7819,7 +7836,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) commit_id= (last_in_queue == leader ? 0 : (uint64)leader->thd->query_id); DBUG_EXECUTE_IF("binlog_force_commit_id", { - const LEX_STRING commit_name= { C_STRING_WITH_LEN("commit_id") }; + const LEX_CSTRING commit_name= { STRING_WITH_LEN("commit_id") }; bool null_value; user_var_entry *entry= (user_var_entry*) my_hash_search(&leader->thd->user_vars, @@ -7890,45 +7907,40 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) else { bool any_error= false; - bool all_error= true; mysql_mutex_assert_not_owner(&LOCK_prepare_ordered); mysql_mutex_assert_owner(&LOCK_log); mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true, last; + for (current= queue; current != NULL; current= current->next) { - last= current->next == NULL; +#ifdef HAVE_REPLICATION if (!current->error && - RUN_HOOK(binlog_storage, after_flush, - (current->thd, - current->cache_mngr->last_commit_pos_file, - current->cache_mngr->last_commit_pos_offset, synced, - first, last))) + repl_semisync_master. + report_binlog_update(current->thd, + current->cache_mngr->last_commit_pos_file, + current->cache_mngr-> + last_commit_pos_offset)) { current->error= ER_ERROR_ON_WRITE; current->commit_errno= -1; current->error_cache= NULL; any_error= true; } - else - all_error= false; - first= false; +#endif } - /* update binlog_end_pos so it can be read by dump thread - * - * note: must be _after_ the RUN_HOOK(after_flush) or else - * semi-sync-plugin might not have put the transaction into - * it's list before dump-thread tries to send it - */ + /* + update binlog_end_pos so it can be read by dump thread + Note: must be _after_ the RUN_HOOK(after_flush) or else + semi-sync might not have put the transaction into + it's list before dump-thread tries to send it + */ update_binlog_end_pos(commit_offset); if (any_error) sql_print_error("Failed to run 'after_flush' hooks"); - if (!all_error) - signal_update(); } /* @@ -7986,18 +7998,19 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) mysql_mutex_assert_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); - bool first= true, last; + bool first __attribute__((unused))= true; + bool last __attribute__((unused)); for (current= queue; current != NULL; current= current->next) { last= current->next == NULL; - if (!current->error && - RUN_HOOK(binlog_storage, after_sync, - (current->thd, current->cache_mngr->last_commit_pos_file, - current->cache_mngr->last_commit_pos_offset, - first, last))) - { - /* error is already printed inside hook */ - } +#ifdef HAVE_REPLICATION + if (!current->error) + current->error= + repl_semisync_master.wait_after_sync(current->cache_mngr-> + last_commit_pos_file, + current->cache_mngr-> + last_commit_pos_offset); +#endif first= false; } } @@ -8308,10 +8321,10 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd) DBUG_ENTER("wait_for_update_relay_log"); mysql_mutex_assert_owner(&LOCK_log); - thd->ENTER_COND(&update_cond, &LOCK_log, + thd->ENTER_COND(&COND_relay_log_updated, &LOCK_log, &stage_slave_has_read_all_relay_log, &old_stage); - mysql_cond_wait(&update_cond, &LOCK_log); + mysql_cond_wait(&COND_relay_log_updated, &LOCK_log); thd->EXIT_COND(&old_stage); DBUG_VOID_RETURN; } @@ -8332,23 +8345,6 @@ void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd) LOCK_log is released by the caller. */ -int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, - const struct timespec *timeout) -{ - int ret= 0; - DBUG_ENTER("wait_for_update_bin_log"); - - thd_wait_begin(thd, THD_WAIT_BINLOG); - mysql_mutex_assert_owner(&LOCK_log); - if (!timeout) - mysql_cond_wait(&update_cond, &LOCK_log); - else - ret= mysql_cond_timedwait(&update_cond, &LOCK_log, - const_cast<struct timespec *>(timeout)); - thd_wait_end(thd); - DBUG_RETURN(ret); -} - int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd, struct timespec *timeout) { @@ -8358,9 +8354,9 @@ int MYSQL_BIN_LOG::wait_for_update_binlog_end_pos(THD* thd, thd_wait_begin(thd, THD_WAIT_BINLOG); mysql_mutex_assert_owner(get_binlog_end_pos_lock()); if (!timeout) - mysql_cond_wait(&update_cond, get_binlog_end_pos_lock()); + mysql_cond_wait(&COND_bin_log_updated, get_binlog_end_pos_lock()); else - ret= mysql_cond_timedwait(&update_cond, get_binlog_end_pos_lock(), + ret= mysql_cond_timedwait(&COND_bin_log_updated, get_binlog_end_pos_lock(), timeout); thd_wait_end(thd); DBUG_RETURN(ret); @@ -8405,7 +8401,8 @@ void MYSQL_BIN_LOG::close(uint exiting) relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF); write_event(&s); bytes_written+= s.data_written; - signal_update(); + flush_io_cache(&log_file); + update_binlog_end_pos(); /* When we shut down server, write out the binlog state to a separate @@ -8624,14 +8621,6 @@ bool flush_error_log() return result; } -void MYSQL_BIN_LOG::signal_update() -{ - DBUG_ENTER("MYSQL_BIN_LOG::signal_update"); - signal_cnt++; - mysql_cond_broadcast(&update_cond); - DBUG_VOID_RETURN; -} - #ifdef _WIN32 static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, size_t length, size_t buffLen) @@ -8675,8 +8664,8 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, time_t skr; struct tm tm_tmp; struct tm *start; - THD *thd; - int tag_length= 0; + THD *thd= 0; + size_t tag_length= 0; char tag[NAME_LEN]; DBUG_ENTER("print_buffer_to_file"); DBUG_PRINT("enter",("buffer: %s", buffer)); @@ -8709,10 +8698,10 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, start->tm_hour, start->tm_min, start->tm_sec, - (unsigned long) pthread_self(), + (unsigned long) (thd ? thd->thread_id : 0), (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ? "Warning" : "Note"), - tag_length, tag, + (int) tag_length, tag, (int) length, buffer); fflush(stderr); @@ -10030,7 +10019,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, for (;;) { while ((ev= Log_event::read_log_event(first_round ? first_log : &log, - 0, fdle, opt_master_verify_checksum)) + fdle, opt_master_verify_checksum)) && ev->is_valid()) { enum Log_event_type typ= ev->get_type_code(); @@ -10051,7 +10040,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, case BINLOG_CHECKPOINT_EVENT: if (first_round && do_xa) { - uint dir_len; + size_t dir_len; Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev; if (cev->binlog_file_len >= FN_REFLEN) sql_print_warning("Incorrect binlog checkpoint event with too " @@ -10271,7 +10260,7 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery) return 1; } - if ((ev= Log_event::read_log_event(&log, 0, &fdle, + if ((ev= Log_event::read_log_event(&log, &fdle, opt_master_verify_checksum)) && ev->get_type_code() == FORMAT_DESCRIPTION_EVENT) { @@ -10448,7 +10437,7 @@ static struct st_mysql_sys_var *binlog_sys_vars[]= static void set_binlog_snapshot_file(const char *src) { - int dir_len = dirname_length(src); + size_t dir_len = dirname_length(src); strmake_buf(binlog_snapshot_file, src + dir_len); } @@ -10512,7 +10501,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) *out_gtid_list= NULL; - if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle, + if (!(ev= Log_event::read_log_event(cache, &init_fdle, opt_master_verify_checksum)) || ev->get_type_code() != FORMAT_DESCRIPTION_EVENT) { @@ -10528,7 +10517,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) { Log_event_type typ; - ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum); + ev= Log_event::read_log_event(cache, fdle, opt_master_verify_checksum); if (!ev) { errormsg= "Could not read GTID list event while looking for GTID " @@ -10558,6 +10547,7 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) return errormsg; } + struct st_mysql_storage_engine binlog_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/log.h b/sql/log.h index a7c28db4ac5..098824d9ec8 100644 --- a/sql/log.h +++ b/sql/log.h @@ -349,19 +349,23 @@ public: /* for documentation of mutexes held in various places in code */ }; +/* Tell the io thread if we can delay the master info sync. */ +#define SEMI_SYNC_SLAVE_DELAY_SYNC 1 +/* Tell the io thread if the current event needs a ack. */ +#define SEMI_SYNC_NEED_ACK 2 + class MYSQL_QUERY_LOG: public MYSQL_LOG { public: MYSQL_QUERY_LOG() : last_time(0) {} void reopen_file(); - bool write(time_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len); + bool write(time_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len); bool write(THD *thd, time_t current_time, - const char *user_host, uint user_host_len, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len); + const char *sql_text, size_t sql_text_len); bool open_slow_log(const char *log_name) { char buf[FN_REFLEN]; @@ -425,14 +429,18 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG #ifdef HAVE_PSI_INTERFACE /** The instrumentation key to use for @ LOCK_index. */ PSI_mutex_key m_key_LOCK_index; - /** The instrumentation key to use for @ update_cond. */ - PSI_cond_key m_key_update_cond; + /** The instrumentation key to use for @ COND_relay_log_updated */ + PSI_cond_key m_key_relay_log_update; + /** The instrumentation key to use for @ COND_bin_log_updated */ + PSI_cond_key m_key_bin_log_update; /** The instrumentation key to use for opening the log file. */ PSI_file_key m_key_file_log; /** The instrumentation key to use for opening the log index file. */ PSI_file_key m_key_file_log_index; PSI_file_key m_key_COND_queue_busy; + /** The instrumentation key to use for LOCK_binlog_end_pos. */ + PSI_mutex_key m_key_LOCK_binlog_end_pos; #endif struct group_commit_entry @@ -488,7 +496,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG mysql_mutex_t LOCK_binlog_end_pos; mysql_mutex_t LOCK_xid_list; mysql_cond_t COND_xid_list; - mysql_cond_t update_cond; + mysql_cond_t COND_relay_log_updated, COND_bin_log_updated; ulonglong bytes_written; IO_CACHE index_file; char index_file_name[FN_REFLEN]; @@ -605,7 +613,7 @@ public: /* This is relay log */ bool is_relay_log; - ulong signal_cnt; // update of the counter is checked by heartbeat + ulong relay_signal_cnt; // update of the counter is checked by heartbeat enum enum_binlog_checksum_alg checksum_alg_reset; // to contain a new value when binlog is rotated /* Holds the last seen in Relay-Log FD's checksum alg value. @@ -668,16 +676,20 @@ public: #ifdef HAVE_PSI_INTERFACE void set_psi_keys(PSI_mutex_key key_LOCK_index, - PSI_cond_key key_update_cond, + PSI_cond_key key_relay_log_update, + PSI_cond_key key_bin_log_update, PSI_file_key key_file_log, PSI_file_key key_file_log_index, - PSI_file_key key_COND_queue_busy) + PSI_file_key key_COND_queue_busy, + PSI_mutex_key key_LOCK_binlog_end_pos) { m_key_LOCK_index= key_LOCK_index; - m_key_update_cond= key_update_cond; + m_key_relay_log_update= key_relay_log_update; + m_key_bin_log_update= key_bin_log_update; m_key_file_log= key_file_log; m_key_file_log_index= key_file_log_index; m_key_COND_queue_busy= key_COND_queue_busy; + m_key_LOCK_binlog_end_pos= key_LOCK_binlog_end_pos; } #endif @@ -714,11 +726,56 @@ public: DBUG_VOID_RETURN; } void set_max_size(ulong max_size_arg); - void signal_update(); + + /* Handle signaling that relay has been updated */ + void signal_relay_log_update() + { + mysql_mutex_assert_owner(&LOCK_log); + DBUG_ASSERT(is_relay_log); + DBUG_ENTER("MYSQL_BIN_LOG::signal_relay_log_update"); + relay_signal_cnt++; + mysql_cond_broadcast(&COND_relay_log_updated); + DBUG_VOID_RETURN; + } + void signal_bin_log_update() + { + mysql_mutex_assert_owner(&LOCK_binlog_end_pos); + DBUG_ASSERT(!is_relay_log); + DBUG_ENTER("MYSQL_BIN_LOG::signal_bin_log_update"); + mysql_cond_broadcast(&COND_bin_log_updated); + DBUG_VOID_RETURN; + } + void update_binlog_end_pos() + { + if (is_relay_log) + signal_relay_log_update(); + else + { + lock_binlog_end_pos(); + binlog_end_pos= my_b_safe_tell(&log_file); + signal_bin_log_update(); + unlock_binlog_end_pos(); + } + } + void update_binlog_end_pos(my_off_t pos) + { + mysql_mutex_assert_owner(&LOCK_log); + mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos); + lock_binlog_end_pos(); + /* + Note: it would make more sense to assert(pos > binlog_end_pos) + but there are two places triggered by mtr that has pos == binlog_end_pos + i didn't investigate but accepted as it should do no harm + */ + DBUG_ASSERT(pos >= binlog_end_pos); + binlog_end_pos= pos; + signal_bin_log_update(); + unlock_binlog_end_pos(); + } + void wait_for_sufficient_commits(); void binlog_trigger_immediate_group_commit(); void wait_for_update_relay_log(THD* thd); - int wait_for_update_bin_log(THD* thd, const struct timespec * timeout); void init(ulong max_size); void init_pthread_objects(); void cleanup(); @@ -815,7 +872,7 @@ public: inline char* get_log_fname() { return log_file_name; } inline char* get_name() { return name; } inline mysql_mutex_t* get_log_lock() { return &LOCK_log; } - inline mysql_cond_t* get_log_cond() { return &update_cond; } + inline mysql_cond_t* get_bin_log_cond() { return &COND_bin_log_updated; } inline IO_CACHE* get_log_file() { return &log_file; } inline void lock_index() { mysql_mutex_lock(&LOCK_index);} @@ -839,23 +896,6 @@ public: bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no); - - void update_binlog_end_pos(my_off_t pos) - { - mysql_mutex_assert_owner(&LOCK_log); - mysql_mutex_assert_not_owner(&LOCK_binlog_end_pos); - lock_binlog_end_pos(); - /** - * note: it would make more sense to assert(pos > binlog_end_pos) - * but there are two places triggered by mtr that has pos == binlog_end_pos - * i didn't investigate but accepted as it should do no harm - */ - DBUG_ASSERT(pos >= binlog_end_pos); - binlog_end_pos= pos; - signal_update(); - unlock_binlog_end_pos(); - } - /** * used when opening new file, and binlog_end_pos moves backwards */ @@ -866,7 +906,7 @@ public: lock_binlog_end_pos(); binlog_end_pos= pos; strcpy(binlog_end_pos_file, file_name); - signal_update(); + signal_bin_log_update(); unlock_binlog_end_pos(); } @@ -909,16 +949,14 @@ public: virtual void cleanup()= 0; virtual bool log_slow(THD *thd, my_hrtime_t current_time, - const char *user_host, - uint user_host_len, ulonglong query_utime, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len)= 0; + const char *sql_text, size_t sql_text_len)= 0; virtual bool log_error(enum loglevel level, const char *format, va_list args)= 0; - virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len, CHARSET_INFO *client_cs)= 0; virtual ~Log_event_handler() {} }; @@ -938,16 +976,14 @@ public: virtual void cleanup(); virtual bool log_slow(THD *thd, my_hrtime_t current_time, - const char *user_host, - uint user_host_len, ulonglong query_utime, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len); + const char *sql_text, size_t sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len, CHARSET_INFO *client_cs); int activate_log(THD *thd, uint log_type); @@ -970,16 +1006,14 @@ public: virtual void cleanup(); virtual bool log_slow(THD *thd, my_hrtime_t current_time, - const char *user_host, - uint user_host_len, ulonglong query_utime, + const char *user_host, size_t user_host_len, ulonglong query_utime, ulonglong lock_utime, bool is_command, - const char *sql_text, uint sql_text_len); + const char *sql_text, size_t sql_text_len); virtual bool log_error(enum loglevel level, const char *format, va_list args); - virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, - uint user_host_len, my_thread_id thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len, + virtual bool log_general(THD *thd, my_hrtime_t event_time, const char *user_host, size_t user_host_len, my_thread_id thread_id, + const char *command_type, size_t command_type_len, + const char *sql_text, size_t sql_text_len, CHARSET_INFO *client_cs); void flush(); void init_pthread_objects(); @@ -1033,12 +1067,12 @@ public: void cleanup_end(); bool error_log_print(enum loglevel level, const char *format, va_list args); - bool slow_log_print(THD *thd, const char *query, uint query_length, + bool slow_log_print(THD *thd, const char *query, size_t query_length, ulonglong current_utime); bool general_log_print(THD *thd,enum enum_server_command command, const char *format, va_list args); bool general_log_write(THD *thd, enum enum_server_command command, - const char *query, uint query_length); + const char *query, size_t query_length); /* we use this function to setup all enabled log event handlers */ int set_handlers(ulonglong error_log_printer, @@ -1090,7 +1124,7 @@ bool general_log_print(THD *thd, enum enum_server_command command, const char *format,...); bool general_log_write(THD *thd, enum enum_server_command command, - const char *query, uint query_length); + const char *query, size_t query_length); void binlog_report_wait_for(THD *thd, THD *other_thd); void sql_perror(const char *message); diff --git a/sql/log_event.cc b/sql/log_event.cc index 2c75045c94d..ac610f978fc 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifndef MYSQL_CLIENT @@ -43,6 +43,7 @@ #include <strfunc.h> #include "compat56.h" #include "wsrep_mysqld.h" +#include "sql_insert.h" #else #include "mysqld_error.h" #endif /* MYSQL_CLIENT */ @@ -220,7 +221,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, const char *handler_error= (ha_error ? HA_ERR(ha_error) : NULL); char buff[MAX_SLAVE_ERRMSG], *slider; const char *buff_end= buff + sizeof(buff); - uint len; + size_t len; Diagnostics_area::Sql_condition_iterator it= thd->get_stmt_da()->sql_conditions(); Relay_log_info const *rli= rgi->rli; @@ -321,7 +322,8 @@ public: bool res; if (copy_event_cache_to_string_and_reinit(m_cache, &tmp_str)) return 1; - res= m_ev->output_buf.append(tmp_str.str, tmp_str.length) != 0; + /* use 2 argument append as tmp_str is not \0 terminated */ + res= m_ev->output_buf.append(tmp_str.str, tmp_str.length); my_free(tmp_str.str); return res ? res : 0; } @@ -713,7 +715,7 @@ static inline int read_str(const char **buf, const char *buf_end, Transforms a string into "" or its expression in X'HHHH' form. */ -char *str_to_hex(char *to, const char *from, uint len) +char *str_to_hex(char *to, const char *from, size_t len) { if (len) { @@ -1603,13 +1605,13 @@ int Log_event_writer::encrypt_and_write(const uchar *pos, size_t len) if (ctx) { - dstsize= encryption_encrypted_length(len, ENCRYPTION_KEY_SYSTEM_DATA, + dstsize= encryption_encrypted_length((uint)len, ENCRYPTION_KEY_SYSTEM_DATA, crypto->key_version); if (!(dst= (uchar*)my_safe_alloca(dstsize))) return 1; uint dstlen; - if (encryption_ctx_update(ctx, pos, len, dst, &dstlen)) + if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen)) goto err; if (maybe_write_event_len(dst, dstlen)) return 1; @@ -1697,12 +1699,12 @@ int Log_event_writer::write_footer() Log_event::write_header() */ -bool Log_event::write_header(ulong event_data_length) +bool Log_event::write_header(size_t event_data_length) { uchar header[LOG_EVENT_HEADER_LEN]; ulong now; DBUG_ENTER("Log_event::write_header"); - DBUG_PRINT("enter", ("filepos: %lld length: %lu type: %d", + DBUG_PRINT("enter", ("filepos: %lld length: %zu type: %d", (longlong) writer->pos(), event_data_length, (int) get_type_code())); @@ -1876,7 +1878,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, DBUG_RETURN(0); } -Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, +Log_event* Log_event::read_log_event(IO_CACHE* file, const Format_description_log_event *fdle, my_bool crc_check) { @@ -1886,9 +1888,6 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, const char *error= 0; Log_event *res= 0; - if (log_lock) - mysql_mutex_lock(log_lock); - switch (read_log_event(file, &event, fdle, BINLOG_CHECKSUM_ALG_OFF)) { case 0: @@ -1925,8 +1924,6 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, mysql_mutex_t* log_lock, res->register_temp_buf(event.release(), true); err: - if (log_lock) - mysql_mutex_unlock(log_lock); if (error) { DBUG_ASSERT(!res); @@ -3077,7 +3074,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, if (my_b_printf(file, "%s", prefix)) goto err; - for (size_t i= 0; i < td->size(); i ++) + for (uint i= 0; i < (uint)td->size(); i ++) { size_t size; int is_null= (null_bits[null_bit_index / 8] @@ -3438,7 +3435,7 @@ Rows_log_event::calc_row_event_length(table_def *td, value+= (bitmap_bits_set(cols_bitmap) + 7) / 8; - for (size_t i= 0; i < td->size(); i ++) + for (uint i= 0; i < (uint)td->size(); i ++) { int is_null; is_null= (null_bits[null_bit_index / 8] >> (null_bit_index % 8)) & 0x01; @@ -4265,8 +4262,7 @@ Query_log_event::Query_log_event() Creates an event for binlogging The value for `errcode' should be supplied by caller. */ -Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, - ulong query_length, bool using_trans, +Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t query_length, bool using_trans, bool direct, bool suppress_use, int errcode) :Log_event(thd_arg, @@ -4275,7 +4271,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0), using_trans), data_buf(0), query(query_arg), catalog(thd_arg->catalog), - db(thd_arg->db), q_len((uint32) query_length), + db(thd_arg->db.str), q_len((uint32) query_length), thread_id(thd_arg->thread_id), /* save the original thread id; we already know the server id */ slave_proxy_id((ulong)thd_arg->variables.pseudo_thread_id), @@ -4492,7 +4488,7 @@ get_str_len_and_pointer(const Log_event::Byte **src, static void copy_str_and_move(const char **src, Log_event::Byte **dst, - uint len) + size_t len) { memcpy(*dst, *src, len); *src= (const char *)*dst; @@ -4625,7 +4621,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, event from the relay log. */ DBUG_ASSERT(description_event->binlog_version < 4); - master_data_written= data_written; + master_data_written= (uint32)data_written; } /* We have parsed everything we know in the post header for QUERY_EVENT, @@ -4941,7 +4937,7 @@ Query_log_event::dummy_event(String *packet, ulong ev_offset, possibly just @`!`). */ static const char var_name[]= "!dummyvar"; - uint name_len= data_len - (min_user_var_event_len - 1); + size_t name_len= data_len - (min_user_var_event_len - 1); p[EVENT_TYPE_OFFSET]= USER_VAR_EVENT; int4store(p + LOG_EVENT_HEADER_LEN, name_len); @@ -5361,10 +5357,11 @@ 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_STRING new_db; + LEX_CSTRING new_db; int expected_error,actual_error= 0; Schema_specification_st db_options; uint64 sub_id= 0; + void *hton= NULL; rpl_gtid gtid; Relay_log_info const *rli= rgi->rli; Rpl_filter *rpl_filter= rli->mi->rpl_filter; @@ -5381,12 +5378,12 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, thd->catalog= catalog_len ? (char *) catalog : (char *)""; new_db.length= db_len; new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); - thd->set_db(new_db.str, new_db.length); /* allocates a copy of 'db' */ + thd->set_db(&new_db); /* allocates a copy of 'db' */ /* Setting the character set and collation of the current database thd->db. */ - load_db_opt_by_name(thd, thd->db, &db_options); + load_db_opt_by_name(thd, thd->db.str, &db_options); if (db_options.default_table_charset) thd->db_charset= db_options.default_table_charset; thd->variables.auto_increment_increment= auto_increment_increment; @@ -5410,7 +5407,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, ::do_apply_event(), then the companion SET also have so we don't need to reset_one_shot_variables(). */ - if (is_trans_keyword() || rpl_filter->db_ok(thd->db)) + if (is_trans_keyword() || rpl_filter->db_ok(thd->db.str)) { thd->set_time(when, when_sec_part); thd->set_query_and_id((char*)query_arg, q_len_arg, @@ -5445,7 +5442,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, if (sql_mode_inited) thd->variables.sql_mode= (sql_mode_t) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) | - (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE)); + (sql_mode & ~(sql_mode_t) MODE_NO_DIR_IN_CREATE)); if (charset_inited) { rpl_sql_thread_info *sql_info= thd->system_thread_info.rpl_sql_info; @@ -5535,7 +5532,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, gtid= rgi->current_gtid; if (rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, - true, false)) + true, false, &hton)) { int errcode= thd->get_stmt_da()->sql_errno(); if (!is_parallel_retry_error(rgi, errcode)) @@ -5578,7 +5575,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, DBUG_ASSERT(thd->m_statement_psi == NULL); thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, stmt_info_rpl.m_key, - thd->db, thd->db_length, + thd->db.str, thd->db.length, thd->charset()); THD_STAGE_INFO(thd, stage_init); MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(), thd->query_length()); @@ -5588,13 +5585,13 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, if (thd->slave_thread) { /* - The opt_log_slow_slave_statements variable can be changed - dynamically, so we have to set the sql_log_slow respectively. + To be compatible with previous releases, the slave thread uses the global + log_slow_disabled_statements value, wich can be changed dynamically, so we + have to set the sql_log_slow respectively. */ - thd->variables.sql_log_slow= opt_log_slow_slave_statements; + thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE); } - thd->enable_slow_log= thd->variables.sql_log_slow; mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, FALSE, FALSE); /* Finalize server status flags after executing a statement. */ @@ -5712,7 +5709,7 @@ compare_errors: "Error '%s' on query. Default database: '%s'. Query: '%s'", (actual_error ? thd->get_stmt_da()->message() : "unexpected success or fatal error"), - print_slave_db_safe(thd->db), query_arg); + thd->get_db(), query_arg); thd->is_slave_error= 1; } @@ -5756,7 +5753,7 @@ compare_errors: end: if (sub_id && !thd->is_slave_error) - rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, rgi); + rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi); /* Probably we have set thd->query, thd->db, thd->catalog to point to places @@ -5769,7 +5766,7 @@ end: TABLE uses the db.table syntax. */ thd->catalog= 0; - thd->set_db(NULL, 0); /* will free the current database */ + thd->set_db(&null_clex_str); /* will free the current database */ thd->reset_query(); DBUG_PRINT("info", ("end: query= 0")); @@ -6359,7 +6356,7 @@ bool Format_description_log_event::write() FD_queue checksum_alg value. */ compile_time_assert(BINLOG_CHECKSUM_ALG_DESC_LEN == 1); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS data_written= 0; // to prepare for need_checksum assert #endif uint8 checksum_byte= (uint8) @@ -7200,14 +7197,14 @@ 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_STRING new_db; + 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= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); - thd->set_db(new_db.str, new_db.length); + new_db.str= rpl_filter->get_rewrite_db(db, &new_db.length); + thd->set_db(&new_db); DBUG_ASSERT(thd->query() == 0); thd->clear_error(1); @@ -7244,21 +7241,20 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, ::do_apply_event(), then the companion SET also have so we don't need to reset_one_shot_variables(). */ - if (rpl_filter->db_ok(thd->db)) + if (rpl_filter->db_ok(thd->db.str)) { thd->set_time(when, when_sec_part); thd->set_query_id(next_query_id()); thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); TABLE_LIST tables; - tables.init_one_table(thd->strmake(thd->db, thd->db_length), - thd->db_length, - table_name, strlen(table_name), - table_name, TL_WRITE); + LEX_CSTRING db_name= { thd->strmake(thd->db.str, thd->db.length), thd->db.length }; + LEX_CSTRING tbl_name= { table_name, strlen(table_name) }; + tables.init_one_table(&db_name, &tbl_name, 0, TL_WRITE); tables.updating= 1; // the table will be opened in mysql_load - if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db, &tables)) + if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db.str, &tables)) { // TODO: this is a bug - this needs to be moved to the I/O thread if (net) @@ -7344,7 +7340,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, ex.skip_lines = skip_lines; List<Item> field_list; thd->lex->select_lex.context.resolve_in_table_list_only(&tables); - set_fields(tables.db, field_list, &thd->lex->select_lex.context); + set_fields(tables.db.str, field_list, &thd->lex->select_lex.context); thd->variables.pseudo_thread_id= thread_id; if (net) { @@ -7370,7 +7366,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, "warning(s). Default database: '%s'", (char*) table_name, log_pos, RPL_LOG_NAME, (ulong) thd->cuted_fields, - print_slave_db_safe(thd->db)); + thd->get_db()); } if (net) net->pkt_nr= thd->net.pkt_nr; @@ -7389,9 +7385,9 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, error: thd->net.vio = 0; - const char *remember_db= thd->db; + const char *remember_db= thd->get_db(); thd->catalog= 0; - thd->set_db(NULL, 0); /* will free the current database */ + thd->set_db(&null_clex_str); /* will free the current database */ thd->reset_query(); thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); @@ -7440,7 +7436,7 @@ error: } rli->report(ERROR_LEVEL, sql_errno, rgi->gtid_info(), "\ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'", - err, (char*)table_name, print_slave_db_safe(remember_db)); + err, (char*)table_name, remember_db); free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); DBUG_RETURN(1); } @@ -7453,7 +7449,7 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'", "Running LOAD DATA INFILE on table '%-.64s'." " Default database: '%-.64s'", (char*)table_name, - print_slave_db_safe(remember_db)); + remember_db); rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(), ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf); @@ -8299,15 +8295,17 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) int ret; if (gl_flags & FLAG_IGN_GTIDS) { + void *hton= NULL; uint32 i; + for (i= 0; i < count; ++i) { if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i], - sub_id_list[i], - false, false))) + sub_id_list[i], + false, false, &hton))) return ret; rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i], - NULL); + hton, NULL); } } ret= Log_event::do_apply_event(rgi); @@ -8399,7 +8397,7 @@ err: fully contruct the event object. */ bool -Gtid_list_log_event::peek(const char *event_start, uint32 event_len, +Gtid_list_log_event::peek(const char *event_start, size_t event_len, enum enum_binlog_checksum_alg checksum_alg, rpl_gtid **out_gtid_list, uint32 *out_list_len, const Format_description_log_event *fdev) @@ -8817,6 +8815,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) rpl_gtid gtid; uint64 sub_id= 0; Relay_log_info const *rli= rgi->rli; + void *hton= NULL; /* XID_EVENT works like a COMMIT statement. And it also updates the @@ -8841,7 +8840,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) gtid= rgi->current_gtid; err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true, - false); + false, &hton); if (err) { int ec= thd->get_stmt_da()->sql_errno(); @@ -8874,7 +8873,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi) thd->mdl_context.release_transactional_locks(); if (!res && sub_id) - rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, rgi); + rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi); /* Increment the global status commit count variable @@ -9129,7 +9128,7 @@ bool User_var_log_event::write() uchar buf2[MY_MAX(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2; uint unsigned_len= 0; uint buf1_length; - ulong event_length; + size_t event_length; int4store(buf, name_len); @@ -9372,7 +9371,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) break; } case STRING_RESULT: - it= new (thd->mem_root) Item_string(thd, val, val_len, charset); + it= new (thd->mem_root) Item_string(thd, val, (uint)val_len, charset); break; case ROW_RESULT: default: @@ -10210,7 +10209,6 @@ int Execute_load_log_event::do_apply_event(rpl_group_info *rgi) } if (!(lev= (Load_log_event*) Log_event::read_log_event(&file, - (mysql_mutex_t*)0, rli->relay_log.description_event_for_exec, opt_slave_sql_verify_checksum)) || lev->get_type_code() != NEW_LOAD_EVENT) @@ -10416,7 +10414,7 @@ bool Execute_load_query_log_event::print(FILE* file, { if (my_b_write(&cache, (uchar*) query, fn_pos_start) || my_b_write_string(&cache, " LOCAL INFILE ") || - pretty_print_str(&cache, local_fname, strlen(local_fname))) + pretty_print_str(&cache, local_fname, (int)strlen(local_fname))) goto err; if (dup_handling == LOAD_DUP_REPLACE) @@ -11583,7 +11581,7 @@ static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD * thd) already. So there should be no need to rollback the transaction. */ DBUG_ASSERT(! thd->transaction_rollback_request); - error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd)); + error|= (int)(error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd)); /* Now what if this is not a transactional engine? we still need to @@ -11789,16 +11787,16 @@ bool Rows_log_event::print_helper(FILE *file, LEX_STRING tmp_str; if (copy_event_cache_to_string_and_reinit(head, &tmp_str)) return 1; - output_buf.append(&tmp_str); + output_buf.append(tmp_str.str, tmp_str.length); // Not \0 terminated my_free(tmp_str.str); if (copy_event_cache_to_string_and_reinit(body, &tmp_str)) return 1; - output_buf.append(&tmp_str); + output_buf.append(tmp_str.str, tmp_str.length); my_free(tmp_str.str); #ifdef WHEN_FLASHBACK_REVIEW_READY if (copy_event_cache_to_string_and_reinit(sql, &tmp_str)) return 1; - output_buf.append(&tmp_str); + output_buf.append(tmp_str.str, tmp_str.length); my_free(tmp_str.str); #endif } @@ -12289,7 +12287,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len, LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN; int len_diff; - if (!(len_diff= new_len - m_dblen)) + if (!(len_diff= (int)(new_len - m_dblen))) { memcpy((void*) (temp_buf + header_len + 1), new_db, m_dblen + 1); memcpy((void*) m_dbnam, new_db, m_dblen + 1); @@ -12311,7 +12309,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len, // Rewrite temp_buf char* ptr= new_temp_buf; - ulong cnt= 0; + size_t cnt= 0; // Copy header and change event length memcpy(ptr, temp_buf, header_len); @@ -12440,7 +12438,7 @@ check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list) Relay_log_info *rli= rgi->rli; if ((rgi->thd->slave_thread /* filtering is for slave only */ || IF_WSREP((WSREP(rgi->thd) && rgi->thd->wsrep_applier), 0)) && - (!rli->mi->rpl_filter->db_ok(table_list->db) || + (!rli->mi->rpl_filter->db_ok(table_list->db.str) || (rli->mi->rpl_filter->is_on() && !rli->mi->rpl_filter->tables_ok("", table_list)))) res= FILTERED_OUT; else @@ -12452,8 +12450,8 @@ check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list) if (ptr->table_id == table_list->table_id) { - if (strcmp(ptr->db, table_list->db) || - strcmp(ptr->alias, table_list->table_name) || + if (cmp(&ptr->db, &table_list->db) || + cmp(&ptr->alias, &table_list->table_name) || ptr->lock_type != TL_WRITE) // the ::do_apply_event always sets TL_WRITE res= SAME_ID_MAPPING_DIFFERENT_TABLE; else @@ -12473,7 +12471,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) { RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; - size_t dummy_len; + size_t dummy_len, db_mem_length, tname_mem_length; void *memory; Rpl_filter *filter; Relay_log_info const *rli= rgi->rli; @@ -12491,19 +12489,20 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) /* call from mysql_client_binlog_statement() will not set rli->mi */ filter= rgi->thd->slave_thread ? rli->mi->rpl_filter : global_rpl_filter; - strmov(db_mem, filter->get_rewrite_db(m_dbnam, &dummy_len)); - strmov(tname_mem, m_tblnam); + 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; - table_list->init_one_table(db_mem, strlen(db_mem), - tname_mem, strlen(tname_mem), - tname_mem, TL_WRITE); + LEX_CSTRING tmp_db_name= {db_mem, db_mem_length }; + LEX_CSTRING tmp_tbl_name= {tname_mem, tname_mem_length }; + table_list->init_one_table(&tmp_db_name, &tmp_tbl_name, 0, TL_WRITE); table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id); table_list->updating= 1; table_list->required_type= TABLE_TYPE_NORMAL; - DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, - table_list->table_id)); + DBUG_PRINT("debug", ("table: %s is mapped to %u", + table_list->table_name.str, + table_list->table_id)); table_list->master_had_triggers= ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? 1 : 0); DBUG_PRINT("debug", ("table->master_had_triggers=%d", (int)table_list->master_had_triggers)); @@ -13027,6 +13026,17 @@ Rows_log_event::write_row(rpl_group_info *rgi, DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet } + // Handle INSERT. + // Set vers fields when replicating from not system-versioned table. + if (m_type == WRITE_ROWS_EVENT_V1 && table->versioned(VERS_TIMESTAMP)) + { + ulong sec_part; + bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + // Check whether a row came from unversioned table and fix vers fields. + if (table->vers_start_field()->get_timestamp(&sec_part) == 0 && sec_part == 0) + table->vers_update_fields(); + } + /* Try to write record. If a corresponding record already exists in the table, we try to change it using ha_update_row() if possible. Otherwise we delete @@ -13353,7 +13363,10 @@ static bool record_compare(TABLE *table) /* Compare fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { - + if (table->versioned() && (*ptr)->vers_sys_field()) + { + continue; + } /** We only compare field contents that are not null. NULL fields (i.e., their null bits) were compared @@ -13421,7 +13434,7 @@ int Rows_log_event::find_key() */ last_part= key->user_defined_key_parts - 1; DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu", - key->name, last_part, key->rec_per_key[last_part])); + key->name.str, last_part, key->rec_per_key[last_part])); if (!(m_table->file->index_flags(i, last_part, 1) & HA_READ_NEXT)) continue; @@ -13472,13 +13485,13 @@ void issue_long_find_row_warning(Log_event_type type, if ((global_system_variables.log_warnings > 1 && !rgi->is_long_find_row_note_printed())) { - time_t now= my_time(0); - time_t stmt_ts= rgi->get_row_stmt_start_timestamp(); + ulonglong now= microsecond_interval_timer(); + ulonglong stmt_ts= rgi->get_row_stmt_start_timestamp(); DBUG_EXECUTE_IF("inject_long_find_row_note", - stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2);); + stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2*HRTIME_RESOLUTION);); - long delta= (long) (now - stmt_ts); + longlong delta= (now - stmt_ts)/HRTIME_RESOLUTION; if (delta > LONG_FIND_ROW_THRESHOLD) { @@ -13488,10 +13501,11 @@ void issue_long_find_row_warning(Log_event_type type, sql_print_information("The slave is applying a ROW event on behalf of a%s statement " "on table %s and is currently taking a considerable amount " - "of time (%ld seconds). This is due to the fact that it is %s " + "of time (%lld seconds). This is due to the fact that it is %s " "while looking up records to be processed. Consider adding a " "primary key (or unique key) to the table to improve " - "performance.", evt_type, table_name, delta, scan_type); + "performance.", + evt_type, table_name, (long) delta, scan_type); } } } @@ -13548,6 +13562,27 @@ int Rows_log_event::find_row(rpl_group_info *rgi) prepare_record(table, m_width, FALSE); error= unpack_current_row(rgi); + m_vers_from_plain= false; + if (table->versioned()) + { + Field *row_end= table->vers_end_field(); + DBUG_ASSERT(table->read_set); + bitmap_set_bit(table->read_set, row_end->field_index); + // check whether master table is unversioned + if (row_end->val_int() == 0) + { + // row_start initialized with NULL when came from plain table. + // Set it notnull() because record_compare() count NULLs. + table->vers_start_field()->set_notnull(); + bitmap_set_bit(table->write_set, row_end->field_index); + // Plain source table may have a PRIMARY KEY. And row_end is always + // a part of PRIMARY KEY. Set it to max value for engine to find it in + // index. Needed for an UPDATE/DELETE cases. + table->vers_end_field()->set_max(); + m_vers_from_plain= true; + } + } + DBUG_PRINT("info",("looking for the following record")); DBUG_DUMP("record[0]", table->record[0], table->s->reclength); @@ -13608,10 +13643,10 @@ int Rows_log_event::find_row(rpl_group_info *rgi) if (m_key_info) { DBUG_PRINT("info",("locating record using key #%u [%s] (index_read)", - m_key_nr, m_key_info->name)); + m_key_nr, m_key_info->name.str)); /* We use this to test that the correct key is used in test cases. */ DBUG_EXECUTE_IF("slave_crash_if_wrong_index", - if(0 != strcmp(m_key_info->name,"expected_key")) abort();); + if(0 != strcmp(m_key_info->name.str,"expected_key")) abort();); /* The key is active: search the table using the index */ if (!table->file->inited && @@ -13884,7 +13919,6 @@ int Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, int error) { - /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/ m_table->file->ha_index_or_rnd_end(); my_free(m_key); m_key= NULL; @@ -13930,7 +13964,19 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi) if (!error) { m_table->mark_columns_per_binlog_row_image(); - error= m_table->file->ha_delete_row(m_table->record[0]); + if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP)) + { + Field *end= m_table->vers_end_field(); + bitmap_set_bit(m_table->write_set, end->field_index); + store_record(m_table, record[1]); + end->set_time(); + error= m_table->file->ha_update_row(m_table->record[1], + m_table->record[0]); + } + else + { + error= m_table->file->ha_delete_row(m_table->record[0]); + } m_table->default_column_bitmaps(); } if (invoke_triggers && !error && @@ -14193,9 +14239,17 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi) memcpy(m_table->write_set->bitmap, m_cols_ai.bitmap, (m_table->write_set->n_bits + 7) / 8); m_table->mark_columns_per_binlog_row_image(); + if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP)) + m_table->vers_update_fields(); error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]); if (error == HA_ERR_RECORD_IS_THE_SAME) error= 0; + if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP)) + { + store_record(m_table, record[2]); + error= vers_insert_history_row(m_table); + restore_record(m_table, record[2]); + } m_table->default_column_bitmaps(); if (invoke_triggers && !error && @@ -14583,43 +14637,6 @@ Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len, #endif #if defined(MYSQL_SERVER) -/* - Access to the current replication position. - - There is a dummy replacement for this in the embedded library that returns - FALSE; this is used by XtraDB to allow it to access replication stuff while - still being able to use the same plugin in both stand-alone and embedded. - - In this function it's ok to use active_mi, as this is only called for - the main replication server. -*/ -bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, - const char **group_relay_log_name, - ulonglong *relay_log_pos) -{ -#if defined(EMBEDDED_LIBRARY) || !defined(HAVE_REPLICATION) - return FALSE; -#else - const Relay_log_info *rli= &(active_mi->rli); - if (!rli->mi->using_parallel()) - { - *log_file_name= rli->group_master_log_name; - *log_pos= rli->group_master_log_pos + - (rli->future_event_relay_log_pos - rli->group_relay_log_pos); - *group_relay_log_name= rli->group_relay_log_name; - *relay_log_pos= rli->future_event_relay_log_pos; - } - else - { - *log_file_name= ""; - *log_pos= 0; - *group_relay_log_name= ""; - *relay_log_pos= 0; - } - return TRUE; -#endif -} - /** Check if we should write event to the relay log diff --git a/sql/log_event.h b/sql/log_event.h index 6b43dcaa763..97e06c165b9 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1203,7 +1203,7 @@ public: /* The number of seconds the query took to run on the master. */ ulong exec_time; /* Number of bytes written by write() function */ - ulong data_written; + size_t data_written; /* The master's server id (is preserved in the relay log; used to @@ -1253,7 +1253,7 @@ public: #endif /* HAVE_REPLICATION */ virtual const char* get_db() { - return thd ? thd->db : 0; + return thd ? thd->db.str : 0; } #else Log_event() : temp_buf(0), when(0), flags(0) {} @@ -1309,7 +1309,6 @@ public: constructor and pass description_event as an argument. */ static Log_event* read_log_event(IO_CACHE* file, - mysql_mutex_t* log_lock, const Format_description_log_event *description_event, my_bool crc_check); @@ -1365,10 +1364,10 @@ public: static void operator delete(void*, void*) { } #ifdef MYSQL_SERVER - bool write_header(ulong data_length); - bool write_data(const uchar *buf, ulong data_length) + bool write_header(size_t event_data_length); + bool write_data(const uchar *buf, size_t data_length) { return writer->write_data(buf, data_length); } - bool write_data(const char *buf, ulong data_length) + bool write_data(const char *buf, size_t data_length) { return write_data((uchar*)buf, data_length); } bool write_footer() { return writer->write_footer(); } @@ -2115,7 +2114,7 @@ public: #ifdef MYSQL_SERVER - Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length, + Query_log_event(THD* thd_arg, const char* query_arg, size_t query_length, bool using_trans, bool direct, bool suppress_use, int error); const char* get_db() { return db; } #ifdef HAVE_REPLICATION @@ -2499,10 +2498,10 @@ public: bool is_concurrent; /* fname doesn't point to memory inside Log_event::temp_buf */ - void set_fname_outside_temp_buf(const char *afname, uint alen) + void set_fname_outside_temp_buf(const char *afname, size_t alen) { fname= afname; - fname_len= alen; + fname_len= (uint)alen; local_fname= TRUE; } /* fname doesn't point to memory inside Log_event::temp_buf */ @@ -3051,9 +3050,9 @@ public: UNSIGNED_F= 1 }; const char *name; - uint name_len; + size_t name_len; const char *val; - ulong val_len; + size_t val_len; Item_result type; uint charset_number; bool is_null; @@ -3061,8 +3060,8 @@ public: #ifdef MYSQL_SERVER bool deferred; query_id_t query_id; - User_var_log_event(THD* thd_arg, const char *name_arg, uint name_len_arg, - const char *val_arg, ulong val_len_arg, Item_result type_arg, + User_var_log_event(THD* thd_arg, const char *name_arg, size_t name_len_arg, + const char *val_arg, size_t val_len_arg, Item_result type_arg, uint charset_number_arg, uchar flags_arg, bool using_trans, bool direct) :Log_event(thd_arg, 0, using_trans), @@ -3520,7 +3519,7 @@ public: virtual int do_apply_event(rpl_group_info *rgi); enum_skip_reason do_shall_skip(rpl_group_info *rgi); #endif - static bool peek(const char *event_start, uint32 event_len, + static bool peek(const char *event_start, size_t event_len, enum enum_binlog_checksum_alg checksum_alg, rpl_gtid **out_gtid_list, uint32 *out_list_len, const Format_description_log_event *fdev); @@ -3865,7 +3864,7 @@ public: bool is_valid() const { return 1; } }; #endif -char *str_to_hex(char *to, const char *from, uint len); +char *str_to_hex(char *to, const char *from, size_t len); /** @class Annotate_rows_log_event @@ -4609,6 +4608,8 @@ protected: uchar *m_extra_row_data; /* Pointer to extra row data if any */ /* If non null, first byte is length */ + bool m_vers_from_plain; + /* helper functions */ @@ -4759,6 +4760,7 @@ public: __attribute__((unused)), const uchar *after_record) { + DBUG_ASSERT(!table->versioned(VERS_TRX_ID)); return thd->binlog_write_row(table, is_transactional, after_record); } #endif @@ -4840,6 +4842,7 @@ public: const uchar *before_record, const uchar *after_record) { + DBUG_ASSERT(!table->versioned(VERS_TRX_ID)); return thd->binlog_update_row(table, is_transactional, before_record, after_record); } @@ -4929,6 +4932,7 @@ public: const uchar *after_record __attribute__((unused))) { + DBUG_ASSERT(!table->versioned(VERS_TRX_ID)); return thd->binlog_delete_row(table, is_transactional, before_record); } @@ -5024,21 +5028,20 @@ public: DBUG_VOID_RETURN; } - Incident_log_event(THD *thd_arg, Incident incident, LEX_STRING const msg) + Incident_log_event(THD *thd_arg, Incident incident, const LEX_CSTRING *msg) : Log_event(thd_arg, 0, FALSE), m_incident(incident) { DBUG_ENTER("Incident_log_event::Incident_log_event"); DBUG_PRINT("enter", ("m_incident: %d", m_incident)); - m_message.str= NULL; m_message.length= 0; - if (!(m_message.str= (char*) my_malloc(msg.length+1, MYF(MY_WME)))) + if (!(m_message.str= (char*) my_malloc(msg->length+1, MYF(MY_WME)))) { /* Mark this event invalid */ m_incident= INCIDENT_NONE; DBUG_VOID_RETURN; } - strmake(m_message.str, msg.str, msg.length); - m_message.length= msg.length; + strmake(m_message.str, msg->str, msg->length); + m_message.length= msg->length; set_direct_logging(); /* Replicate the incident irregardless of @@skip_replication. */ flags&= ~LOG_EVENT_SKIP_REPLICATION_F; @@ -5189,10 +5192,6 @@ inline int Log_event_writer::write(Log_event *ev) bool slave_execute_deferred_events(THD *thd); #endif -bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos, - const char **group_relay_log_name, - ulonglong *relay_log_pos); - bool event_that_should_be_ignored(const char *buf); bool event_checksum_test(uchar *buf, ulong event_len, enum_binlog_checksum_alg alg); enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len); diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index bda9a345790..20986050203 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifndef MYSQL_CLIENT #include "unireg.h" diff --git a/sql/log_slow.h b/sql/log_slow.h index c52722f0cd7..069c35173b9 100644 --- a/sql/log_slow.h +++ b/sql/log_slow.h @@ -15,6 +15,9 @@ /* Defining what to log to slow log */ +#ifndef LOG_SLOW_INCLUDED +#define LOG_SLOW_INCLUDED + #define LOG_SLOW_VERBOSITY_INIT 0 #define LOG_SLOW_VERBOSITY_INNODB (1U << 0) #define LOG_SLOW_VERBOSITY_QUERY_PLAN (1U << 1) @@ -25,13 +28,26 @@ #define QPLAN_ADMIN (1U << 0) #define QPLAN_FILESORT (1U << 1) #define QPLAN_FILESORT_DISK (1U << 2) -#define QPLAN_FULL_JOIN (1U << 3) -#define QPLAN_FULL_SCAN (1U << 4) -#define QPLAN_QC (1U << 5) -#define QPLAN_QC_NO (1U << 6) -#define QPLAN_TMP_DISK (1U << 7) -#define QPLAN_TMP_TABLE (1U << 8) -#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 9) +#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 3) +#define QPLAN_FULL_JOIN (1U << 4) +#define QPLAN_FULL_SCAN (1U << 5) +#define QPLAN_NOT_USING_INDEX (1U << 6) +#define QPLAN_QC (1U << 7) +#define QPLAN_QC_NO (1U << 8) +#define QPLAN_TMP_TABLE (1U << 9) +#define QPLAN_TMP_DISK (1U << 10) /* ... */ #define QPLAN_MAX (1UL << 31) /* reserved as placeholder */ + +/* Bits for log_slow_disabled_statements */ +#define LOG_SLOW_DISABLE_ADMIN (1 << 0) +#define LOG_SLOW_DISABLE_CALL (1 << 1) +#define LOG_SLOW_DISABLE_SLAVE (1 << 2) +#define LOG_SLOW_DISABLE_SP (1 << 3) + +/* Bits for log_disabled_statements */ +#define LOG_DISABLE_SLAVE (1 << 0) +#define LOG_DISABLE_SP (1 << 1) + +#endif /* LOG_SLOW_INCLUDED */ diff --git a/sql/mariadb.h b/sql/mariadb.h new file mode 100644 index 00000000000..00cf2ed1d9c --- /dev/null +++ b/sql/mariadb.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, 2017, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Include file that should always be included first in all file in the sql + directory. Used to ensure that some files, like my_global.h and my_config.h + are always included first. + It can also be used to speed up compilation by using precompiled headers. + + This file should include a minum set of header files used by all files + and header files that are very seldom changed. + It can also include some defines that all files should be aware of. +*/ + +#ifndef MARIADB_INCLUDED +#define MARIADB_INCLUDED +#include <my_global.h> +#endif /* MARIADB_INCLUDED */ diff --git a/sql/mdl.cc b/sql/mdl.cc index 8ce4e291706..bf708c945f4 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -14,6 +14,7 @@ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ +#include "mariadb.h" #include "sql_class.h" #include "debug_sync.h" #include "sql_array.h" @@ -2086,6 +2087,14 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) */ lock= ticket->m_lock; + if (lock_wait_timeout == 0) + { + mysql_prlock_unlock(&lock->m_rwlock); + MDL_ticket::destroy(ticket); + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); + DBUG_RETURN(TRUE); + } + lock->m_waiting.add_ticket(ticket); /* @@ -2110,11 +2119,11 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout) the parallel replication scheduler should never schedule a DDL while DML's are still running. */ - DBUG_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE && - mdl_request->type != MDL_EXCLUSIVE) || - !(get_thd()->rgi_slave && - get_thd()->rgi_slave->is_parallel_exec && - lock->check_if_conflicting_replication_locks(this))); + DBUG_SLOW_ASSERT((mdl_request->type != MDL_INTENTION_EXCLUSIVE && + mdl_request->type != MDL_EXCLUSIVE) || + !(get_thd()->rgi_slave && + get_thd()->rgi_slave->is_parallel_exec && + lock->check_if_conflicting_replication_locks(this))); mysql_prlock_unlock(&lock->m_rwlock); @@ -2635,7 +2644,7 @@ void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket) void MDL_context::release_lock(MDL_ticket *ticket) { - DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT); + DBUG_SLOW_ASSERT(ticket->m_duration == MDL_EXPLICIT); release_lock(MDL_EXPLICIT, ticket); } @@ -2895,8 +2904,8 @@ bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint, void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration) { - DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION && - duration != MDL_TRANSACTION); + DBUG_SLOW_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION && + duration != MDL_TRANSACTION); m_tickets[MDL_TRANSACTION].remove(mdl_ticket); m_tickets[duration].push_front(mdl_ticket); diff --git a/sql/mdl.h b/sql/mdl.h index 2aeaef89f26..be9cc806ec2 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -347,7 +347,7 @@ public: NAME_LEN) - m_ptr + 1); m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1, m_length - 1); - DBUG_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(mdl_namespace_arg == USER_LOCK || ok_for_lower_case_names(db)); } void mdl_key_init(const MDL_key *rhs) { diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index 6535f16445b..3d3b4da11db 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -32,7 +32,7 @@ flush_io_cache(). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // THD #ifdef HAVE_REPLICATION diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc index d078a183e31..9724ca4e19e 100644 --- a/sql/mf_iocache_encr.cc +++ b/sql/mf_iocache_encr.cc @@ -177,9 +177,9 @@ static int my_b_encr_write(IO_CACHE *info, const uchar *Buffer, size_t Count) crypt_data->inbuf_counter= crypt_data->counter; set_iv(iv, info->pos_in_file, crypt_data->inbuf_counter); - if (encryption_crypt(Buffer, length, ebuffer, &elength, - crypt_data->key, sizeof(crypt_data->key), - iv, sizeof(iv), ENCRYPTION_FLAG_ENCRYPT, + if (encryption_crypt(Buffer, (uint)length, ebuffer, &elength, + crypt_data->key, (uint) sizeof(crypt_data->key), + iv, (uint) sizeof(iv), ENCRYPTION_FLAG_ENCRYPT, keyid, keyver)) { my_errno= 1; @@ -194,7 +194,7 @@ static int my_b_encr_write(IO_CACHE *info, const uchar *Buffer, size_t Count) buffer_length bytes should *always* produce block_length bytes */ DBUG_ASSERT(crypt_data->block_length == 0 || crypt_data->block_length == wlength); - DBUG_ASSERT(elength <= encryption_encrypted_length(length, keyid, keyver)); + DBUG_ASSERT(elength <= encryption_encrypted_length((uint)length, keyid, keyver)); crypt_data->block_length= wlength; } else diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 50918d8dcf2..ec3c85b34c4 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" #include <my_bit.h> #include "sql_select.h" @@ -1655,10 +1656,10 @@ int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size) else if (mrr_mode & DSMRR_IMPL_SORT_ROWIDS) used_str= rowid_ordered; - uint used_str_len= strlen(used_str); - uint copy_len= MY_MIN(used_str_len, size); + size_t used_str_len= strlen(used_str); + size_t copy_len= MY_MIN(used_str_len, size); memcpy(str, used_str, copy_len); - return copy_len; + return (int)copy_len; } return 0; } @@ -1717,7 +1718,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, else { cost->reset(); - *buffer_size= MY_MAX(*buffer_size, + *buffer_size= (uint)MY_MAX(*buffer_size, (size_t)(1.2*rows_in_last_step) * elem_size + primary_file->ref_length + table->key_info[keynr].key_length); } diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h index b8234998f74..56761e3623f 100644 --- a/sql/multi_range_read.h +++ b/sql/multi_range_read.h @@ -57,7 +57,7 @@ Storage engine internals - Currently DS-MRR is used by MyISAM, InnoDB/XtraDB and Maria storage engines. + Currently DS-MRR is used by MyISAM, InnoDB and Maria storage engines. Potentially it can be used with any table handler that has disk-based data storage and has better performance when reading data in rowid order. */ diff --git a/sql/my_apc.cc b/sql/my_apc.cc index 2699ac9c60b..9a0310234a2 100644 --- a/sql/my_apc.cc +++ b/sql/my_apc.cc @@ -17,6 +17,7 @@ #ifndef MY_APC_STANDALONE +#include "mariadb.h" #include "sql_class.h" #endif diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index be732d4a927..338f78d8f08 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include <time.h> @@ -238,7 +238,7 @@ int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec, E_DEC_OOM */ -int str2my_decimal(uint mask, const char *from, uint length, +int str2my_decimal(uint mask, const char *from, size_t length, CHARSET_INFO *charset, my_decimal *decimal_value, const char **end_ptr) { diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 265b370a154..775617dce0f 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -132,8 +132,8 @@ public: void sanity_check() { - DBUG_ASSERT(foo1 == test_value); - DBUG_ASSERT(foo2 == test_value); + DBUG_SLOW_ASSERT(foo1 == test_value); + DBUG_SLOW_ASSERT(foo2 == test_value); } void fix_buffer_pointer() { buf= buffer; } @@ -365,11 +365,11 @@ int str2my_decimal(uint mask, const char *str, my_decimal *d, char **end) } -int str2my_decimal(uint mask, const char *from, uint length, +int str2my_decimal(uint mask, const char *from, size_t length, CHARSET_INFO *charset, my_decimal *decimal_value, const char **end); -inline int str2my_decimal(uint mask, const char *from, uint length, +inline int str2my_decimal(uint mask, const char *from, size_t length, CHARSET_INFO *charset, my_decimal *decimal_value) { const char *end; diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index 390123fbba9..d219e88b98b 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_string.h" @@ -221,7 +221,7 @@ bool Single_line_formatting_helper::on_add_member(const char *name) buf_ptr+=len; *(buf_ptr++)= 0; - line_len= owner->indent_level + len + 1; + line_len= owner->indent_level + (uint)len + 1; state= ADD_MEMBER; return true; // handled } @@ -286,7 +286,7 @@ bool Single_line_formatting_helper::on_add_str(const char *str) memcpy(buf_ptr, str, len); buf_ptr+=len; *(buf_ptr++)= 0; - line_len += len + 4; + line_len += (uint)len + 4; return true; // handled } diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index 5960346c60e..b4af5cc79ed 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -18,9 +18,8 @@ on Windows. */ #define DONT_DEFINE_VOID -#include <my_global.h> +#include "mariadb.h" #include <my_getopt.h> -#include <my_sys.h> #include <m_string.h> #include <windows.h> @@ -525,28 +524,6 @@ static int set_directory_permissions(const char *dir, const char *os_user) } -/* - Give directory permissions for special service user NT SERVICE\servicename - this user is available only on Win7 and later. -*/ - -void grant_directory_permissions_to_service() -{ - char service_user[MAX_PATH+ 12]; - OSVERSIONINFO info; - info.dwOSVersionInfoSize= sizeof(info); - GetVersionEx(&info); - if (info.dwMajorVersion >6 || - (info.dwMajorVersion== 6 && info.dwMinorVersion > 0) - && opt_service) - { - my_snprintf(service_user,sizeof(service_user), "NT SERVICE\\%s", - opt_service); - set_directory_permissions(opt_datadir, service_user); - } -} - - /* Create database instance (including registering as service etc) .*/ static int create_db_instance() @@ -669,7 +646,6 @@ static int create_db_instance() if (opt_service && opt_service[0]) { ret= register_service(); - grant_directory_permissions_to_service(); if (ret) goto end; } diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc index 36de05e54e4..2f242bebb0d 100644 --- a/sql/mysql_upgrade_service.cc +++ b/sql/mysql_upgrade_service.cc @@ -20,8 +20,8 @@ */ #define DONT_DEFINE_VOID +#include "mariadb.h" #include <process.h> -#include <my_global.h> #include <my_getopt.h> #include <my_sys.h> #include <m_string.h> @@ -519,4 +519,4 @@ int main(int argc, char **argv) CloseHandle(logfile_handle); my_end(0); exit(0); -}
\ No newline at end of file +} diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c5804c7d315..1dff9c4591e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" // Includes mariadb.h #include "sql_priv.h" #include "unireg.h" #include <signal.h> @@ -75,6 +75,7 @@ #include "wsrep_var.h" #include "wsrep_thd.h" #include "wsrep_sst.h" +#include "proxy_protocol.h" #include "sql_callback.h" #include "threadpool.h" @@ -96,8 +97,10 @@ #include "set_var.h" #include "rpl_injector.h" +#include "semisync_master.h" +#include "semisync_slave.h" -#include "rpl_handler.h" +#include "transaction.h" #ifdef HAVE_SYS_PRCTL_H #include <sys/prctl.h> @@ -373,6 +376,8 @@ char *my_bind_addr_str; static char *default_collation_name; char *default_storage_engine, *default_tmp_storage_engine; char *enforced_storage_engine=NULL; +char *gtid_pos_auto_engines; +plugin_ref *opt_gtid_pos_auto_plugins; static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; static I_List<CONNECT> thread_cache; static bool binlog_format_used= false; @@ -394,7 +399,6 @@ my_bool disable_log_notes, opt_support_flashback= 0; static my_bool opt_abort; ulonglong log_output_options; my_bool opt_userstat_running; -my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; bool opt_skip_name_resolve=0; @@ -442,6 +446,7 @@ my_bool opt_replicate_annotate_row_events= 0; my_bool opt_mysql56_temporal_format=0, strict_password_validation= 1; my_bool opt_explicit_defaults_for_timestamp= 0; char *opt_slave_skip_errors; +char *opt_slave_transaction_retry_errors; /* Legacy global handlerton. These will be removed (please do not add more). @@ -456,8 +461,6 @@ my_bool relay_log_recovery; my_bool opt_sync_frm, opt_allow_suspicious_udfs; my_bool opt_secure_auth= 0; char* opt_secure_file_priv; -my_bool opt_log_slow_admin_statements= 0; -my_bool opt_log_slow_slave_statements= 0; my_bool lower_case_file_system= 0; my_bool opt_large_pages= 0; my_bool opt_super_large_pages= 0; @@ -491,7 +494,6 @@ uint protocol_version; uint lower_case_table_names; ulong tc_heuristic_recover= 0; int32 thread_count, service_thread_count; -int32 thread_running; int32 slave_open_temp_tables; ulong thread_created; ulong back_log, connect_timeout, concurrency, server_id; @@ -499,6 +501,7 @@ ulong what_to_log; ulong slow_launch_time; ulong open_files_limit, max_binlog_size; ulong slave_trans_retries; +ulong slave_trans_retry_interval; uint slave_net_timeout; ulong slave_exec_mode_options; ulong slave_run_triggers_for_rbr= 0; @@ -506,6 +509,7 @@ ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT; ulonglong slave_type_conversions_options; ulong thread_cache_size=0; ulonglong binlog_cache_size=0; +ulonglong binlog_file_cache_size=0; ulonglong max_binlog_cache_size=0; ulong slave_max_allowed_packet= 0; ulonglong binlog_stmt_cache_size=0; @@ -525,6 +529,9 @@ ulong max_connections, max_connect_errors; ulong extra_max_connections; uint max_digest_length= 0; ulong slave_retried_transactions; +ulong transactions_multi_engine; +ulong rpl_transactions_multi_engine; +ulong transactions_gtid_foreign_engine; ulonglong slave_skipped_errors; ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0; ulonglong denied_connections; @@ -619,7 +626,7 @@ char mysql_real_data_home[FN_REFLEN], *opt_init_file, *opt_tc_log_file; char *lc_messages_dir_ptr= lc_messages_dir, *log_error_file_ptr; char mysql_unpacked_real_data_home[FN_REFLEN]; -int mysql_unpacked_real_data_home_len; +size_t mysql_unpacked_real_data_home_len; uint mysql_real_data_home_len, mysql_data_home_len= 1; uint reg_ext_length; const key_map key_map_empty(0); @@ -908,7 +915,7 @@ PSI_mutex_key key_LOCK_des_key_file; PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_BINLOG_LOCK_binlog_background_thread, - m_key_LOCK_binlog_end_pos, + key_LOCK_binlog_end_pos, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -930,9 +937,11 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_thread_count, key_LOCK_thread_cache, key_PARTITION_LOCK_auto_inc; PSI_mutex_key key_RELAYLOG_LOCK_index; +PSI_mutex_key key_LOCK_relaylog_end_pos; PSI_mutex_key key_LOCK_thread_id; PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; +PSI_mutex_key key_LOCK_binlog; PSI_mutex_key key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, @@ -944,6 +953,10 @@ PSI_mutex_key key_LOCK_after_binlog_sync; PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered, key_LOCK_slave_background; PSI_mutex_key key_TABLE_SHARE_LOCK_share; +PSI_mutex_key key_LOCK_ack_receiver; + +PSI_mutex_key key_TABLE_SHARE_LOCK_rotation; +PSI_cond_key key_TABLE_SHARE_COND_rotation; static PSI_mutex_info all_server_mutexes[]= { @@ -962,8 +975,9 @@ static PSI_mutex_info all_server_mutexes[]= { &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0}, { &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0}, { &key_BINLOG_LOCK_binlog_background_thread, "MYSQL_BIN_LOG::LOCK_binlog_background_thread", 0}, - { &m_key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 }, + { &key_LOCK_binlog_end_pos, "MYSQL_BIN_LOG::LOCK_binlog_end_pos", 0 }, { &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0}, + { &key_LOCK_relaylog_end_pos, "MYSQL_RELAY_LOG::LOCK_binlog_end_pos", 0}, { &key_delayed_insert_mutex, "Delayed_insert::mutex", 0}, { &key_hash_filo_lock, "hash_filo::lock", 0}, { &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL}, @@ -1007,6 +1021,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0}, { &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0}, { &key_TABLE_SHARE_LOCK_share, "TABLE_SHARE::LOCK_share", 0}, + { &key_TABLE_SHARE_LOCK_rotation, "TABLE_SHARE::LOCK_rotation", 0}, { &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL}, { &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL}, { &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL}, @@ -1021,14 +1036,16 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_binlog_state, "LOCK_binlog_state", 0}, { &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0}, { &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0}, - { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0} + { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0}, + { &key_LOCK_ack_receiver, "Ack_receiver::mutex", 0}, + { &key_LOCK_binlog, "LOCK_binlog", 0} }; PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, - key_LOCK_SEQUENCE; - + key_LOCK_SEQUENCE, + key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial; static PSI_rwlock_info all_server_rwlocks[]= { @@ -1041,14 +1058,17 @@ static PSI_rwlock_info all_server_rwlocks[]= { &key_rwlock_LOCK_sys_init_slave, "LOCK_sys_init_slave", PSI_FLAG_GLOBAL}, { &key_LOCK_SEQUENCE, "LOCK_SEQUENCE", 0}, { &key_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL}, - { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0} + { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0}, + { &key_rwlock_LOCK_vers_stats, "Vers_field_stats::lock", 0}, + { &key_rwlock_LOCK_stat_serial, "TABLE_SHARE::LOCK_stat_serial", 0} }; #ifdef HAVE_MMAP PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ -PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, +PSI_cond_key key_BINLOG_COND_xid_list, + key_BINLOG_COND_bin_log_updated, key_BINLOG_COND_relay_log_updated, key_BINLOG_COND_binlog_background_thread, key_BINLOG_COND_binlog_background_thread_end, key_COND_cache_status_changed, key_COND_manager, @@ -1062,9 +1082,10 @@ PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, key_rpl_group_info_sleep_cond, key_TABLE_SHARE_cond, key_user_level_lock_cond, key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache, - key_COND_start_thread, + key_COND_start_thread, key_COND_binlog_send, key_BINLOG_COND_queue_busy; -PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready, +PSI_cond_key key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready, key_COND_wait_commit; PSI_cond_key key_RELAYLOG_COND_queue_busy; PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy; @@ -1073,6 +1094,7 @@ PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread, key_COND_parallel_entry, key_COND_group_commit_orderer, key_COND_prepare_ordered, key_COND_slave_background; PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; +PSI_cond_key key_COND_ack_receiver; static PSI_cond_info all_server_conds[]= { @@ -1085,12 +1107,13 @@ static PSI_cond_info all_server_conds[]= { &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0}, { &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0}, #endif /* HAVE_MMAP */ + { &key_BINLOG_COND_bin_log_updated, "MYSQL_BIN_LOG::COND_bin_log_updated", 0}, { &key_BINLOG_COND_relay_log_updated, "MYSQL_BIN_LOG::COND_relay_log_updated", 0}, { &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0}, - { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0}, { &key_BINLOG_COND_binlog_background_thread, "MYSQL_BIN_LOG::COND_binlog_background_thread", 0}, { &key_BINLOG_COND_binlog_background_thread_end, "MYSQL_BIN_LOG::COND_binlog_background_thread_end", 0}, { &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0}, - { &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0}, + { &key_RELAYLOG_COND_relay_log_updated, "MYSQL_RELAY_LOG::COND_relay_log_updated", 0}, + { &key_RELAYLOG_COND_bin_log_updated, "MYSQL_RELAY_LOG::COND_bin_log_updated", 0}, { &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0}, { &key_COND_wakeup_ready, "THD::COND_wakeup_ready", 0}, { &key_COND_wait_commit, "wait_for_commit::COND_wait_commit", 0}, @@ -1124,13 +1147,17 @@ static PSI_cond_info all_server_conds[]= { &key_COND_slave_background, "COND_slave_background", 0}, { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL}, { &key_COND_wait_gtid, "COND_wait_gtid", 0}, - { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0} + { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}, + { &key_COND_ack_receiver, "Ack_receiver::cond", 0}, + { &key_COND_binlog_send, "COND_binlog_send", 0}, + { &key_TABLE_SHARE_COND_rotation, "TABLE_SHARE::COND_rotation", 0} }; PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_main, key_thread_one_connection, key_thread_signal_hand, key_thread_slave_background, key_rpl_parallel_thread; +PSI_thread_key key_thread_ack_receiver; static PSI_thread_info all_server_threads[]= { @@ -1157,6 +1184,7 @@ static PSI_thread_info all_server_threads[]= { &key_thread_one_connection, "one_connection", 0}, { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}, { &key_thread_slave_background, "slave_background", PSI_FLAG_GLOBAL}, + { &key_thread_ack_receiver, "Ack_receiver", PSI_FLAG_GLOBAL}, { &key_rpl_parallel_thread, "rpl_parallel_thread", 0} }; @@ -1223,7 +1251,7 @@ void net_after_header_psi(struct st_net *net, void *user_data, { thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, stmt_info_new_packet.m_key, - thd->db, thd->db_length, + thd->get_db(), thd->db.length, thd->charset()); THD_STAGE_INFO(thd, stage_init); @@ -1634,13 +1662,11 @@ static void close_connections(void) { if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(base_ip_sock, SHUT_RDWR); (void) mysql_socket_close(base_ip_sock); base_ip_sock= MYSQL_INVALID_SOCKET; } if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(extra_ip_sock, SHUT_RDWR); (void) mysql_socket_close(extra_ip_sock); extra_ip_sock= MYSQL_INVALID_SOCKET; } @@ -1672,7 +1698,6 @@ static void close_connections(void) #ifdef HAVE_SYS_UN_H if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) { - (void) mysql_socket_shutdown(unix_sock, SHUT_RDWR); (void) mysql_socket_close(unix_sock); (void) unlink(mysqld_unix_port); unix_sock= MYSQL_INVALID_SOCKET; @@ -1739,6 +1764,7 @@ static void close_connections(void) Events::deinit(); slave_prepare_for_shutdown(); mysql_bin_log.stop_background_thread(); + ack_receiver.stop(); /* Give threads time to die. @@ -2168,7 +2194,10 @@ static void mysqld_exit(int exit_code) (long) global_status_var.global_memory_used); if (!opt_debugging && !my_disable_leak_check && exit_code == 0) { - DBUG_ASSERT(global_status_var.global_memory_used == 0); +#ifdef SAFEMALLOC + sf_report_leaked_memory(0); +#endif + DBUG_SLOW_ASSERT(global_status_var.global_memory_used == 0); } cleanup_tls(); DBUG_LEAVE; @@ -2217,7 +2246,9 @@ void clean_up(bool print_message) ha_end(); if (tc_log) tc_log->close(); - delegates_destroy(); +#ifdef HAVE_REPLICATION + semi_sync_master_deinit(); +#endif xid_cache_free(); tdc_deinit(); mdl_destroy(); @@ -2292,6 +2323,7 @@ void clean_up(bool print_message) my_free(const_cast<char*>(relay_log_index)); #endif free_list(opt_plugin_load_list_ptr); + cleanup_proxy_protocol_networks(); /* The following lines may never be executed as the main thread may have @@ -2689,6 +2721,9 @@ static void network_init(void) if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) unireg_abort(1); /* purecov: inspected */ + if (set_proxy_protocol_networks(my_proxy_protocol_networks)) + unireg_abort(1); + set_ports(); if (report_port == 0) @@ -2709,7 +2744,7 @@ static void network_init(void) #ifdef _WIN32 /* create named pipe */ - if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap && + if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) { @@ -2899,16 +2934,17 @@ void unlink_thd(THD *thd) DBUG_ENTER("unlink_thd"); DBUG_PRINT("enter", ("thd: %p", thd)); + thd->cleanup(); + thd->add_status_to_global(); + unlink_not_visible_thd(thd); + /* Do not decrement when its wsrep system thread. wsrep_applier is set for applier as well as rollbacker threads. */ if (IF_WSREP(!thd->wsrep_applier, 1)) dec_connection_count(thd->scheduler); - thd->cleanup(); - thd->add_status_to_global(); - unlink_not_visible_thd(thd); thd->free_connection(); DBUG_VOID_RETURN; @@ -2946,13 +2982,11 @@ static bool cache_thread(THD *thd) DBUG_PRINT("info", ("Adding thread to cache")); cached_thread_count++; -#ifdef HAVE_PSI_THREAD_INTERFACE /* Delete the instrumentation for the job that just completed, before parking this pthread in the cache (blocked on COND_thread_cache). */ - PSI_THREAD_CALL(delete_current_thread)(); -#endif + PSI_CALL_delete_current_thread(); #ifndef DBUG_OFF while (_db_is_pushed_()) @@ -2999,15 +3033,13 @@ static bool cache_thread(THD *thd) */ thd->store_globals(); -#ifdef HAVE_PSI_THREAD_INTERFACE /* Create new instrumentation for the new THD job, and attach it to this running pthread. */ - PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_one_connection, + PSI_thread *psi= PSI_CALL_new_thread(key_thread_one_connection, thd, thd->thread_id); - PSI_THREAD_CALL(set_thread)(psi); -#endif + PSI_CALL_set_thread(psi); /* reset abort flag for the thread */ thd->mysys_var->abort= 0; @@ -3324,6 +3356,20 @@ static size_t my_setstacksize(pthread_attr_t *attr, size_t stacksize) } #endif +#ifdef DBUG_ASSERT_AS_PRINTF +extern "C" void +mariadb_dbug_assert_failed(const char *assert_expr, const char *file, + unsigned long line) +{ + fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n", + assert_expr, file, line); + if (opt_stack_trace) + { + fprintf(stderr, "Attempting backtrace to find out the reason for the assert:\n"); + my_print_stacktrace(NULL, (ulong) my_thread_stack_size, 1); + } +} +#endif /* DBUG_ASSERT_AS_PRINT */ #if !defined(__WIN__) #ifndef SA_RESETHAND @@ -3524,10 +3570,8 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) if (!abort_loop) { abort_loop=1; // mark abort for threads -#ifdef HAVE_PSI_THREAD_INTERFACE /* Delete the instrumentation for the signal thread */ - PSI_THREAD_CALL(delete_current_thread)(); -#endif + PSI_CALL_delete_current_thread(); #ifdef USE_ONE_SIGNAL_HAND pthread_t tmp; if ((error= mysql_thread_create(0, /* Not instrumented */ @@ -4128,7 +4172,10 @@ static int init_common_variables() #ifdef SAFEMALLOC sf_malloc_dbug_id= mariadb_dbug_id; -#endif +#endif /* SAFEMALLOC */ +#ifdef DBUG_ASSERT_AS_PRINTF + my_dbug_assert_failed= mariadb_dbug_assert_failed; +#endif /* DBUG_ASSERT_AS_PRINTF */ if (!(type_handler_data= new Type_handler_data) || type_handler_data->init()) @@ -4197,10 +4244,12 @@ static int init_common_variables() constructor (called before main()). */ mysql_bin_log.set_psi_keys(key_BINLOG_LOCK_index, - key_BINLOG_update_cond, + key_BINLOG_COND_relay_log_updated, + key_BINLOG_COND_bin_log_updated, key_file_binlog, key_file_binlog_index, - key_BINLOG_COND_queue_busy); + key_BINLOG_COND_queue_busy, + key_LOCK_binlog_end_pos); #endif /* @@ -4250,12 +4299,13 @@ static int init_common_variables() (except in the embedded server, where the default continues to be MyISAM) */ -#if defined(WITH_INNOBASE_STORAGE_ENGINE) || defined(WITH_XTRADB_STORAGE_ENGINE) +#if defined(WITH_INNOBASE_STORAGE_ENGINE) default_storage_engine= const_cast<char *>("InnoDB"); #else default_storage_engine= const_cast<char *>("MyISAM"); #endif default_tmp_storage_engine= NULL; + gtid_pos_auto_engines= const_cast<char *>(""); /* Add server status variables to the dynamic list of @@ -4671,6 +4721,8 @@ static int init_common_variables() return 1; } + global_system_variables.in_subquery_conversion_threshold= IN_SUBQUERY_CONVERSION_THRESHOLD; + return 0; } @@ -4949,6 +5001,34 @@ static int init_default_storage_engine_impl(const char *opt_name, return 0; } + +static int +init_gtid_pos_auto_engines(void) +{ + plugin_ref *plugins; + + /* + For the command-line option --gtid_pos_auto_engines, we allow (and ignore) + engines that are unknown. This is convenient, since it allows to set + default auto-create engines that might not be used by particular users. + The option sets a list of storage engines that will have gtid position + table auto-created for them if needed. And if the engine is not available, + then it will certainly not be needed. + */ + if (gtid_pos_auto_engines) + plugins= resolve_engine_list(NULL, gtid_pos_auto_engines, + strlen(gtid_pos_auto_engines), false, false); + else + plugins= resolve_engine_list(NULL, "", 0, false, false); + if (!plugins) + return 1; + mysql_mutex_lock(&LOCK_global_system_variables); + opt_gtid_pos_auto_plugins= plugins; + mysql_mutex_unlock(&LOCK_global_system_variables); + return 0; +} + + static int init_server_components() { DBUG_ENTER("init_server_components"); @@ -5066,13 +5146,6 @@ static int init_server_components() xid_cache_init(); - /* - initialize delegates for extension observers, errors have already - been reported in the function - */ - if (delegates_init()) - unireg_abort(1); - /* need to configure logging before initializing storage engines */ if (!opt_bin_log_used && !WSREP_ON) { @@ -5104,6 +5177,13 @@ static int init_server_components() "this server. However this will be ignored as the " "--log-bin option is not defined."); } + + if (repl_semisync_master.init_object() || + repl_semisync_slave.init_object()) + { + sql_print_error("Could not initialize semisync."); + unireg_abort(1); + } #endif if (opt_bin_log) @@ -5392,6 +5472,9 @@ static int init_server_components() if (init_default_storage_engine(enforced_storage_engine, enforced_table_plugin)) unireg_abort(1); + if (init_gtid_pos_auto_engines()) + unireg_abort(1); + #ifdef USE_ARIA_FOR_TMP_TABLES if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap) { @@ -5606,8 +5689,8 @@ static void test_lc_time_sz() DBUG_ENTER("test_lc_time_sz"); for (MY_LOCALE **loc= my_locales; *loc; loc++) { - uint max_month_len= 0; - uint max_day_len = 0; + size_t max_month_len= 0; + size_t max_day_len= 0; for (const char **month= (*loc)->month_names->type_names; *month; month++) { set_if_bigger(max_month_len, @@ -5754,8 +5837,8 @@ int mysqld_main(int argc, char **argv) */ init_server_psi_keys(); /* Instrument the main thread */ - PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_main, NULL, 0); - PSI_THREAD_CALL(set_thread)(psi); + PSI_thread *psi= PSI_CALL_new_thread(key_thread_main, NULL, 0); + PSI_CALL_set_thread(psi); /* Now that some instrumentation is in place, @@ -6074,13 +6157,11 @@ int mysqld_main(int argc, char **argv) mysql_mutex_unlock(&LOCK_start_thread); #endif /* __WIN__ */ -#ifdef HAVE_PSI_THREAD_INTERFACE /* Disable the main thread instrumentation, to avoid recording events during the shutdown. */ - PSI_THREAD_CALL(delete_current_thread)(); -#endif + PSI_CALL_delete_current_thread(); /* Wait until cleanup is done */ mysql_mutex_lock(&LOCK_thread_count); @@ -6089,7 +6170,7 @@ int mysqld_main(int argc, char **argv) mysql_mutex_unlock(&LOCK_thread_count); #if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) - if (Service.IsNT() && start_mode) + if (start_mode) Service.Stop(); else { @@ -6232,87 +6313,86 @@ int mysqld_main(int argc, char **argv) return 1; } - if (Service.GetOS()) /* true NT family */ + + char file_path[FN_REFLEN]; + my_path(file_path, argv[0], ""); /* Find name in path */ + fn_format(file_path,argv[0],file_path,"", MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS); + + if (argc == 2) { - char file_path[FN_REFLEN]; - my_path(file_path, argv[0], ""); /* Find name in path */ - fn_format(file_path,argv[0],file_path,"", - MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS); + if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME, + file_path, "", NULL)) + return 0; - if (argc == 2) - { - if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME, - file_path, "", NULL)) - return 0; - if (Service.IsService(argv[1])) /* Start an optional service */ - { - /* - Only add the service name to the groups read from the config file - if it's not "MySQL". (The default service name should be 'mysqld' - but we started a bad tradition by calling it MySQL from the start - and we are now stuck with it. - */ - if (my_strcasecmp(system_charset_info, argv[1],"mysql")) - load_default_groups[load_default_groups_sz-2]= argv[1]; - start_mode= 1; - Service.Init(argv[1], mysql_service); - return 0; - } - } - else if (argc == 3) /* install or remove any optional service */ - { - if (!default_service_handling(argv, argv[2], argv[2], file_path, "", - NULL)) - return 0; - if (Service.IsService(argv[2])) - { - /* - mysqld was started as - mysqld --defaults-file=my_path\my.ini service-name - */ - use_opt_args=1; - opt_argc= 2; // Skip service-name - opt_argv=argv; - start_mode= 1; - if (my_strcasecmp(system_charset_info, argv[2],"mysql")) - load_default_groups[load_default_groups_sz-2]= argv[2]; - Service.Init(argv[2], mysql_service); - return 0; - } - } - else if (argc == 4 || argc == 5) + if (Service.IsService(argv[1])) /* Start an optional service */ { /* - This may seem strange, because we handle --local-service while - preserving 4.1's behavior of allowing any one other argument that is - passed to the service on startup. (The assumption is that this is - --defaults-file=file, but that was not enforced in 4.1, so we don't - enforce it here.) + Only add the service name to the groups read from the config file + if it's not "MySQL". (The default service name should be 'mysqld' + but we started a bad tradition by calling it MySQL from the start + and we are now stuck with it. */ - const char *extra_opt= NullS; - const char *account_name = NullS; - int index; - for (index = 3; index < argc; index++) - { - if (!strcmp(argv[index], "--local-service")) - account_name= "NT AUTHORITY\\LocalService"; - else - extra_opt= argv[index]; - } - - if (argc == 4 || account_name) - if (!default_service_handling(argv, argv[2], argv[2], file_path, - extra_opt, account_name)) - return 0; + if (my_strcasecmp(system_charset_info, argv[1],"mysql")) + load_default_groups[load_default_groups_sz-2]= argv[1]; + start_mode= 1; + Service.Init(argv[1], mysql_service); + return 0; } - else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME)) + } + else if (argc == 3) /* install or remove any optional service */ + { + if (!default_service_handling(argv, argv[2], argv[2], file_path, "", + NULL)) + return 0; + if (Service.IsService(argv[2])) { - /* start the default service */ + /* + mysqld was started as + mysqld --defaults-file=my_path\my.ini service-name + */ + use_opt_args=1; + opt_argc= 2; // Skip service-name + opt_argv=argv; start_mode= 1; - Service.Init(MYSQL_SERVICENAME, mysql_service); + if (my_strcasecmp(system_charset_info, argv[2],"mysql")) + load_default_groups[load_default_groups_sz-2]= argv[2]; + Service.Init(argv[2], mysql_service); return 0; } } + else if (argc == 4 || argc == 5) + { + /* + This may seem strange, because we handle --local-service while + preserving 4.1's behavior of allowing any one other argument that is + passed to the service on startup. (The assumption is that this is + --defaults-file=file, but that was not enforced in 4.1, so we don't + enforce it here.) + */ + const char *extra_opt= NullS; + const char *account_name = NullS; + int index; + for (index = 3; index < argc; index++) + { + if (!strcmp(argv[index], "--local-service")) + account_name= "NT AUTHORITY\\LocalService"; + else + extra_opt= argv[index]; + } + + if (argc == 4 || account_name) + if (!default_service_handling(argv, argv[2], argv[2], file_path, + extra_opt, account_name)) + return 0; + } + else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME)) + { + /* start the default service */ + start_mode= 1; + Service.Init(MYSQL_SERVICENAME, mysql_service); + return 0; + } + /* Start as standalone server */ Service.my_argc=argc; Service.my_argv=argv; @@ -6353,6 +6433,7 @@ static void bootstrap(MYSQL_FILE *file) sql_print_warning("Can't create thread to handle bootstrap (errno= %d)", error); bootstrap_error=-1; + delete thd; DBUG_VOID_RETURN; } /* Wait for thread to die */ @@ -7232,9 +7313,6 @@ struct my_option my_long_options[]= {"autocommit", 0, "Set default value for autocommit (0 or 1)", &opt_autocommit, &opt_autocommit, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, NULL}, - {"bind-address", 0, "IP address to bind to.", - &my_bind_addr_str, &my_bind_addr_str, 0, GET_STR, - REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, " "and exclude all others not explicitly mentioned.", @@ -7385,6 +7463,14 @@ struct my_option my_long_options[]= "Set up signals usable for debugging. Deprecated, use --debug-gdb instead.", &opt_debugging, &opt_debugging, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"gtid-pos-auto-engines", 0, + "List of engines for which to automatically create a " + "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine " + "is replicated. This can be used to avoid introducing cross-engine " + "transactions, if engines are used different from that used by table " + "mysql.gtid_slave_pos", + >id_pos_auto_engines, 0, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0 }, #ifdef HAVE_LARGE_PAGE_OPTION {"super-large-pages", 0, "Enable support for super large pages.", &opt_super_large_pages, &opt_super_large_pages, 0, @@ -7682,6 +7768,7 @@ struct my_option my_long_options[]= MYSQL_SUGGEST_ANALOG_OPTION("max-binlog-dump-events", "--debug-max-binlog-dump-events"), MYSQL_SUGGEST_ANALOG_OPTION("sporadic-binlog-dump-fail", "--debug-sporadic-binlog-dump-fail"), MYSQL_COMPATIBILITY_OPTION("new"), + MYSQL_COMPATIBILITY_OPTION("show_compatibility_56"), /* The following options were added after 5.6.10 */ MYSQL_TO_BE_IMPLEMENTED_OPTION("rpl-stop-slave-timeout"), @@ -7782,7 +7869,7 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) var->type= SHOW_LONGLONG; var->value= buff; - *((longlong *)buff)= any_slave_sql_running(); + *((longlong *)buff)= any_slave_sql_running(false); return 0; } @@ -8154,6 +8241,27 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff, return 0; } +#define SHOW_FNAME(name) \ + rpl_semi_sync_master_show_##name + +#define DEF_SHOW_FUNC(name, show_type) \ + static int SHOW_FNAME(name)(MYSQL_THD thd, SHOW_VAR *var, char *buff) \ + { \ + repl_semisync_master.set_export_stats(); \ + var->type= show_type; \ + var->value= (char *)&rpl_semi_sync_master_##name; \ + return 0; \ + } + +DEF_SHOW_FUNC(status, SHOW_BOOL) +DEF_SHOW_FUNC(clients, SHOW_LONG) +DEF_SHOW_FUNC(wait_sessions, SHOW_LONG) +DEF_SHOW_FUNC(trx_wait_time, SHOW_LONGLONG) +DEF_SHOW_FUNC(trx_wait_num, SHOW_LONGLONG) +DEF_SHOW_FUNC(net_wait_time, SHOW_LONGLONG) +DEF_SHOW_FUNC(net_wait_num, SHOW_LONGLONG) +DEF_SHOW_FUNC(avg_net_wait_time, SHOW_LONG) +DEF_SHOW_FUNC(avg_trx_wait_time, SHOW_LONG) #ifdef HAVE_YASSL @@ -8384,6 +8492,8 @@ SHOW_VAR status_vars[]= { {"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS}, {"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS}, {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS}, + {"Column_compressions", (char*) offsetof(STATUS_VAR, column_compressions), SHOW_LONG_STATUS}, + {"Column_decompressions", (char*) offsetof(STATUS_VAR, column_decompressions), SHOW_LONG_STATUS}, {"Com", (char*) com_status_vars, SHOW_ARRAY}, {"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC}, {"Connections", (char*) &global_thread_id, SHOW_LONG_NOFLUSH}, @@ -8412,6 +8522,7 @@ SHOW_VAR status_vars[]= { {"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS}, {"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS}, {"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS}, + {"Feature_invisible_columns", (char*) offsetof(STATUS_VAR, feature_invisible_columns), SHOW_LONG_STATUS}, {"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS}, {"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS}, {"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS}, @@ -8422,7 +8533,7 @@ SHOW_VAR status_vars[]= { {"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS}, {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS}, {"Handler_discover", (char*) offsetof(STATUS_VAR, ha_discover_count), SHOW_LONG_STATUS}, - {"Handler_external_lock", (char*) offsetof(STATUS_VAR, ha_external_lock_count), SHOW_LONGLONG_STATUS}, + {"Handler_external_lock", (char*) offsetof(STATUS_VAR, ha_external_lock_count), SHOW_LONG_STATUS}, {"Handler_icp_attempts", (char*) offsetof(STATUS_VAR, ha_icp_attempts), SHOW_LONG_STATUS}, {"Handler_icp_match", (char*) offsetof(STATUS_VAR, ha_icp_match), SHOW_LONG_STATUS}, {"Handler_mrr_init", (char*) offsetof(STATUS_VAR, ha_mrr_init_count), SHOW_LONG_STATUS}, @@ -8469,6 +8580,26 @@ SHOW_VAR status_vars[]= { {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS}, {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS}, {"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS}, +#ifdef HAVE_REPLICATION + {"Rpl_semi_sync_master_status", (char*) &SHOW_FNAME(status), SHOW_FUNC}, + {"Rpl_semi_sync_master_clients", (char*) &SHOW_FNAME(clients), SHOW_FUNC}, + {"Rpl_semi_sync_master_yes_tx", (char*) &rpl_semi_sync_master_yes_transactions, SHOW_LONG}, + {"Rpl_semi_sync_master_no_tx", (char*) &rpl_semi_sync_master_no_transactions, SHOW_LONG}, + {"Rpl_semi_sync_master_wait_sessions", (char*) &SHOW_FNAME(wait_sessions), SHOW_FUNC}, + {"Rpl_semi_sync_master_no_times", (char*) &rpl_semi_sync_master_off_times, SHOW_LONG}, + {"Rpl_semi_sync_master_timefunc_failures", (char*) &rpl_semi_sync_master_timefunc_fails, SHOW_LONG}, + {"Rpl_semi_sync_master_wait_pos_backtraverse", (char*) &rpl_semi_sync_master_wait_pos_backtraverse, SHOW_LONG}, + {"Rpl_semi_sync_master_tx_wait_time", (char*) &SHOW_FNAME(trx_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_tx_waits", (char*) &SHOW_FNAME(trx_wait_num), SHOW_FUNC}, + {"Rpl_semi_sync_master_tx_avg_wait_time", (char*) &SHOW_FNAME(avg_trx_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_wait_time", (char*) &SHOW_FNAME(net_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_waits", (char*) &SHOW_FNAME(net_wait_num), SHOW_FUNC}, + {"Rpl_semi_sync_master_net_avg_wait_time", (char*) &SHOW_FNAME(avg_net_wait_time), SHOW_FUNC}, + {"Rpl_semi_sync_master_request_ack", (char*) &rpl_semi_sync_master_request_ack, SHOW_LONGLONG}, + {"Rpl_semi_sync_master_get_ack", (char*)&rpl_semi_sync_master_get_ack, SHOW_LONGLONG}, + {"Rpl_semi_sync_slave_status", (char*) &rpl_semi_sync_slave_status, SHOW_BOOL}, + {"Rpl_semi_sync_slave_send_ack", (char*) &rpl_semi_sync_slave_send_ack, SHOW_LONGLONG}, +#endif /* HAVE_REPLICATION */ #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -8545,6 +8676,10 @@ SHOW_VAR status_vars[]= { {"Subquery_cache_miss", (char*) &subquery_cache_miss, SHOW_LONG}, {"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG}, {"Table_locks_waited", (char*) &locks_waited, SHOW_LONG}, + {"Table_open_cache_active_instances", (char*) &tc_active_instances, SHOW_UINT}, + {"Table_open_cache_hits", (char*) offsetof(STATUS_VAR, table_open_cache_hits), SHOW_LONGLONG_STATUS}, + {"Table_open_cache_misses", (char*) offsetof(STATUS_VAR, table_open_cache_misses), SHOW_LONGLONG_STATUS}, + {"Table_open_cache_overflows", (char*) offsetof(STATUS_VAR, table_open_cache_overflows), SHOW_LONGLONG_STATUS}, #ifdef HAVE_MMAP {"Tc_log_max_pages_used", (char*) &tc_log_max_pages_used, SHOW_LONG}, {"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG_NOFLUSH}, @@ -8557,7 +8692,10 @@ SHOW_VAR status_vars[]= { {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH}, {"Threads_connected", (char*) &connection_count, SHOW_INT}, {"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH}, - {"Threads_running", (char*) &thread_running, SHOW_INT}, + {"Threads_running", (char*) offsetof(STATUS_VAR, threads_running), SHOW_UINT32_STATUS}, + {"Transactions_multi_engine", (char*) &transactions_multi_engine, SHOW_LONG}, + {"Rpl_transactions_multi_engine", (char*) &rpl_transactions_multi_engine, SHOW_LONG}, + {"Transactions_gtid_foreign_engine", (char*) &transactions_gtid_foreign_engine, SHOW_LONG}, {"Update_scan", (char*) offsetof(STATUS_VAR, update_scan_count), SHOW_LONG_STATUS}, {"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC}, #ifdef ENABLED_PROFILING @@ -8731,7 +8869,7 @@ static int mysql_init_variables(void) kill_in_progress= 0; cleanup_done= 0; test_flags= select_errors= dropping_tables= ha_open_options=0; - thread_count= thread_running= kill_cached_threads= wake_thread= 0; + thread_count= kill_cached_threads= wake_thread= 0; service_thread_count= 0; slave_open_temp_tables= 0; cached_thread_count= 0; @@ -8801,6 +8939,9 @@ static int mysql_init_variables(void) report_user= report_password = report_host= 0; /* TO BE DELETED */ opt_relay_logname= opt_relaylog_index_name= 0; slave_retried_transactions= 0; + transactions_multi_engine= 0; + rpl_transactions_multi_engine= 0; + transactions_gtid_foreign_engine= 0; log_bin_basename= NULL; log_bin_index= NULL; @@ -8955,12 +9096,12 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument) opt->name); break; case OPT_MYSQL_COMPATIBILITY: - sql_print_warning("'%s' is MySQL 5.6 compatible option. Not used or needed " - "in MariaDB.", opt->name); + sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. Not used or " + "needed in MariaDB.", opt->name); break; case OPT_MYSQL_TO_BE_IMPLEMENTED: - sql_print_warning("'%s' is MySQL 5.6 compatible option. To be implemented " - "in later versions.", opt->name); + sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. To be " + "implemented in later versions.", opt->name); break; case 'a': SYSVAR_AUTOSIZE(global_system_variables.sql_mode, MODE_ANSI); @@ -9548,8 +9689,10 @@ static int get_options(int *argc_ptr, char ***argv_ptr) flush_time= 0; #ifdef HAVE_REPLICATION - if (opt_slave_skip_errors) - init_slave_skip_errors(opt_slave_skip_errors); + if (init_slave_skip_errors(opt_slave_skip_errors)) + return 1; + if (init_slave_transaction_retry_errors(opt_slave_transaction_retry_errors)) + return 1; #endif if (global_system_variables.max_join_size == HA_POS_ERROR) @@ -9716,7 +9859,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr) /* Ensure that some variables are not set higher than needed */ if (thread_cache_size > max_connections) SYSVAR_AUTOSIZE(thread_cache_size, max_connections); - + return 0; } @@ -9864,7 +10007,7 @@ static int fix_paths(void) my_realpath(mysql_unpacked_real_data_home, mysql_real_data_home, MYF(0)); mysql_unpacked_real_data_home_len= - (int) strlen(mysql_unpacked_real_data_home); + strlen(mysql_unpacked_real_data_home); if (mysql_unpacked_real_data_home[mysql_unpacked_real_data_home_len-1] == FN_LIBCHAR) --mysql_unpacked_real_data_home_len; @@ -10194,6 +10337,10 @@ PSI_stage_info stage_waiting_for_insert= { 0, "Waiting for INSERT", 0}; PSI_stage_info stage_waiting_for_master_to_send_event= { 0, "Waiting for master to send event", 0}; PSI_stage_info stage_waiting_for_master_update= { 0, "Waiting for master update", 0}; PSI_stage_info stage_waiting_for_relay_log_space= { 0, "Waiting for the slave SQL thread to free enough relay log space", 0}; +PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave= +{ 0, "Waiting for semi-sync ACK from slave", 0}; +PSI_stage_info stage_waiting_for_semi_sync_slave={ 0, "Waiting for semi-sync slave connection", 0}; +PSI_stage_info stage_reading_semi_sync_ack={ 0, "Reading semi-sync ACK from slave", 0}; PSI_stage_info stage_waiting_for_slave_mutex_on_exit= { 0, "Waiting for slave mutex on exit", 0}; PSI_stage_info stage_waiting_for_slave_thread_to_start= { 0, "Waiting for slave thread to start", 0}; PSI_stage_info stage_waiting_for_table_flush= { 0, "Waiting for table flush", 0}; @@ -10354,6 +10501,9 @@ PSI_stage_info *all_server_stages[]= & stage_gtid_wait_other_connection, & stage_slave_background_process_request, & stage_slave_background_wait_request, + & stage_waiting_for_semi_sync_ack_from_slave, + & stage_waiting_for_semi_sync_slave, + & stage_reading_semi_sync_ack, & stage_waiting_for_deadlock_kill }; diff --git a/sql/mysqld.h b/sql/mysqld.h index 5c6ad695354..069ad85dd16 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -17,8 +17,8 @@ #ifndef MYSQLD_INCLUDED #define MYSQLD_INCLUDED -#include <my_global.h> /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */ #include "sql_basic_types.h" /* query_id_t */ +#include "sql_plugin.h" #include "sql_bitmap.h" /* Bitmap */ #include "my_decimal.h" /* my_decimal */ #include "mysql_com.h" /* SERVER_VERSION_LENGTH */ @@ -37,7 +37,6 @@ class Time_zone; struct scheduler_functions; -typedef struct st_mysql_const_lex_string LEX_CSTRING; typedef struct st_mysql_show_var SHOW_VAR; #if MAX_INDEXES <= 64 @@ -116,7 +115,6 @@ extern my_bool opt_backup_progress_log; extern my_bool opt_support_flashback; extern ulonglong log_output_options; extern ulong log_backup_output_options; -extern my_bool opt_log_queries_not_using_indexes; extern bool opt_disable_networking, opt_skip_show_db; extern bool opt_skip_name_resolve; extern bool opt_ignore_builtin_innodb; @@ -130,6 +128,9 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options; extern ulong slave_retried_transactions; +extern ulong transactions_multi_engine; +extern ulong rpl_transactions_multi_engine; +extern ulong transactions_gtid_foreign_engine; extern ulong slave_run_triggers_for_rbr; extern ulonglong slave_type_conversions_options; extern my_bool read_only, opt_readonly; @@ -140,7 +141,6 @@ extern const char *current_dbug_option; extern char* opt_secure_file_priv; extern char* opt_secure_backup_file_priv; extern size_t opt_secure_backup_file_priv_len; -extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements; extern my_bool sp_automatic_privileges, opt_noacl; extern ulong use_stat_tables; extern my_bool opt_old_style_user_limits, trust_function_creators; @@ -151,8 +151,11 @@ extern my_bool opt_enable_shared_memory; 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 char *default_storage_engine, *default_tmp_storage_engine; extern char *enforced_storage_engine; +extern char *gtid_pos_auto_engines; +extern plugin_ref *opt_gtid_pos_auto_plugins; extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; extern bool opt_using_transactions; @@ -175,6 +178,36 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname, *opt_backup_settings_name; extern const char *log_output_str; extern const char *log_backup_output_str; + +/* System Versioning begin */ +enum vers_system_time_t +{ + SYSTEM_TIME_UNSPECIFIED = 0, + SYSTEM_TIME_AS_OF, + SYSTEM_TIME_FROM_TO, + SYSTEM_TIME_BETWEEN, + SYSTEM_TIME_BEFORE, + SYSTEM_TIME_ALL +}; + +struct vers_asof_timestamp_t +{ + ulong type; + MYSQL_TIME ltime; + vers_asof_timestamp_t() : + type(SYSTEM_TIME_UNSPECIFIED) + {} +}; + +enum vers_alter_history_enum +{ + VERS_ALTER_HISTORY_ERROR= 0, + VERS_ALTER_HISTORY_KEEP, + VERS_ALTER_HISTORY_SURVIVE, + VERS_ALTER_HISTORY_DROP +}; +/* System Versioning end */ + extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; @@ -204,13 +237,14 @@ extern my_bool slave_allow_batching; extern my_bool allow_slave_start; extern LEX_CSTRING reason_slave_blocked; extern ulong slave_trans_retries; +extern ulong slave_trans_retry_interval; extern uint slave_net_timeout; extern int max_user_connections; extern volatile ulong cached_thread_count; extern ulong what_to_log,flush_time; extern ulong max_prepared_stmt_count, prepared_stmt_count; extern ulong open_files_limit; -extern ulonglong binlog_cache_size, binlog_stmt_cache_size; +extern ulonglong binlog_cache_size, binlog_stmt_cache_size, binlog_file_cache_size; extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size; extern ulong max_binlog_size; extern ulong slave_max_allowed_packet; @@ -231,7 +265,7 @@ extern "C" MYSQL_PLUGIN_IMPORT ulong server_id; extern ulong concurrency; extern time_t server_start_time, flush_status_time; extern char *opt_mysql_tmpdir, mysql_charsets_dir[]; -extern int mysql_unpacked_real_data_home_len; +extern size_t mysql_unpacked_real_data_home_len; extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list; extern const char *first_keyword, *delayed_user, *binary_keyword; extern MYSQL_PLUGIN_IMPORT const char *my_localhost; @@ -284,7 +318,7 @@ extern PSI_mutex_key key_LOCK_des_key_file; extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_BINLOG_LOCK_binlog_background_thread, - m_key_LOCK_binlog_end_pos, + key_LOCK_binlog_end_pos, key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, @@ -304,18 +338,22 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_start_thread, key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc; extern PSI_mutex_key key_RELAYLOG_LOCK_index; +extern PSI_mutex_key key_LOCK_relaylog_end_pos; extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, - key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit; + key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit, + key_TABLE_SHARE_LOCK_rotation; extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, - key_LOCK_SEQUENCE; + key_LOCK_SEQUENCE, + key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial; + #ifdef HAVE_MMAP extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ @@ -335,7 +373,8 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond, key_TABLE_SHARE_cond, key_user_level_lock_cond, key_COND_start_thread, key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache; -extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready, +extern PSI_cond_key key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready, key_COND_wait_commit; extern PSI_cond_key key_RELAYLOG_COND_queue_busy; extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy; @@ -343,6 +382,7 @@ extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue, key_COND_rpl_thread_stop, key_COND_rpl_thread_pool, key_COND_parallel_entry, key_COND_group_commit_orderer; extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; +extern PSI_cond_key key_TABLE_SHARE_COND_rotation; extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_kill_server, key_thread_main, @@ -559,6 +599,7 @@ extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[]; extern char mysql_unpacked_real_data_home[]; extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables; extern char default_logfile_name[FN_REFLEN]; +extern char *my_proxy_protocol_networks; #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) @@ -589,7 +630,6 @@ extern mysql_prlock_t LOCK_system_variables_hash; extern mysql_cond_t COND_thread_count, COND_start_thread; extern mysql_cond_t COND_manager; extern mysql_cond_t COND_slave_background; -extern int32 thread_running; extern int32 thread_count, service_thread_count; extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, @@ -771,16 +811,6 @@ inline void thread_safe_decrement64(int64 *value) (void) my_atomic_add64_explicit(value, -1, MY_MEMORY_ORDER_RELAXED); } -inline void inc_thread_running() -{ - thread_safe_increment32(&thread_running); -} - -inline void dec_thread_running() -{ - thread_safe_decrement32(&thread_running); -} - extern void set_server_version(char *buf, size_t size); #define current_thd _current_thd() diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 7b988fd369b..4c3771bc710 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -35,7 +35,7 @@ embedded library */ -#include <my_global.h> +#include "mariadb.h" #include <mysql.h> #include <mysql_com.h> #include <mysqld_error.h> @@ -45,12 +45,7 @@ #include <violite.h> #include <signal.h> #include "probes_mysql.h" - -#ifdef EMBEDDED_LIBRARY -#undef MYSQL_SERVER -#undef MYSQL_CLIENT -#define MYSQL_CLIENT -#endif /*EMBEDDED_LIBRARY */ +#include "proxy_protocol.h" /* to reduce the number of ifdef's in the code @@ -64,8 +59,11 @@ static void inline EXTRA_DEBUG_fprintf(...) {} #ifndef MYSQL_SERVER static int inline EXTRA_DEBUG_fflush(...) { return 0; } #endif -#endif +#endif /* EXTRA_DEBUG */ + #ifdef MYSQL_SERVER +#include <sql_class.h> +#include <sql_connect.h> #define MYSQL_SERVER_my_error my_error #else static void inline MYSQL_SERVER_my_error(...) {} @@ -110,7 +108,7 @@ extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; #ifdef HAVE_QUERY_CACHE #define USE_QUERY_CACHE -extern void query_cache_insert(void *thd, const char *packet, ulong length, +extern void query_cache_insert(void *thd, const char *packet, size_t length, unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A @@ -122,9 +120,8 @@ extern my_bool thd_net_is_killed(); #define thd_net_is_killed() 0 #endif -#define TEST_BLOCKING 8 -static my_bool net_write_buff(NET *, const uchar *, ulong); +static my_bool net_write_buff(NET *, const uchar *, size_t len); my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags); @@ -549,13 +546,13 @@ net_write_command(NET *net,uchar command, */ static my_bool -net_write_buff(NET *net, const uchar *packet, ulong len) +net_write_buff(NET *net, const uchar *packet, size_t len) { - ulong left_length; + size_t left_length; if (net->compress && net->max_packet > MAX_PACKET_LENGTH) - left_length= (ulong) (MAX_PACKET_LENGTH - (net->write_pos - net->buff)); + left_length= (MAX_PACKET_LENGTH - (net->write_pos - net->buff)); else - left_length= (ulong) (net->buff_end - net->write_pos); + left_length= (net->buff_end - net->write_pos); #ifdef DEBUG_DATA_PACKETS DBUG_DUMP("data_written", packet, len); @@ -833,6 +830,70 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, /** + Try to parse and process proxy protocol header. + + This function is called in case MySQL packet header cannot be parsed. + It checks if proxy header was sent, and that it was send from allowed remote + host, as defined by proxy-protocol-networks parameter. + + If proxy header is parsed, then THD and ACL structures and changed to indicate + the new peer address and port. + + Note, that proxy header can only be sent either when the connection is established, + or as the client reply packet to +*/ +#undef IGNORE /* for Windows */ +typedef enum { RETRY, ABORT, IGNORE} handle_proxy_header_result; +static handle_proxy_header_result handle_proxy_header(NET *net) +{ +#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) + return IGNORE; +#else + THD *thd= (THD *)net->thd; + + if (!has_proxy_protocol_header(net) || !thd || + thd->get_command() != COM_CONNECT) + return IGNORE; + + /* + Proxy information found in the first 4 bytes received so far. + Read and parse proxy header , change peer ip address and port in THD. + */ + proxy_peer_info peer_info; + + if (!thd->net.vio) + { + DBUG_ASSERT(0); + return ABORT; + } + + if (!is_proxy_protocol_allowed((sockaddr *)&(thd->net.vio->remote))) + { + /* proxy-protocol-networks variable needs to be set to allow this remote address */ + my_printf_error(ER_HOST_NOT_PRIVILEGED, "Proxy header is not accepted from %s", + MYF(0), thd->main_security_ctx.ip); + return ABORT; + } + + if (parse_proxy_protocol_header(net, &peer_info)) + { + /* Failed to parse proxy header*/ + my_printf_error(ER_UNKNOWN_ERROR, "Failed to parse proxy header", MYF(0)); + return ABORT; + } + + if (peer_info.is_local_command) + /* proxy header indicates LOCAL connection, no action necessary */ + return RETRY; + /* Change peer address in THD and ACL structures.*/ + uint host_errors; + return (handle_proxy_header_result)thd_set_peer_addr(thd, + &(peer_info.peer_addr), NULL, peer_info.port, + false, &host_errors); +#endif +} + +/** Reads one packet to net->buff + net->where_b. Long packets are handled by my_net_read(). This function reallocates the net->buff buffer if necessary. @@ -854,6 +915,9 @@ my_real_read(NET *net, size_t *complen, #ifndef NO_ALARM ALARM alarm_buff; #endif + +retry: + my_bool net_blocking=vio_is_blocking(net->vio); uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE); @@ -974,7 +1038,7 @@ my_real_read(NET *net, size_t *complen, #endif if (i == 0) { /* First parts is packet length */ - ulong helping; + size_t helping; #ifndef DEBUG_DATA_PACKETS DBUG_DUMP("packet_header", net->buff+net->where_b, NET_HEADER_SIZE); @@ -1085,6 +1149,17 @@ end: packets_out_of_order: { + switch (handle_proxy_header(net)) { + case ABORT: + /* error happened, message is already written. */ + len= packet_error; + goto end; + case RETRY: + goto retry; + case IGNORE: + break; + } + DBUG_PRINT("error", ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], @@ -1167,7 +1242,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) size_t total_length= 0; do { - net->where_b += len; + net->where_b += (ulong)len; total_length += len; len = my_real_read(net,&complen, 0); } while (len == MAX_PACKET_LENGTH); @@ -1175,14 +1250,15 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) len+= total_length; net->where_b = save_pos; } + net->read_pos = net->buff + net->where_b; if (len != packet_error) { net->read_pos[len]=0; /* Safeguard for mysql_use_result */ - *reallen = len; + *reallen = (ulong)len; } MYSQL_NET_READ_DONE(0, len); - return len; + return (ulong)len; #ifdef HAVE_COMPRESS } else @@ -1280,7 +1356,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) MYSQL_NET_READ_DONE(1, 0); return packet_error; } - buf_length+= complen; + buf_length+= (ulong)complen; *reallen += packet_len; } @@ -1294,7 +1370,7 @@ my_net_read_packet_reallen(NET *net, my_bool read_from_server, ulong* reallen) } #endif /* HAVE_COMPRESS */ MYSQL_NET_READ_DONE(0, len); - return len; + return (ulong)len; } diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index e05e43a0a59..654383c3320 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -64,19 +64,6 @@ NTService::~NTService() -------------------------------------------------------------------------- */ -BOOL NTService::GetOS() -{ - bOsNT = FALSE; - memset(&osVer, 0, sizeof(OSVERSIONINFO)); - osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (GetVersionEx(&osVer)) - { - if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT) - bOsNT = TRUE; - } - return bOsNT; -} - /** Registers the main service thread with the service manager. diff --git a/sql/nt_servc.h b/sql/nt_servc.h index 6781fe0ddfa..c8b64515b31 100644 --- a/sql/nt_servc.h +++ b/sql/nt_servc.h @@ -45,8 +45,6 @@ class NTService int nError; DWORD dwState; - BOOL GetOS(); // returns TRUE if WinNT - BOOL IsNT() { return bOsNT;} //init service entry point long Init(LPCSTR szInternName,void *ServiceThread); diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index 1dde5228263..b21cbb33c64 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "sql_select.h" #include "sql_test.h" diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e90dbb468f1..77bf64f991a 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -108,7 +108,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // is_key_used, key_copy, key_cmp, key_restore #include "sql_parse.h" // check_stack_overrun @@ -1236,7 +1236,8 @@ QUICK_SELECT_I::QUICK_SELECT_I() QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc, bool *create_error) - :free_file(0),cur_range(NULL),last_range(0),dont_free(0) + :thd(thd), no_alloc(no_alloc), parent_alloc(parent_alloc), + free_file(0),cur_range(NULL),last_range(0),dont_free(0) { my_bitmap_map *bitmap; DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT"); @@ -3461,6 +3462,13 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) free_root(&alloc,MYF(0)); // Return memory & allocator DBUG_RETURN(FALSE); } + + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_update_range_constants(thd)) + { + retval= TRUE; + goto end2; + } dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); @@ -3561,6 +3569,7 @@ all_used: mark_all_partitions_as_used(prune_param.part_info); end: dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); +end2: thd->no_errors=0; thd->mem_root= range_par->old_root; free_root(&alloc,MYF(0)); // Return memory & allocator @@ -3987,7 +3996,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) simply set res= -1 as if the mapper had returned that. TODO: What to do here is defined in WL#4065. */ - if (ppar->arg_stack[0]->part == 0) + if (ppar->arg_stack[0]->part == 0 || ppar->part_info->part_type == VERSIONING_PARTITION) { uint32 i; uint32 store_length_array[MAX_KEY]; @@ -6205,7 +6214,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, &key_ptr, 0); keypart_map= (keypart_map << 1) | 1; } - min_range.length= max_range.length= (size_t) (key_ptr - key_val); + min_range.length= max_range.length= (uint) (key_ptr - key_val); min_range.keypart_map= max_range.keypart_map= keypart_map; records= (info->param->table->file-> records_in_range(scan->keynr, &min_range, &max_range)); @@ -6281,7 +6290,7 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info, DBUG_ENTER("ror_intersect_add"); DBUG_PRINT("info", ("Current out_rows= %g", info->out_rows)); DBUG_PRINT("info", ("Adding scan on %s", - info->param->table->key_info[ror_scan->keynr].name)); + info->param->table->key_info[ror_scan->keynr].name.str)); DBUG_PRINT("info", ("is_cpk_scan: %d",is_cpk_scan)); selectivity_mult = ror_scan_selectivity(info, ror_scan); @@ -6664,7 +6673,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, total_cost += (*ror_scan_mark)->index_read_cost; records += (*ror_scan_mark)->records; DBUG_PRINT("info", ("Adding scan on %s", - param->table->key_info[(*ror_scan_mark)->keynr].name)); + param->table->key_info[(*ror_scan_mark)->keynr].name.str)); if (total_cost > read_time) DBUG_RETURN(NULL); /* F=F-covered by first(I) */ @@ -6826,7 +6835,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, read_plan->mrr_buf_size= best_buf_size; DBUG_PRINT("info", ("Returning range plan for key %s, cost %g, records %lu", - param->table->key_info[param->real_keynr[best_idx]].name, + param->table->key_info[param->real_keynr[best_idx]].name.str, read_plan->read_cost, (ulong) read_plan->records)); } } @@ -7346,6 +7355,7 @@ SEL_TREE *Item_func_in::get_func_row_mm_tree(RANGE_OPT_PARAM *param, key_col->bring_value(); key_col_info.comparator= row_cmp_item->get_comparator(i); + DBUG_ASSERT(key_col_info.comparator); key_col_info.comparator->store_value(key_col); col_comparators++; @@ -7956,7 +7966,7 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, } uint maybe_null= (uint) field->real_maybe_null(); - uint field_length= field->pack_length() + maybe_null; + size_t field_length= field->pack_length() + maybe_null; size_t offset= maybe_null; size_t length= key_part->store_length; @@ -10959,7 +10969,7 @@ int read_keys_and_merge_scans(THD *thd, if (unique == NULL) { - DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_ABORT(); ); + DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_SUICIDE(); ); DBUG_EXECUTE_IF("only_one_Unique_may_be_created", DBUG_SET("+d,index_merge_may_not_create_a_Unique"); ); @@ -11073,7 +11083,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() if (doing_pk_scan) DBUG_RETURN(pk_quick_select->get_next()); - if ((result= read_record.read_record(&read_record)) == -1) + if ((result= read_record.read_record()) == -1) { result= HA_ERR_END_OF_FILE; end_read_record(&read_record); @@ -11109,7 +11119,7 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next() int result; DBUG_ENTER("QUICK_INDEX_INTERSECT_SELECT::get_next"); - if ((result= read_record.read_record(&read_record)) == -1) + if ((result= read_record.read_record()) == -1) { result= HA_ERR_END_OF_FILE; end_read_record(&read_record); @@ -11864,7 +11874,7 @@ void QUICK_SELECT_I::add_key_name(String *str, bool *first) *first= FALSE; else str->append(','); - str->append(key_info->name); + str->append(&key_info->name); } @@ -12019,7 +12029,7 @@ void QUICK_SELECT_I::add_key_and_length(String *key_names, key_names->append(','); used_lengths->append(','); } - key_names->append(key_info->name); + key_names->append(&key_info->name); length= longlong10_to_str(max_used_key_length, buf, 10) - buf; used_lengths->append(buf, length); } @@ -14663,7 +14673,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, uint keynr= param->real_keynr[idx]; if (tmp.length()) tmp.append(','); - tmp.append(param->table->key_info[keynr].name); + tmp.append(¶m->table->key_info[keynr].name); } } if (!tmp.length()) @@ -14689,7 +14699,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg, { if (tmp.length()) tmp.append(','); - tmp.append(table->key_info[(*start)->keynr].name); + tmp.append(&table->key_info[(*start)->keynr].name); } if (!tmp.length()) tmp.append(STRING_WITH_LEN("(empty)")); @@ -14771,7 +14781,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) { /* purecov: begin inspected */ fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n", - indent, "", head->key_info[index].name, max_used_key_length); + indent, "", head->key_info[index].name.str, max_used_key_length); if (verbose) { @@ -14875,7 +14885,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) { fprintf(DBUG_FILE, "%*squick_group_min_max_select: index %s (%d), length: %d\n", - indent, "", index_info->name, index, max_used_key_length); + indent, "", index_info->name.str, index, max_used_key_length); if (key_infix_len > 0) { fprintf(DBUG_FILE, "%*susing key_infix with length %d:\n", diff --git a/sql/opt_range.h b/sql/opt_range.h index 95e231433d2..bd85a12d4a1 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -1047,6 +1047,10 @@ bool quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range); class QUICK_RANGE_SELECT : public QUICK_SELECT_I { protected: + THD *thd; + bool no_alloc; + MEM_ROOT *parent_alloc; + /* true if we enabled key only reads */ handler *file; @@ -1085,6 +1089,9 @@ public: QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc, MEM_ROOT *parent_alloc, bool *create_err); ~QUICK_RANGE_SELECT(); + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { return new QUICK_RANGE_SELECT(thd, head, index, no_alloc, parent_alloc, + create_error); } void need_sorted_output(); int init(); @@ -1155,6 +1162,12 @@ public: :QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc, create_err) {}; + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { + DBUG_ASSERT(0); + return new QUICK_RANGE_SELECT_GEOM(thd, head, index, no_alloc, + parent_alloc, create_error); + } virtual int get_next(); }; @@ -1584,6 +1597,8 @@ class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT { public: QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts); + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { DBUG_ASSERT(0); return new QUICK_SELECT_DESC(this, used_key_parts); } int get_next(); bool reverse_sorted() { return 1; } int get_type() { return QS_TYPE_RANGE_DESC; } @@ -1651,6 +1666,38 @@ class SQL_SELECT :public Sql_alloc { }; +class SQL_SELECT_auto +{ + SQL_SELECT *select; +public: + SQL_SELECT_auto(): select(NULL) + {} + ~SQL_SELECT_auto() + { + delete select; + } + SQL_SELECT_auto& + operator= (SQL_SELECT *_select) + { + select= _select; + return *this; + } + operator SQL_SELECT * () const + { + return select; + } + SQL_SELECT * + operator-> () const + { + return select; + } + operator bool () const + { + return select; + } +}; + + class FT_SELECT: public QUICK_RANGE_SELECT { public: @@ -1658,6 +1705,8 @@ public: QUICK_RANGE_SELECT (thd, table, key, 1, NULL, create_err) { (void) init(); } ~FT_SELECT() { file->ft_end(); } + virtual QUICK_RANGE_SELECT *clone(bool *create_error) + { DBUG_ASSERT(0); return new FT_SELECT(thd, head, index, create_error); } int init() { return file->ft_init(); } int reset() { return 0; } int get_next() { return file->ha_ft_read(record); } diff --git a/sql/opt_split.cc b/sql/opt_split.cc new file mode 100644 index 00000000000..0cb76f03917 --- /dev/null +++ b/sql/opt_split.cc @@ -0,0 +1,1137 @@ +/* + Copyright (c) 2017 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 */ + +/* + This file contains functions to support the splitting technique. + This optimization technique can be applied to equi-joins involving + materialized tables such as materialized views, materialized derived tables + and materialized CTEs. The technique also could be applied to materialized + semi-joins though the code below does not support this usage yet. + + Here are the main ideas behind this technique that we'll call SM optimization + (SplitMaterialization). + + Consider the query + SELECT t1.a, t.min + FROM t1, (SELECT t2.a, MIN(t2.b) as min FROM t2 GROUP BY t2.a) t + WHERE t1.a = t.a and t1.b < const + + Re-write the query into + SELECT t1.a, t.min + FROM t1, LATERAL (SELECT t2.a, MIN(t2.b) as min + FROM t2 WHERE t2.a = t1.a GROUP BY t2.a) t + WHERE t1.b < const + + The execution of the original query (Q1) does the following: + 1. Executes the query in the specification of the derived table + and puts the result set into a temporary table with an index + on the first column. + 2. Joins t1 with the temporary table using the its index. + + The execution of the transformed query (Q1R) follows these steps: + 1. For each row of t1 where t1.b < const a temporary table + containing all rows of of t2 with t2.a = t1.a is created + 2. If there are any rows in the temporary table aggregation + is performed for them + 3. The result of the aggregation is joined with t1. + + The second execution can win if: + a) There is an efficient way to select rows of t2 for which t2.a = t1.a + (For example if there is an index on t2.a) + and + b) The number of temporary tables created for partitions + is much smaller that the total number of partitions + + It should be noted that for the transformed query aggregation + for a partition may be performed several times. + + As we can see the optimization basically splits table t2 into + partitions and performs aggregation over each of them + independently. + + If we have only one equi-join condition then we either push it as + for Q1R or we don't. In a general case we may have much more options. + Consider the query (Q3) + SELECT + FROM t1,t2 (SELECT t3.a, t3.b, MIN(t3.c) as min + FROM t3 GROUP BY a,b) t + WHERE t.a = t1.a AND t.b = t2.b + AND t1.c < c1 and t2.c < c2 + AND P(t1,t2); + (P(t1,t2) designates some additional conditions over columns of t1,t2). + + Assuming that there indexes on t3(a,b) and t3(b) here we have several + reasonable options to push equi-join conditions into the derived. + All these options should be taken into account when the optimizer + evaluates different join orders. When the join order (t1,t,t2) is + evaluated there is only one way of splitting : to push the condition + t.a = t1.a into t. With the join order (t2,t,t1) only the condition + t.b = t2.b can be pushed. When the join orders (t1,t2,t) and (t2,t1,t) + are evaluated then the optimizer should consider pushing t.a = t1.a, + t.b = t2.b and (t.a = t1.a AND t.b = t2.b) to choose the best condition + for splitting. Apparently here last condition is the best one because + it provides the miximum possible number of partitions. + + If we dropped the index on t3(a,b) and created the index on t3(a) instead + then we would have two options for splitting: to push t.a = t1.a or to + push t.b = t2.b. If the selectivity of the index t3(a) is better than + the selectivity of t3(b) then the first option is preferred. + + Although the condition (t.a = t1.a AND t.b = t2.b) provides a better + splitting than the condition t.a = t1.a the latter will be used for + splitting if the execution plan with the join order (t1,t,t2) turns out + to be the cheapest one. It's quite possible when the join condition + P(t1,t2) has a bad selectivity. + + Whenever the optimizer evaluates the cost of using a splitting it + compares it with the cost of materialization without splitting. + + If we just drop the index on t3(a,b) the chances that the splitting + will be used becomes much lower but they still exists providing that + the fanout of the partial join of t1 and t2 is small enough. +*/ + +/* + Splitting can be applied to a materialized table specified by the query + with post-join operations that require partitioning of the result set produced + by the join expression used in the FROM clause the query such as GROUP BY + operation and window function operation. In any of these cases the post-join + operation can be executed independently for any partition only over the rows + of this partition. Also if the set of all partitions is divided into disjoint + subsets the operation can applied to each subset independently. In this case + all rows are first partitioned into the groups each of which contains all the + rows from the partitions belonging the same subset and then each group + is subpartitioned into groups in the the post join operation. + + The set of all rows belonging to the union of several partitions is called + here superpartition. If a grouping operation is defined by the list + e_1,...,e_n then any set S = {e_i1,...,e_ik} can be used to devide all rows + into superpartions such that for any two rows r1, r2 the following holds: + e_ij(r1) = e_ij(r2) for each e_ij from S. We use the splitting technique + only if S consists of references to colums of the joined tables. + For example if the GROUP BY list looks like this a, g(b), c we can consider + applying the splitting technique to the superpartitions defined by {a,c}, + {a}, {c} (a and c here may be the references to the columns from different + tables). +*/ + + /* + The following describes when and how the optimizer decides whether it + makes sense to employ the splitting technique. + + 1. For each instance of a materialized table (derived/view/CTE) it is + checked that it is potentially splittable. Now it is done right after the + execution plan for the select specifying this table has been chosen. + + 2. Any potentially splittable materialized table T is subject to two-phase + optimization. It means that the optimizer first builds the best execution + plan for join that specifies T. Then the control is passed back to the + optimization process of the embedding select Q. After the execution plan + for Q has been chosen the optimizer finishes the optimization of the join + specifying T. + + 3. When the optimizer builds the container with the KEYUSE structures + for the join of embedding select it detects the equi-join conditions + PC that potentially could be pushed into a potentially splittable + materialized table T. The collected information about such conditions + is stored together with other facts on potential splittings for table T. + + 4. When the optimizer starts looking for the best execution plan for the + embedding select Q for each potentially splittable materialized table T + it creates special KEYUSE structures for pushable equi-join conditions + PC. These structures are used to add new elements to the container + of KEYUSE structures built for T. The specifics of these elements is + that they can be ebabled and disabled during the process of choosing + the best plan for Q. + + 5. When the optimizer extends a partial join order with a potentially + splittable materialized table T (in function best_access_path) it + first evaluates a new execution plan for the modified specification + of T that adds all equi-join conditions that can be pushed with + current join prefix to the WHERE conditions of the original + specification of T. If the cost of the new plan is better than the + the cost of the original materialized table then the optimizer + prefers to use splitting for the current join prefix. As the cost + of the plan depends only on the pushed conditions it makes sense + to cache this plan for other prefixes. + + 6. The optimizer takes into account the cost of splitting / materialization + of a potentially splittable materialized table T as a startup cost + to access table T. + + 7. When the optimizer finally chooses the best execution plan for + the embedding select Q and this plan prefers using splitting + for table T with pushed equi-join conditions PC then the execution + plan for the underlying join with these conditions is chosen for T. +*/ + +/* + The implementation of the splitting technique below allows to apply + the technique only to a materialized derived table / view / CTE whose + specification is either a select with GROUP BY or a non-grouping select + with window functions that share the same PARTITION BY list. +*/ + +#include "mariadb.h" +#include "sql_select.h" + +/* Info on a splitting field */ +struct SplM_field_info +{ + /* Splitting field in the materialized table T */ + Field *mat_field; + /* The item from the select list of the specification of T */ + Item *producing_item; + /* The corresponding splitting field from the specification of T */ + Field *underlying_field; +}; + + +/* Info on the splitting execution plan saved in SplM_opt_info::cache */ +struct SplM_plan_info +{ + /* The cached splitting execution plan P */ + struct st_position *best_positions; + /* The cost of the above plan */ + double cost; + /* Selectivity of splitting used in P */ + double split_sel; + /* For fast search of KEYUSE_EXT elements used for splitting in P */ + struct KEYUSE_EXT *keyuse_ext_start; + /* The tables that contains the fields used for splitting in P */ + TABLE *table; + /* The number of the key from 'table' used for splitting in P */ + uint key; + /* Number of the components of 'key' used for splitting in P */ + uint parts; +}; + + +/* + The structure contains the information that is used by the optimizer + for potentially splittable materialization of T that is a materialized + derived_table / view / CTE +*/ +class SplM_opt_info : public Sql_alloc +{ +public: + /* The join for the select specifying T */ + JOIN *join; + /* The map of tables from 'join' whose columns can be used for partitioning */ + table_map tables_usable_for_splitting; + /* Info about the fields of the joined tables usable for splitting */ + SplM_field_info *spl_fields; + /* The number of elements in the above list */ + uint spl_field_cnt; + /* Contains the structures to generate all KEYUSEs for pushable equalities */ + List<KEY_FIELD> added_key_fields; + /* The cache of evaluated execution plans for 'join' with pushed equalities */ + List<SplM_plan_info> plan_cache; + /* Cost of best execution plan for join when nothing is pushed */ + double unsplit_cost; + /* Cardinality of T when nothing is pushed */ + double unsplit_card; + /* Lastly evaluated execution plan for 'join' with pushed equalities */ + SplM_plan_info *last_plan; + + SplM_plan_info *find_plan(TABLE *table, uint key, uint parts); +}; + + +void TABLE::set_spl_opt_info(SplM_opt_info *spl_info) +{ + if (spl_info) + spl_info->join->spl_opt_info= spl_info; + spl_opt_info= spl_info; +} + + +void TABLE::deny_splitting() +{ + DBUG_ASSERT(spl_opt_info != NULL); + spl_opt_info->join->spl_opt_info= NULL; + spl_opt_info= NULL; +} + + +/* This structure is auxiliary and used only in the function that follows it */ +struct SplM_field_ext_info: public SplM_field_info +{ + uint item_no; + bool is_usable_for_ref_access; +}; + + +/** + @brief + Check whether this join is one for potentially splittable materialized table + + @details + The function checks whether this join is for select that specifies + a potentially splittable materialized table T. If so, the collected + info on potential splittability of T is attached to the field spl_opt_info + of the TABLE structure for T. + + The function returns a positive answer if the following holds: + 1. the optimizer switch 'split_materialized' is set 'on' + 2. the select owning this join specifies a materialized derived/view/cte T + 3. this is the only select in the specification of T + 4. condition pushdown is not prohibited into T + 5. T is not recursive + 6. not all of this join are constant or optimized away + 7. T is either + 7.1. a grouping table with GROUP BY list P + or + 7.2. a non-grouping table with window functions over the same non-empty + partition specified by the PARTITION BY list P + 8. P contains some references on the columns of the joined tables C + occurred also in the select list of this join + 9. There are defined some keys usable for ref access of fields from C + with available statistics. + + @retval + true if the answer is positive + false otherwise +*/ + +bool JOIN::check_for_splittable_materialized() +{ + ORDER *partition_list= 0; + st_select_lex_unit *unit= select_lex->master_unit(); + TABLE_LIST *derived= unit->derived; + if (!(optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)) || // !(1) + !(derived && derived->is_materialized_derived()) || // !(2) + (unit->first_select()->next_select()) || // !(3) + (derived->prohibit_cond_pushdown) || // !(4) + (derived->is_recursive_with_table()) || // !(5) + (table_count == 0 || const_tables == top_join_tab_count)) // !(6) + return false; + if (group_list) // (7.1) + { + if (!select_lex->have_window_funcs()) + partition_list= group_list; + } + else if (select_lex->have_window_funcs() && + select_lex->window_specs.elements == 1) // (7.2) + { + partition_list= + select_lex->window_specs.head()->partition_list->first; + } + if (!partition_list) + return false; + + ORDER *ord; + Dynamic_array<SplM_field_ext_info> candidates; + + /* + Select from partition_list all candidates for splitting. + A candidate must be + - field item or refer to such (8.1) + - item mentioned in the select list (8.2) + Put info about such candidates into the array candidates + */ + table_map usable_tables= 0; // tables that contains the candidate + for (ord= partition_list; ord; ord= ord->next) + { + Item *ord_item= *ord->item; + if (ord_item->real_item()->type() != Item::FIELD_ITEM) // !(8.1) + continue; + + Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; + + JOIN_TAB *tab= ord_field->table->reginfo.join_tab; + if (tab->is_inner_table_of_outer_join()) + continue; + + List_iterator<Item> li(fields_list); + Item *item; + uint item_no= 0; + while ((item= li++)) + { + if ((*ord->item)->eq(item, 0)) // (8.2) + { + SplM_field_ext_info new_elem; + new_elem.producing_item= item; + new_elem.item_no= item_no; + new_elem.mat_field= derived->table->field[item_no]; + new_elem.underlying_field= ord_field; + new_elem.is_usable_for_ref_access= false; + candidates.push(new_elem); + usable_tables|= ord_field->table->map; + break; + } + item_no++; + } + } + if (candidates.elements() == 0) // no candidates satisfying (8.1) && (8.2) + return false; + + /* + For each table from this join find the keys that can be used for ref access + of the fields mentioned in the 'array candidates' + */ + + SplM_field_ext_info *cand; + SplM_field_ext_info *cand_start= &candidates.at(0); + SplM_field_ext_info *cand_end= cand_start + candidates.elements(); + + for (JOIN_TAB *tab= join_tab; + tab < join_tab + top_join_tab_count; tab++) + { + TABLE *table= tab->table; + if (!(table->map & usable_tables)) + continue; + + table->keys_usable_for_splitting.clear_all(); + uint i; + for (i= 0; i < table->s->keys; i++) + { + if (!table->keys_in_use_for_query.is_set(i)) + continue; + KEY *key_info= table->key_info + i; + uint key_parts= table->actual_n_key_parts(key_info); + uint usable_kp_cnt= 0; + for ( ; usable_kp_cnt < key_parts; usable_kp_cnt++) + { + if (key_info->actual_rec_per_key(usable_kp_cnt) == 0) + break; + int fldnr= key_info->key_part[usable_kp_cnt].fieldnr; + + for (cand= cand_start; cand < cand_end; cand++) + { + if (cand->underlying_field->field_index + 1 == fldnr) + { + cand->is_usable_for_ref_access= true; + break; + } + } + if (cand == cand_end) + break; + } + if (usable_kp_cnt) + table->keys_usable_for_splitting.set_bit(i); + } + } + + /* Count the candidate fields that can be accessed by ref */ + uint spl_field_cnt= (uint)candidates.elements(); + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + spl_field_cnt--; + } + + if (!spl_field_cnt) // No candidate field can be accessed by ref => !(9) + return false; + + /* + Create a structure of the type SplM_opt_info and fill it with + the collected info on potential splittability of T + */ + SplM_opt_info *spl_opt_info= new (thd->mem_root) SplM_opt_info(); + SplM_field_info *spl_field= + (SplM_field_info *) (thd->calloc(sizeof(SplM_field_info) * + spl_field_cnt)); + + if (!(spl_opt_info && spl_field)) // consider T as not good for splitting + return false; + + spl_opt_info->join= this; + spl_opt_info->tables_usable_for_splitting= 0; + spl_opt_info->spl_field_cnt= spl_field_cnt; + spl_opt_info->spl_fields= spl_field; + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + continue; + spl_field->producing_item= cand->producing_item; + spl_field->underlying_field= cand->underlying_field; + spl_field->mat_field= cand->mat_field; + spl_opt_info->tables_usable_for_splitting|= + cand->underlying_field->table->map; + spl_field++; + } + + /* Attach this info to the table T */ + derived->table->set_spl_opt_info(spl_opt_info); + + return true; +} + + +/** + @brief + Collect info on KEY_FIELD usable for splitting + + @param + key_field KEY_FIELD to collect info on + + @details + The function assumes that this table is potentially splittable. + The function checks whether the KEY_FIELD structure key_field built for + this table was created for a splitting field f. If so, the function does + the following using info from key_field: + 1. Builds an equality of the form f = key_field->val that could be + pushed into this table. + 2. Creates a new KEY_FIELD structure for this equality and stores + a reference to this structure in this->spl_opt_info. +*/ + +void TABLE::add_splitting_info_for_key_field(KEY_FIELD *key_field) +{ + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + Field *field= key_field->field; + SplM_field_info *spl_field= spl_opt_info->spl_fields; + uint i= spl_opt_info->spl_field_cnt; + for ( ; i; i--, spl_field++) + { + if (spl_field->mat_field == field) + break; + } + if (!i) // field is not usable for splitting + return; + + /* + Any equality condition that can be potentially pushed into the + materialized derived table is constructed now though later it may turn out + that it is not needed, because it is not used for splitting. + The reason for this is that the failure to construct it when it has to be + injected causes denial for further processing of the query. + Formally this equality is needed in the KEY_FIELD structure constructed + here that will be used to generate additional keyuses usable for splitting. + However key_field.cond could be used for this purpose (see implementations + of virtual function can_optimize_keypart_ref()). + + The condition is built in such a form that it can be added to the WHERE + condition of the select that specifies this table. + */ + THD *thd= in_use; + Item *left_item= spl_field->producing_item->build_clone(thd); + Item *right_item= key_field->val->build_clone(thd); + Item_func_eq *eq_item= 0; + if (left_item && right_item) + { + right_item->walk(&Item::set_fields_as_dependent_processor, + false, join->select_lex); + right_item->update_used_tables(); + eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); + } + if (!eq_item) + return; + KEY_FIELD *added_key_field= + (KEY_FIELD *) thd->alloc(sizeof(KEY_FIELD)); + if (!added_key_field || + spl_opt_info->added_key_fields.push_back(added_key_field,thd->mem_root)) + return; + added_key_field->field= spl_field->underlying_field; + added_key_field->cond= eq_item; + added_key_field->val= key_field->val; + added_key_field->level= 0; + added_key_field->optimize= KEY_OPTIMIZE_EQ; + added_key_field->eq_func= true; + added_key_field->null_rejecting= true; + added_key_field->cond_guard= NULL; + added_key_field->sj_pred_no= UINT_MAX; + return; +} + + +static bool +add_ext_keyuse_for_splitting(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field, uint key, uint part) +{ + KEYUSE_EXT keyuse_ext; + Field *field= added_key_field->field; + + JOIN_TAB *tab=field->table->reginfo.join_tab; + key_map possible_keys=field->get_possible_keys(); + possible_keys.intersect(field->table->keys_usable_for_splitting); + tab->keys.merge(possible_keys); + + Item_func_eq *eq_item= (Item_func_eq *) (added_key_field->cond); + keyuse_ext.table= field->table; + keyuse_ext.val= eq_item->arguments()[1]; + keyuse_ext.key= key; + keyuse_ext.keypart=part; + keyuse_ext.keypart_map= (key_part_map) 1 << part; + keyuse_ext.used_tables= keyuse_ext.val->used_tables(); + keyuse_ext.optimize= added_key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; + keyuse_ext.ref_table_rows= 0; + keyuse_ext.null_rejecting= added_key_field->null_rejecting; + keyuse_ext.cond_guard= added_key_field->cond_guard; + keyuse_ext.sj_pred_no= added_key_field->sj_pred_no; + keyuse_ext.validity_ref= 0; + keyuse_ext.needed_in_prefix= added_key_field->val->used_tables(); + keyuse_ext.validity_var= false; + return ext_keyuses->push(keyuse_ext); +} + + +static int +sort_ext_keyuse(KEYUSE_EXT *a, KEYUSE_EXT *b) +{ + if (a->table->tablenr != b->table->tablenr) + return (int) (a->table->tablenr - b->table->tablenr); + if (a->key != b->key) + return (int) (a->key - b->key); + return (int) (a->keypart - b->keypart); +} + + +static void +sort_ext_keyuses(Dynamic_array<KEYUSE_EXT> *keyuses) +{ + KEYUSE_EXT *first_keyuse= &keyuses->at(0); + my_qsort(first_keyuse, keyuses->elements(), sizeof(KEYUSE_EXT), + (qsort_cmp) sort_ext_keyuse); +} + + +/** + @brief + Add info on keyuses usable for splitting into an array +*/ + +static bool +add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field) +{ + Field *field= added_key_field->field; + TABLE *table= field->table; + for (uint key= 0; key < table->s->keys; key++) + { + if (!(table->keys_usable_for_splitting.is_set(key))) + continue; + KEY *key_info= table->key_info + key; + uint key_parts= table->actual_n_key_parts(key_info); + KEY_PART_INFO *key_part_info= key_info->key_part; + for (uint part=0; part < key_parts; part++, key_part_info++) + { + if (!field->eq(key_part_info->field)) + continue; + if (add_ext_keyuse_for_splitting(ext_keyuses, added_key_field, key, part)) + return true; + } + } + return false; +} + + +/* + @brief + Cost of the post join operation used in specification of splittable table +*/ + +static +double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len) +{ + double cost; + cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to fill tmp table + cost+= get_tmp_table_lookup_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to perform post join operation used here + cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) + + (join_record_count == 0 ? 0 : + join_record_count * log2 (join_record_count)) * + SORT_INDEX_CMP_COST; // cost to perform sorting + return cost; +} + +/** + @brief + Add KEYUSE structures that can be usable for splitting + + @details + This function is called only for joins created for potentially + splittable materialized tables. The function does the following: + 1. Creates the dynamic array ext_keyuses_for_splitting of KEYUSE_EXT + structures and fills is with info about all keyuses that + could be used for splitting. + 2. Sort the array ext_keyuses_for_splitting for fast access by key + on certain columns. + 3. Collects and stores cost and cardinality info on the best execution + plan that does not use splitting and save this plan together with + corresponding array of keyuses. + 4. Expand this array with KEYUSE elements built from the info stored + in ext_keyuses_for_splitting that could be produced by pushed + equalities employed for splitting. + 5. Prepare the extended array of keyuses to be used in the function + best_access_plan() +*/ + +void JOIN::add_keyuses_for_splitting() +{ + uint i; + uint idx; + KEYUSE_EXT *keyuse_ext; + KEYUSE_EXT keyuse_ext_end; + double oper_cost; + uint rec_len; + uint added_keyuse_count; + TABLE *table= select_lex->master_unit()->derived->table; + List_iterator_fast<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + if (!spl_opt_info->added_key_fields.elements) + goto err; + if (!(ext_keyuses_for_splitting= new Dynamic_array<KEYUSE_EXT>)) + goto err; + while ((added_key_field= li++)) + { + (void) add_ext_keyuses_for_splitting_field(ext_keyuses_for_splitting, + added_key_field); + } + added_keyuse_count= (uint)ext_keyuses_for_splitting->elements(); + if (!added_keyuse_count) + goto err; + sort_ext_keyuses(ext_keyuses_for_splitting); + bzero((char*) &keyuse_ext_end, sizeof(keyuse_ext_end)); + if (ext_keyuses_for_splitting->push(keyuse_ext_end)) + goto err; + + spl_opt_info->unsplit_card= join_record_count; + + rec_len= table->s->rec_buff_length; + + oper_cost= spl_postjoin_oper_cost(thd, join_record_count, rec_len); + + spl_opt_info->unsplit_cost= best_positions[table_count-1].read_time + + oper_cost; + + if (!(save_qep= new Join_plan_state(table_count + 1))) + goto err; + + save_query_plan(save_qep); + + if (!keyuse.buffer && + my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64, + MYF(MY_THREAD_SPECIFIC))) + goto err; + + if (allocate_dynamic(&keyuse, + save_qep->keyuse.elements + + added_keyuse_count)) + goto err; + + memcpy(keyuse.buffer, + save_qep->keyuse.buffer, + (size_t) save_qep->keyuse.elements * keyuse.size_of_element); + keyuse.elements= save_qep->keyuse.elements; + + keyuse_ext= &ext_keyuses_for_splitting->at(0); + idx= save_qep->keyuse.elements; + for (i=0; i < added_keyuse_count; i++, keyuse_ext++, idx++) + { + set_dynamic(&keyuse, (KEYUSE *) keyuse_ext, idx); + KEYUSE *added_keyuse= ((KEYUSE *) (keyuse.buffer)) + idx; + added_keyuse->validity_ref= &keyuse_ext->validity_var; + } + + if (sort_and_filter_keyuse(thd, &keyuse, true)) + goto err; + optimize_keyuse(this, &keyuse); + + for (uint i= 0; i < table_count; i++) + { + JOIN_TAB *tab= join_tab + i; + map2table[tab->table->tablenr]= tab; + } + + return; + +err: + if (save_qep) + restore_query_plan(save_qep); + table->deny_splitting(); + return; +} + + +/** + @brief + Add KEYUSE structures that can be usable for splitting of this joined table +*/ + +void JOIN_TAB::add_keyuses_for_splitting() +{ + DBUG_ASSERT(table->spl_opt_info != NULL); + SplM_opt_info *spl_opt_info= table->spl_opt_info; + spl_opt_info->join->add_keyuses_for_splitting(); +} + + +/* + @brief + Find info on the splitting plan by the splitting key +*/ + +SplM_plan_info *SplM_opt_info::find_plan(TABLE *table, uint key, uint parts) +{ + List_iterator_fast<SplM_plan_info> li(plan_cache); + SplM_plan_info *spl_plan; + while ((spl_plan= li++)) + { + if (spl_plan->table == table && + spl_plan->key == key && + spl_plan->parts == parts) + break; + } + return spl_plan; +} + + +/* + @breaf + Enable/Disable a keyuses that can be used for splitting + */ + +static +void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start, + TABLE *table, uint key, + table_map remaining_tables, + bool validity_val) +{ + KEYUSE_EXT *keyuse_ext= key_keyuse_ext_start; + do + { + if (!(keyuse_ext->needed_in_prefix & remaining_tables)) + { + /* + The enabling/disabling flags are set just in KEYUSE_EXT structures. + Yet keyuses that are used by best_access_path() have pointers + to these flags. + */ + keyuse_ext->validity_var= validity_val; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); +} + + +/** + @brief + Choose the best splitting to extend the evaluated partial join + + @param + record_count estimated cardinality of the extended partial join + remaining_tables tables not joined yet + + @details + This function is called during the search for the best execution + plan of the join that contains this table T. The function is called + every time when the optimizer tries to extend a partial join by + joining it with table T. Depending on what tables are already in the + partial join different equalities usable for splitting can be pushed + into T. The function evaluates different variants and chooses the + best one. Then the function finds the plan for the materializing join + with the chosen equality conditions pushed into it. If the cost of the + plan turns out to be less than the cost of the best plan without + splitting the function set it as the true plan of materialization + of the table T. + The function caches the found plans for materialization of table T + together if the info what key was used for splitting. Next time when + the optimizer prefers to use the same key the plan is taken from + the cache of plans + + @retval + Pointer to the info on the found plan that employs the pushed equalities + if the plan has been chosen, NULL - otherwise. +*/ + +SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + THD *thd= join->thd; + table_map tables_usable_for_splitting= + spl_opt_info->tables_usable_for_splitting; + KEYUSE_EXT *keyuse_ext= &join->ext_keyuses_for_splitting->at(0); + KEYUSE_EXT *best_key_keyuse_ext_start; + TABLE *best_table= 0; + double best_rec_per_key= DBL_MAX; + SplM_plan_info *spl_plan= 0; + uint best_key; + uint best_key_parts; + + /* + Check whether there are keys that can be used to join T employing splitting + and if so, select the best out of such keys + */ + for (uint tablenr= 0; tablenr < join->table_count; tablenr++) + { + if (!((1ULL << tablenr) & tables_usable_for_splitting)) + continue; + JOIN_TAB *tab= join->map2table[tablenr]; + TABLE *table= tab->table; + do + { + uint key= keyuse_ext->key; + KEYUSE_EXT *key_keyuse_ext_start= keyuse_ext; + key_part_map found_parts= 0; + do + { + if (keyuse_ext->needed_in_prefix & remaining_tables) + { + keyuse_ext++; + continue; + } + if (!(keyuse_ext->keypart_map & found_parts)) + { + if ((!found_parts && !keyuse_ext->keypart) || + (found_parts && ((keyuse_ext->keypart_map >> 1) & found_parts))) + found_parts|= keyuse_ext->keypart_map; + else + { + do + { + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + break; + } + } + KEY *key_info= table->key_info + key; + double rec_per_key= + key_info->actual_rec_per_key(keyuse_ext->keypart); + if (rec_per_key < best_rec_per_key) + { + best_table= keyuse_ext->table; + best_key= keyuse_ext->key; + best_key_parts= keyuse_ext->keypart + 1; + best_rec_per_key= rec_per_key; + best_key_keyuse_ext_start= key_keyuse_ext_start; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + } + while (keyuse_ext->table == table); + } + spl_opt_info->last_plan= 0; + if (best_table) + { + /* + The key for splitting was chosen, look for the plan for this key + in the cache + */ + spl_plan= spl_opt_info->find_plan(best_table, best_key, best_key_parts); + if (!spl_plan && + (spl_plan= (SplM_plan_info *) thd->alloc(sizeof(SplM_plan_info))) && + (spl_plan->best_positions= + (POSITION *) thd->alloc(sizeof(POSITION) * join->table_count)) && + !spl_opt_info->plan_cache.push_back(spl_plan)) + { + /* + The plan for the chosen key has not been found in the cache. + Build a new plan and save info on it in the cache + */ + table_map all_table_map= (1 << join->table_count) - 1; + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, true); + choose_plan(join, all_table_map & ~join->const_table_map); + spl_plan->keyuse_ext_start= best_key_keyuse_ext_start; + spl_plan->table= best_table; + spl_plan->key= best_key; + spl_plan->parts= best_key_parts; + spl_plan->split_sel= best_rec_per_key / + (spl_opt_info->unsplit_card ? + spl_opt_info->unsplit_card : 1); + + uint rec_len= table->s->rec_buff_length; + + double split_card= spl_opt_info->unsplit_card * spl_plan->split_sel; + double oper_cost= split_card * + spl_postjoin_oper_cost(thd, split_card, rec_len); + spl_plan->cost= join->best_positions[join->table_count-1].read_time + + + oper_cost; + + memcpy((char *) spl_plan->best_positions, + (char *) join->best_positions, + sizeof(POSITION) * join->table_count); + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, false); + } + if (spl_plan) + { + if(record_count * spl_plan->cost < spl_opt_info->unsplit_cost) + { + /* + The best plan that employs splitting is cheaper than + the plan without splitting + */ + spl_opt_info->last_plan= spl_plan; + } + } + } + + /* Set the cost of the preferred materialization for this partial join */ + records= (ha_rows)spl_opt_info->unsplit_card; + spl_plan= spl_opt_info->last_plan; + if (spl_plan) + { + startup_cost= record_count * spl_plan->cost; + records= (ha_rows) (records * spl_plan->split_sel); + } + else + startup_cost= spl_opt_info->unsplit_cost; + return spl_plan; +} + + +/** + @brief + Inject equalities for splitting used by the materialization join + + @param + remaining_tables used to filter out the equalities that cannot + be pushed. + + @details + This function is called by JOIN_TAB::fix_splitting that is used + to fix the chosen splitting of a splittable materialized table T + in the final query execution plan. In this plan the table T + is joined just before the 'remaining_tables'. So all equalities + usable for splitting whose right parts do not depend on any of + remaining tables can be pushed into join for T. + The function also marks the select that specifies T as + UNCACHEABLE_DEPENDENT_INJECTED. + + @retval + false on success + true on failure +*/ + +bool JOIN::inject_best_splitting_cond(table_map remaining_tables) +{ + Item *inj_cond= 0; + List<Item> inj_cond_list; + List_iterator<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + while ((added_key_field= li++)) + { + if (remaining_tables & added_key_field->val->used_tables()) + continue; + if (inj_cond_list.push_back(added_key_field->cond, thd->mem_root)) + return true; + } + DBUG_ASSERT(inj_cond_list.elements); + switch (inj_cond_list.elements) { + case 1: + inj_cond= inj_cond_list.head(); break; + default: + inj_cond= new (thd->mem_root) Item_cond_and(thd, inj_cond_list); + if (!inj_cond) + return true; + } + if (inj_cond) + inj_cond->fix_fields(thd,0); + + if (inject_cond_into_where(inj_cond)) + return true; + + select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + st_select_lex_unit *unit= select_lex->master_unit(); + unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + + return false; +} + + +/** + @brief + Fix the splitting chosen for a splittable table in the final query plan + + @param + spl_plan info on the splitting plan chosen for the splittable table T + remaining_tables the table T is joined just before these tables + + @details + If in the final query plan the optimizer has chosen a splitting plan + then the function sets this plan as the final execution plan to + materialized the table T. Otherwise the plan that does not use + splitting is set for the materialization. + + @retval + false on success + true on failure +*/ + +bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(table->spl_opt_info != 0); + JOIN *md_join= spl_opt_info->join; + if (spl_plan) + { + memcpy((char *) md_join->best_positions, + (char *) spl_plan->best_positions, + sizeof(POSITION) * md_join->table_count); + if (md_join->inject_best_splitting_cond(remaining_tables)) + return true; + /* + This is called for a proper work of JOIN::get_best_combination() + called for the join that materializes T + */ + reset_validity_vars_for_keyuses(spl_plan->keyuse_ext_start, + spl_plan->table, + spl_plan->key, + remaining_tables, + true); + } + else + { + md_join->restore_query_plan(md_join->save_qep); + } + return false; +} + + +/** + @brief + Fix the splittings chosen splittable tables in the final query plan + + @details + The function calls JOIN_TAB::fix_splittins for all potentially + splittable tables in this join to set all final materialization + plans chosen for these tables. + + @retval + false on success + true on failure +*/ + +bool JOIN::fix_all_splittings_in_plan() +{ + table_map prev_tables= 0; + table_map all_tables= (1 << table_count) - 1; + for (uint tablenr= 0; tablenr < table_count; tablenr++) + { + POSITION *cur_pos= &best_positions[tablenr]; + JOIN_TAB *tab= cur_pos->table; + if (tablenr >= const_tables && tab->table->is_splittable()) + { + SplM_plan_info *spl_plan= cur_pos->spl_plan; + if (tab->fix_splitting(spl_plan, all_tables & ~prev_tables)) + return true; + } + prev_tables|= tab->table->map; + } + return false; +} diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 3b98b650f8f..eff28d0c27d 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "sql_select.h" #include "filesort.h" @@ -432,9 +432,9 @@ referred to, we can only generate equalities that refer to the outer (or inner) tables. Note that this will disallow handling of cases like (CASE-FOR-SUBST). Currently, solution #2 is implemented. - */ +LEX_CSTRING weedout_key= {STRING_WITH_LEN("weedout_key")}; static bool subquery_types_allow_materialization(Item_in_subselect *in_subs); @@ -446,10 +446,6 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, Item_in_subselect *subq_pred, bool *remove); static TABLE_LIST *alloc_join_nest(THD *thd); static uint get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements); -static double get_tmp_table_lookup_cost(THD *thd, double row_count, - uint row_size); -static double get_tmp_table_write_cost(THD *thd, double row_count, - uint row_size); bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables); static SJ_MATERIALIZATION_INFO * at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, @@ -461,7 +457,7 @@ void best_access_path(JOIN *join, JOIN_TAB *s, static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm, Item_in_subselect *subq_pred); -static void remove_sj_conds(THD *thd, Item **tree); +static bool remove_sj_conds(THD *thd, Item **tree); static bool is_cond_sj_in_equality(Item *item); static bool sj_table_is_included(JOIN *join, JOIN_TAB *join_tab); static Item *remove_additional_cond(Item* conds); @@ -1065,6 +1061,8 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) DBUG_RETURN(1); if (subq_sel->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); + if (subq_sel->join->transform_in_predicates_into_in_subq(thd)) + DBUG_RETURN(TRUE); subq_sel->update_used_tables(); } @@ -1499,6 +1497,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) { TABLE_LIST *outer_tbl= subq_pred->emb_on_expr_nest; TABLE_LIST *wrap_nest; + LEX_CSTRING sj_wrap_name= { STRING_WITH_LEN("(sj-wrap)") }; /* We're dealing with @@ -1524,7 +1523,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) } wrap_nest->embedding= outer_tbl->embedding; wrap_nest->join_list= outer_tbl->join_list; - wrap_nest->alias= (char*) "(sj-wrap)"; + wrap_nest->alias= sj_wrap_name; wrap_nest->nested_join->join_list.empty(); wrap_nest->nested_join->join_list.push_back(outer_tbl, thd->mem_root); @@ -1563,6 +1562,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) TABLE_LIST *sj_nest; NESTED_JOIN *nested_join; + LEX_CSTRING sj_nest_name= { STRING_WITH_LEN("(sj-nest)") }; if (!(sj_nest= alloc_join_nest(thd))) { DBUG_RETURN(TRUE); @@ -1571,7 +1571,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) sj_nest->join_list= emb_join_list; sj_nest->embedding= emb_tbl_nest; - sj_nest->alias= (char*) "(sj-nest)"; + sj_nest->alias= sj_nest_name; sj_nest->sj_subq_pred= subq_pred; sj_nest->original_subq_pred_used_tables= subq_pred->used_tables() | subq_pred->left_expr->used_tables(); @@ -1748,9 +1748,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) */ Item_row *row= new (thd->mem_root) Item_row(thd, subq_lex->pre_fix); /* fix fields on subquery was call so they should be the same */ - DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols()); if (!row) DBUG_RETURN(TRUE); + DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols()); nested_join->sj_outer_expr_list.push_back(&subq_pred->left_expr); Item_func_eq *item_eq= new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig, row); @@ -1837,19 +1837,22 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) } parent_lex->have_merged_subqueries= TRUE; - DBUG_RETURN(FALSE); + /* Fatal error may have been set to by fix_after_pullout() */ + DBUG_RETURN(thd->is_fatal_error); } const int SUBQERY_TEMPTABLE_NAME_MAX_LEN= 20; -static void create_subquery_temptable_name(char *to, uint number) +static void create_subquery_temptable_name(LEX_STRING *str, uint number) { + char *to= str->str; DBUG_ASSERT(number < 10000); to= strmov(to, "<subquery"); to= int10_to_str((int) number, to, 10); to[0]= '>'; to[1]= 0; + str->length= (size_t) (to - str->str)+1; } @@ -1877,7 +1880,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, TABLE_LIST *tl; bool optimization_delayed= TRUE; TABLE_LIST *jtbm; - char *tbl_alias; + LEX_STRING tbl_alias; + THD *thd= parent_join->thd; DBUG_ENTER("convert_subq_to_jtbm"); subq_pred->set_strategy(SUBS_MATERIALIZATION); @@ -1885,8 +1889,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, *remove_item= TRUE; - if (!(tbl_alias= (char*)parent_join->thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) || - !(jtbm= alloc_join_nest(parent_join->thd))) //todo: this is not a join nest! + if (!(tbl_alias.str= (char*)thd->calloc(SUBQERY_TEMPTABLE_NAME_MAX_LEN)) || + !(jtbm= alloc_join_nest(thd))) //todo: this is not a join nest! { DBUG_RETURN(TRUE); } @@ -1898,13 +1902,13 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, /* Nests do not participate in those 'chains', so: */ /* jtbm->next_leaf= jtbm->next_local= jtbm->next_global == NULL*/ - emb_join_list->push_back(jtbm, parent_join->thd->mem_root); + emb_join_list->push_back(jtbm, thd->mem_root); /* Inject the jtbm table into TABLE_LIST::next_leaf list, so that make_join_statistics() and co. can find it. */ - parent_lex->leaf_tables.push_back(jtbm, parent_join->thd->mem_root); + parent_lex->leaf_tables.push_back(jtbm, thd->mem_root); if (subq_pred->unit->first_select()->options & OPTION_SCHEMA_TABLE) parent_lex->options |= OPTION_SCHEMA_TABLE; @@ -1925,11 +1929,12 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, jtbm->jtbm_table_no= parent_join->table_count; - create_subquery_temptable_name(tbl_alias, + create_subquery_temptable_name(&tbl_alias, subq_pred->unit->first_select()->select_number); - jtbm->alias= tbl_alias; + jtbm->alias.str= tbl_alias.str; + jtbm->alias.length= tbl_alias.length; parent_join->table_count++; - DBUG_RETURN(FALSE); + DBUG_RETURN(thd->is_fatal_error); } subselect_hash_sj_engine *hash_sj_engine= ((subselect_hash_sj_engine*)subq_pred->engine); @@ -1947,32 +1952,16 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, DBUG_EXECUTE("where", print_where(conds,"SJ-EXPR", QT_ORDINARY);); - create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join-> + create_subquery_temptable_name(&tbl_alias, hash_sj_engine->materialize_join-> select_lex->select_number); - jtbm->alias= tbl_alias; + jtbm->alias.str= tbl_alias.str; + jtbm->alias.length= tbl_alias.length; parent_lex->have_merged_subqueries= TRUE; -#if 0 - /* Inject sj_on_expr into the parent's WHERE or ON */ - if (emb_tbl_nest) - { - DBUG_ASSERT(0); - /*emb_tbl_nest->on_expr= and_items(emb_tbl_nest->on_expr, - sj_nest->sj_on_expr); - emb_tbl_nest->on_expr->fix_fields(parent_join->thd, &emb_tbl_nest->on_expr); - */ - } - else - { - /* Inject into the WHERE */ - parent_join->conds= and_items(parent_join->conds, conds); - parent_join->conds->fix_fields(parent_join->thd, &parent_join->conds); - parent_join->select_lex->where= parent_join->conds; - } -#endif + /* Don't unlink the child subselect, as the subquery will be used. */ - DBUG_RETURN(FALSE); + DBUG_RETURN(thd->is_fatal_error); } @@ -1987,6 +1976,9 @@ static TABLE_LIST *alloc_join_nest(THD *thd) return tbl; } +/* + @Note thd->is_fatal_error can be set in case of OOM +*/ void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist) { @@ -2478,7 +2470,7 @@ static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements) @return the cost of one lookup */ -static double +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) { if (row_count * row_size > thd->variables.max_heap_table_size) @@ -2498,7 +2490,7 @@ get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) @return the cost of writing one row */ -static double +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size) { double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size); @@ -3719,6 +3711,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) sjm->sjm_table_param.init(); sjm->sjm_table_param.bit_fields_as_long= TRUE; SELECT_LEX *subq_select= emb_sj_nest->sj_subq_pred->unit->first_select(); + const LEX_CSTRING sj_materialize_name= { STRING_WITH_LEN("sj-materialize") }; Ref_ptr_array p_items= subq_select->ref_pointer_array; for (uint i= 0; i < subq_select->item_list.elements; i++) sjm->sjm_table_cols.push_back(p_items[i], thd->mem_root); @@ -3732,7 +3725,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) 1, /*save_sum_fields*/ thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR /*rows_limit */, - (char*)"sj-materialize"))) + &sj_materialize_name))) DBUG_RETURN(TRUE); /* purecov: inspected */ sjm->table->map= emb_sj_nest->nested_join->used_tables; sjm->table->file->extra(HA_EXTRA_WRITE_CACHE); @@ -3748,6 +3741,11 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) DBUG_RETURN(FALSE); } +/** + @retval + FALSE ok + TRUE error +*/ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) { @@ -3760,8 +3758,6 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; uint i; - //List<Item> &item_list= emb_sj_nest->sj_subq_pred->unit->first_select()->item_list; - //List_iterator<Item> it(item_list); if (!sjm->is_sj_scan) { @@ -3811,6 +3807,8 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) null_count ? cur_ref_buff : 0, cur_key_part->length, tab_ref->items[i], FALSE); + if (!*ref_key) + DBUG_RETURN(TRUE); cur_ref_buff+= cur_key_part->store_length; } *ref_key= NULL; /* End marker. */ @@ -3836,9 +3834,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) */ for (i= 0; i < sjm->tables; i++) { - remove_sj_conds(thd, &tab[i].select_cond); - if (tab[i].select) - remove_sj_conds(thd, &tab[i].select->cond); + if (remove_sj_conds(thd, &tab[i].select_cond) || + (tab[i].select && remove_sj_conds(thd, &tab[i].select->cond))) + DBUG_RETURN(TRUE); } if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm, emb_sj_nest->sj_subq_pred))) @@ -3875,7 +3873,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) temptable record, we copy its columns to their corresponding columns in the record buffers for the source tables. */ - sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements]; + if (!(sjm->copy_field= new Copy_field[sjm->sjm_table_cols.elements])) + DBUG_RETURN(TRUE); + //it.rewind(); Ref_ptr_array p_items= emb_sj_nest->sj_subq_pred->unit->first_select()->ref_pointer_array; for (uint i=0; i < sjm->sjm_table_cols.elements; i++) @@ -3943,7 +3943,7 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) sjm_tab->read_record.copy_field= sjm->copy_field; sjm_tab->read_record.copy_field_end= sjm->copy_field + sjm->sjm_table_cols.elements; - sjm_tab->read_record.read_record= rr_sequential_and_unpack; + sjm_tab->read_record.read_record_func= rr_sequential_and_unpack; } sjm_tab->bush_children->end[-1].next_select= end_sj_materialize; @@ -4002,16 +4002,20 @@ static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm, } +/** + @retval + 0 ok + 1 error +*/ - -static void remove_sj_conds(THD *thd, Item **tree) +static bool remove_sj_conds(THD *thd, Item **tree) { if (*tree) { if (is_cond_sj_in_equality(*tree)) { *tree= NULL; - return; + return 0; } else if ((*tree)->type() == Item::COND_ITEM) { @@ -4020,12 +4024,19 @@ static void remove_sj_conds(THD *thd, Item **tree) while ((item= li++)) { if (is_cond_sj_in_equality(item)) - li.replace(new (thd->mem_root) Item_int(thd, 1)); + { + Item_int *tmp= new (thd->mem_root) Item_int(thd, 1); + if (!tmp) + return 1; + li.replace(tmp); + } } } } + return 0; } + /* Check if given Item was injected by semi-join equality */ static bool is_cond_sj_in_equality(Item *item) { @@ -4214,7 +4225,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) share->db_plugin= ha_lock_engine(0, heap_hton); table->file= get_new_handler(share, &table->mem_root, share->db_type()); - DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); + DBUG_ASSERT(!table->file || uniq_tuple_length_arg <= table->file->max_key_length()); } if (!table->file) goto err; @@ -4314,7 +4325,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) keyinfo->key_length=0; keyinfo->rec_per_key=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; - keyinfo->name= (char*) "weedout_key"; + keyinfo->name= weedout_key; { key_part_info->null_bit=0; key_part_info->field= field; @@ -5332,7 +5343,11 @@ TABLE *create_dummy_tmp_table(THD *thd) sjm_table_param.init(); sjm_table_param.field_count= 1; List<Item> sjm_table_cols; + const LEX_CSTRING dummy_name= { STRING_WITH_LEN("dummy") }; Item *column_item= new (thd->mem_root) Item_int(thd, 1); + if (!column_item) + DBUG_RETURN(NULL); + sjm_table_cols.push_back(column_item, thd->mem_root); if (!(table= create_tmp_table(thd, &sjm_table_param, sjm_table_cols, (ORDER*) 0, @@ -5341,7 +5356,7 @@ TABLE *create_dummy_tmp_table(THD *thd) thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR /*rows_limit */, - (char*)"dummy", TRUE /* Do not open */))) + &dummy_name, TRUE /* Do not open */))) { DBUG_RETURN(NULL); } diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 7954becfad4..9cb19e0cc6c 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -298,7 +298,7 @@ public: pos->table= tab; // todo need ref_depend_map ? DBUG_PRINT("info", ("Produced a LooseScan plan, key %s, %s", - tab->table->key_info[best_loose_scan_key].name, + tab->table->key_info[best_loose_scan_key].name.str, best_loose_scan_start_key? "(ref access)": "(range/index access)")); } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 8a75aaed8d6..43d1c2de7ad 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -48,7 +48,7 @@ (assuming a index for column d of table t2 is defined) */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "key.h" // key_cmp_if_same #include "sql_select.h" @@ -398,6 +398,8 @@ int opt_sum_query(THD *thd, const_result= 0; break; } + longlong info_limit= 1; + table->file->info_push(INFO_KIND_FORCE_LIMIT_BEGIN, &info_limit); if (!(error= table->file->ha_index_init((uint) ref.key, 1))) error= (is_max ? get_index_max_value(table, &ref, range_fl) : @@ -410,6 +412,7 @@ int opt_sum_query(THD *thd, error= HA_ERR_KEY_NOT_FOUND; table->file->ha_end_keyread(); table->file->ha_index_end(); + table->file->info_push(INFO_KIND_FORCE_LIMIT_END, NULL); if (error) { if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index 191360a6969..ef9b07cca47 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -28,6 +28,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "my_bit.h" #include "sql_select.h" @@ -1809,6 +1810,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl) { DBUG_PRINT("info", ("Eliminated table %s", table->alias.c_ptr())); tab->type= JT_CONST; + tab->table->const_table= 1; join->eliminated_tables |= table->map; join->const_table_map|= table->map; set_position(join, join->const_tables++, tab, (KEYUSE*)0); diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 94f72b7b492..90d766f15d2 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -20,13 +20,12 @@ Text .frm files management routines */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "parse_file.h" #include "unireg.h" // CREATE_MODE #include "sql_table.h" // build_table_filename #include <m_ctype.h> -#include <my_sys.h> #include <my_dir.h> /* from sql_db.cc */ @@ -761,12 +760,12 @@ File_parser::parse(uchar* base, MEM_ROOT *mem_root, { File_option *parameter= parameters+first_param, *parameters_end= parameters+required; - int len= 0; + size_t len= 0; for (; parameter < parameters_end; parameter++) { len= parameter->name.length; // check length - if (len < (end-ptr) && ptr[len] != '=') + if (len < (size_t)(end-ptr) && ptr[len] != '=') continue; // check keyword if (memcmp(parameter->name.str, ptr, len) == 0) diff --git a/sql/parse_file.h b/sql/parse_file.h index 3f48b2072db..19c7883f8cc 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -17,9 +17,8 @@ #ifndef _PARSE_FILE_H_ #define _PARSE_FILE_H_ -#include "my_global.h" // uchar #include "sql_string.h" // LEX_STRING -#include "sql_list.h" // Sql_alloc +#include "sql_alloc.h" // Sql_alloc class THD; diff --git a/sql/partition_element.h b/sql/partition_element.h index c774994b7f5..162c446a3c2 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -26,7 +26,8 @@ enum partition_type { NOT_A_PARTITION= 0, RANGE_PARTITION, HASH_PARTITION, - LIST_PARTITION + LIST_PARTITION, + VERSIONING_PARTITION }; enum partition_state { @@ -89,8 +90,76 @@ typedef struct p_elem_val struct st_ddl_log_memory_entry; -class partition_element :public Sql_alloc { +/* Used for collecting MIN/MAX stats on row_end for doing pruning + in SYSTEM_TIME partitiong. */ +class Vers_min_max_stats : public Sql_alloc +{ + static const uint buf_size= 4 + (TIME_SECOND_PART_DIGITS + 1) / 2; + uchar min_buf[buf_size]; + uchar max_buf[buf_size]; + Field_timestampf min_value; + Field_timestampf max_value; + mysql_rwlock_t lock; + public: + Vers_min_max_stats(const LEX_CSTRING *field_name, TABLE_SHARE *share) : + min_value(min_buf, NULL, 0, Field::NONE, field_name, share, 6), + max_value(max_buf, NULL, 0, Field::NONE, field_name, share, 6) + { + min_value.set_max(); + memset(max_buf, 0, buf_size); + mysql_rwlock_init(key_rwlock_LOCK_vers_stats, &lock); + } + ~Vers_min_max_stats() + { + mysql_rwlock_destroy(&lock); + } + bool update_unguarded(Field *from) + { + return + from->update_min(&min_value, false) + + from->update_max(&max_value, false); + } + bool update(Field *from) + { + mysql_rwlock_wrlock(&lock); + bool res= update_unguarded(from); + mysql_rwlock_unlock(&lock); + return res; + } + my_time_t min_time() + { + mysql_rwlock_rdlock(&lock); + ulong sec_part; + my_time_t res= min_value.get_timestamp(&sec_part); + mysql_rwlock_unlock(&lock); + return res; + } + my_time_t max_time() + { + mysql_rwlock_rdlock(&lock); + ulong sec_part; + my_time_t res= max_value.get_timestamp(&sec_part); + mysql_rwlock_unlock(&lock); + return res; + } +}; + +enum stat_trx_field +{ + STAT_TRX_END= 0 +}; + +class partition_element :public Sql_alloc +{ +public: + enum elem_type + { + CONVENTIONAL= 0, + CURRENT, + HISTORY + }; + List<partition_element> subpartitions; List<part_elem_value> list_val_list; ha_rows part_max_rows; @@ -109,6 +178,21 @@ public: bool has_null_value; bool signed_flag; // Range value signed bool max_value; // MAXVALUE range + uint32 id; + bool empty; + + // TODO: subclass partition_element by partitioning type to avoid such semantic + // mixup + elem_type type() + { + return (elem_type)(int(signed_flag) << 1 | int(max_value)); + } + + void type(elem_type val) + { + max_value= (bool)(val & 1); + signed_flag= (bool)(val & 2); + } partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -117,9 +201,10 @@ public: data_file_name(NULL), index_file_name(NULL), engine_type(NULL), connect_string(null_clex_str), part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), - signed_flag(FALSE), max_value(FALSE) - { - } + signed_flag(FALSE), max_value(FALSE), + id(UINT_MAX32), + empty(true) + {} partition_element(partition_element *part_elem) : part_max_rows(part_elem->part_max_rows), part_min_rows(part_elem->part_min_rows), @@ -132,10 +217,35 @@ public: connect_string(null_clex_str), part_state(part_elem->part_state), nodegroup_id(part_elem->nodegroup_id), - has_null_value(FALSE) + has_null_value(FALSE), + id(part_elem->id), + empty(part_elem->empty) + {} + ~partition_element() {} + + part_column_list_val& get_col_val(uint idx) { + DBUG_ASSERT(type() == CONVENTIONAL || list_val_list.elements == 1); + part_elem_value *ev= list_val_list.head(); + DBUG_ASSERT(ev); + DBUG_ASSERT(ev->col_val_array); + return ev->col_val_array[idx]; + } + + bool find_engine_flag(uint32 flag) + { + if (ha_check_storage_engine_flag(engine_type, flag)) + return true; + + List_iterator_fast<partition_element> it(subpartitions); + while (partition_element *element= it++) + { + if (element->find_engine_flag(flag)) + return true; + } + + return false; } - ~partition_element() {} }; #endif /* PARTITION_ELEMENT_INCLUDED */ diff --git a/sql/partition_info.cc b/sql/partition_info.cc index d721453a181..5939da09eae 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -20,7 +20,9 @@ #pragma implementation #endif +#include "mariadb.h" #include <my_global.h> +#include <tztime.h> #include "sql_priv.h" // Required to get server definitions for mysql/plugin.h right #include "sql_plugin.h" @@ -30,6 +32,9 @@ #include "sql_parse.h" #include "sql_acl.h" // *_ACL #include "sql_base.h" // fill_record +#include "sql_statistics.h" // vers_stat_end +#include "vers_utils.h" +#include "lock.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -113,6 +118,19 @@ partition_info *partition_info::get_clone(THD *thd) part_clone->list_val_list.push_back(new_val, mem_root); } } + if (part_type == VERSIONING_PARTITION && vers_info) + { + // clone Vers_part_info; set now_part, hist_part + clone->vers_info= new (mem_root) Vers_part_info(*vers_info); + List_iterator<partition_element> it(clone->partitions); + while ((part= it++)) + { + if (vers_info->now_part && part->id == vers_info->now_part->id) + clone->vers_info->now_part= part; + else if (vers_info->hist_part && part->id == vers_info->hist_part->id) + clone->vers_info->hist_part= part; + } // while ((part= it++)) + } // if (part_type == VERSIONING_PARTITION ... DBUG_RETURN(clone); } @@ -127,8 +145,7 @@ partition_info *partition_info::get_clone(THD *thd) @retval false Partition not found */ -bool partition_info::add_named_partition(const char *part_name, - uint length) +bool partition_info::add_named_partition(const char *part_name, size_t length) { HASH *part_name_hash; PART_NAME_DEF *part_def; @@ -179,8 +196,7 @@ bool partition_info::add_named_partition(const char *part_name, @param part_elem Partition element that matched. */ -bool partition_info::set_named_partition_bitmap(const char *part_name, - uint length) +bool partition_info::set_named_partition_bitmap(const char *part_name, size_t length) { DBUG_ENTER("partition_info::set_named_partition_bitmap"); bitmap_clear_all(&read_partitions); @@ -199,13 +215,55 @@ bool partition_info::set_named_partition_bitmap(const char *part_name, @param table_list Table list pointing to table to prune. @return Operation status + @retval false Success + @retval true Failure +*/ +bool partition_info::set_read_partitions(List<char> *partition_names) +{ + DBUG_ENTER("partition_info::set_read_partitions"); + if (!partition_names || !partition_names->elements) + { + DBUG_RETURN(true); + } + + uint num_names= partition_names->elements; + List_iterator<char> partition_names_it(*partition_names); + uint i= 0; + /* + TODO: When adding support for FK in partitioned tables, the referenced + table must probably lock all partitions for read, and also write depending + of ON DELETE/UPDATE. + */ + bitmap_clear_all(&read_partitions); + + /* No check for duplicate names or overlapping partitions/subpartitions. */ + + DBUG_PRINT("info", ("Searching through partition_name_hash")); + do + { + char *part_name= partition_names_it++; + if (add_named_partition(part_name, strlen(part_name))) + DBUG_RETURN(true); + } while (++i < num_names); + DBUG_RETURN(false); +} + + + +/** + Prune away partitions not mentioned in the PARTITION () clause, + if used. + + @param partition_names list of names of partitions. + + @return Operation status @retval true Failure @retval false Success */ -bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list) +bool partition_info::prune_partition_bitmaps(List<String> *partition_names) { - List_iterator<String> partition_names_it(*(table_list->partition_names)); - uint num_names= table_list->partition_names->elements; + List_iterator<String> partition_names_it(*(partition_names)); + uint num_names= partition_names->elements; uint i= 0; DBUG_ENTER("partition_info::prune_partition_bitmaps"); @@ -235,8 +293,7 @@ bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list) /** Set read/lock_partitions bitmap over non pruned partitions - @param table_list Possible TABLE_LIST which can contain - list of partition names to query + @param partition_names list of partition names to query @return Operation status @retval FALSE OK @@ -246,7 +303,7 @@ bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list) @note OK to call multiple times without the need for free_bitmaps. */ -bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list) +bool partition_info::set_partition_bitmaps(List<String> *partition_names) { DBUG_ENTER("partition_info::set_partition_bitmaps"); @@ -255,16 +312,15 @@ bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list) if (!bitmaps_are_initialized) DBUG_RETURN(TRUE); - if (table_list && - table_list->partition_names && - table_list->partition_names->elements) + if (partition_names && + partition_names->elements) { if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) { my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0)); DBUG_RETURN(true); } - if (prune_partition_bitmaps(table_list)) + if (prune_partition_bitmaps(partition_names)) DBUG_RETURN(TRUE); } else @@ -278,6 +334,27 @@ bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list) } +/** + Set read/lock_partitions bitmap over non pruned partitions + + @param table_list Possible TABLE_LIST which can contain + list of partition names to query + + @return Operation status + @retval FALSE OK + @retval TRUE Failed to allocate memory for bitmap or list of partitions + did not match + + @note OK to call multiple times without the need for free_bitmaps. +*/ +bool partition_info::set_partition_bitmaps_from_table(TABLE_LIST *table_list) +{ + List<String> *partition_names= table_list ? + NULL : table_list->partition_names; + return set_partition_bitmaps(partition_names); +} + + /* Create a memory area where default partition names are stored and fill it up with the names. @@ -340,7 +417,7 @@ char *partition_info::create_default_partition_names(THD *thd, uint part_no, char *partition_info::create_default_subpartition_name(THD *thd, uint subpart_no, const char *part_name) { - uint size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE; + size_t size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE; char *ptr= (char*) thd->calloc(size_alloc); DBUG_ENTER("create_default_subpartition_name"); @@ -779,6 +856,501 @@ bool partition_info::has_unique_name(partition_element *element) DBUG_RETURN(TRUE); } +bool partition_info::vers_init_info(THD * thd) +{ + part_type= VERSIONING_PARTITION; + list_of_part_fields= TRUE; + column_list= TRUE; + num_columns= 1; + vers_info= new (thd->mem_root) Vers_part_info; + if (!vers_info) + { + mem_alloc_error(sizeof(Vers_part_info)); + return true; + } + return false; +} + +bool partition_info::vers_set_interval(const INTERVAL & i) +{ + if (i.neg || i.second_part) + return true; + + DBUG_ASSERT(vers_info); + + // TODO: INTERVAL conversion to seconds leads to mismatch with calendar intervals (MONTH and YEAR) + vers_info->interval= static_cast<my_time_t>( + i.second + + i.minute * 60 + + i.hour * 60 * 60 + + i.day * 24 * 60 * 60 + + i.month * 30 * 24 * 60 * 60 + + i.year * 365 * 30 * 24 * 60 * 60); + + if (vers_info->interval == 0) + return true; + + return false; +} + +bool partition_info::vers_set_limit(ulonglong limit) +{ + if (limit < 1) + return true; + + DBUG_ASSERT(vers_info); + + vers_info->limit= limit; + return false; +} + +partition_element* +partition_info::vers_part_rotate(THD * thd) +{ + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(vers_info && vers_info->initialized()); + + if (table->s->hist_part_id >= vers_info->now_part->id - 1) + { + DBUG_ASSERT(table->s->hist_part_id == vers_info->now_part->id - 1); + push_warning_printf(thd, + thd->lex->sql_command == SQLCOM_ALTER_TABLE ? + Sql_condition::WARN_LEVEL_NOTE : + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_FULL, + ER_THD(thd, WARN_VERS_PART_FULL), + table->s->db.str, table->s->error_table_name(), + vers_info->hist_part->partition_name); + return vers_info->hist_part; + } + + table->s->hist_part_id++; + const char* old_part_name= vers_info->hist_part->partition_name; + vers_hist_part(); + + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, + WARN_VERS_PART_ROTATION, + ER_THD(thd, WARN_VERS_PART_ROTATION), + table->s->db.str, table->s->error_table_name(), + old_part_name, + vers_info->hist_part->partition_name); + + return vers_info->hist_part; +} + +bool partition_info::vers_set_expression(THD *thd, partition_element *el, MYSQL_TIME& t) +{ + curr_part_elem= el; + init_column_part(thd); + el->list_val_list.empty(); + el->list_val_list.push_back(curr_list_val, thd->mem_root); + for (uint i= 0; i < num_columns; ++i) + { + part_column_list_val *col_val= add_column_value(thd); + if (el->type() == partition_element::CURRENT) + { + col_val->max_value= true; + col_val->item_expression= NULL; + col_val->column_value= NULL; + col_val->part_info= this; + col_val->fixed= 1; + continue; + } + Item *item_expression= new (thd->mem_root) Item_datetime_literal(thd, &t); + if (!item_expression) + return true; + /* We initialize col_val with bogus max value to make fix_partition_func() and check_range_constants() happy. + Later in vers_setup_stats() it is initialized with real stat value if there will be any. */ + /* FIXME: TIME_RESULT in col_val is expensive. It should be INT_RESULT + (got to be fixed when InnoDB is supported). */ + init_col_val(col_val, item_expression); + DBUG_ASSERT(item_expression == el->get_col_val(i).item_expression); + } // for (num_columns) + return false; +} + +bool partition_info::vers_setup_expression(THD * thd, uint32 alter_add) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + + if (!table->versioned(VERS_TIMESTAMP)) + { + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->s->table_name.str); + return true; + } + + if (alter_add) + { + DBUG_ASSERT(partitions.elements > alter_add + 1); + Vers_min_max_stats** old_array= table->s->stat_trx; + table->s->stat_trx= static_cast<Vers_min_max_stats**>( + alloc_root(&table->s->mem_root, sizeof(void *) * (partitions.elements * num_columns + 1))); + memcpy(table->s->stat_trx, old_array, sizeof(void *) * (partitions.elements - alter_add) * num_columns); + table->s->stat_trx[partitions.elements * num_columns]= NULL; + } + else + { + /* Prepare part_field_list */ + Field *row_end= table->vers_end_field(); + part_field_list.push_back(row_end->field_name.str, thd->mem_root); + DBUG_ASSERT(part_field_list.elements == num_columns); + // needed in handle_list_of_fields() + row_end->flags|= GET_FIXED_FIELDS_FLAG; + } + + List_iterator<partition_element> it(partitions); + partition_element *el; + MYSQL_TIME t; + memset(&t, 0, sizeof(t)); + my_time_t ts= TIMESTAMP_MAX_VALUE - partitions.elements; + uint32 id= 0; + while ((el= it++)) + { + DBUG_ASSERT(el->type() != partition_element::CONVENTIONAL); + ++ts; + if (alter_add) + { + /* Non-empty historical partitions are left as is. */ + if (el->type() == partition_element::HISTORY && !el->empty) + { + ++id; + continue; + } + /* Newly added element is inserted before AS_OF_NOW. */ + if (el->id == UINT_MAX32 || el->type() == partition_element::CURRENT) + { + DBUG_ASSERT(table && table->s); + Vers_min_max_stats *stat_trx_end= new (&table->s->mem_root) + Vers_min_max_stats(&table->s->vers_end_field()->field_name, table->s); + table->s->stat_trx[id * num_columns + STAT_TRX_END]= stat_trx_end; + el->id= id++; + if (el->type() == partition_element::CURRENT) + break; + goto set_expression; + } + /* Existing element expression is recalculated. */ + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + for (uint i= 0; i < num_columns; ++i) + { + part_column_list_val &col_val= el->get_col_val(i); + static_cast<Item_datetime_literal *>(col_val.item_expression)->set_time(&t); + col_val.fixed= 0; + } + ++id; + continue; + } + + set_expression: + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + if (vers_set_expression(thd, el, t)) + return true; + } + return false; +} + + +class Table_locker +{ + THD *thd; + TABLE &table; + thr_lock_type saved_type; + MYSQL_LOCK *saved_lock; + enum_locked_tables_mode saved_mode; + TABLE_LIST **saved_query_tables_own_last; + TABLE_LIST table_list; + bool locked; + +public: + Table_locker(THD *_thd, TABLE &_table, thr_lock_type lock_type) : + thd(_thd), + table(_table), + saved_type(table.reginfo.lock_type), + saved_lock(_thd->lock), + saved_mode(_thd->locked_tables_mode), + saved_query_tables_own_last(_thd->lex->query_tables_own_last), + table_list(&_table, lock_type), + locked(false) + { + table.reginfo.lock_type= lock_type; + } + bool lock() + { + DBUG_ASSERT(table.file); + // FIXME: check consistency with table.reginfo.lock_type + if (table.file->get_lock_type() != F_UNLCK + || table.s->tmp_table) + { + return false; + } + thd->lock= NULL; + thd->locked_tables_mode= LTM_NONE; + thd->lex->query_tables_own_last= NULL; + bool res= lock_tables(thd, &table_list, 1, 0); + locked= !res; + return res; + } + ~Table_locker() + { + if (locked) + mysql_unlock_tables(thd, thd->lock); + table.reginfo.lock_type= saved_type; + thd->lock= saved_lock; + thd->locked_tables_mode= saved_mode; + thd->lex->query_tables_own_last= saved_query_tables_own_last; + if (locked && !thd->in_sub_stmt) + { + ha_commit_trans(thd, false); + ha_commit_trans(thd, true); + } + } +}; + + +// scan table for min/max row_end +inline +bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) +{ + uint32 sub_factor= num_subparts ? num_subparts : 1; + uint32 part_id= part->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part->empty); + DBUG_ASSERT(part->type() == partition_element::HISTORY); + DBUG_ASSERT(table->s->stat_trx); + + Table_locker l(thd, *table, TL_READ); + if (l.lock()) + { + my_error(ER_INTERNAL_ERROR, MYF(0), "min/max scan failed on lock_tables()"); + return true; + } + + for (; part_id < part_id_end; ++part_id) + { + handler *file= table->file->part_handler(part_id); // requires update_partition() for ha_innopart + DBUG_ASSERT(file); + + table->default_column_bitmaps(); + bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); + file->column_bitmaps_signal(); + + int rc= file->ha_rnd_init(true); + if (!rc) + { + while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) + { + if (part->empty) + part->empty= false; + if (thd->killed) + { + file->ha_rnd_end(); + file->update_partition(part_id); + ha_commit_trans(thd, false); + return true; + } + if (rc) + { + if (rc == HA_ERR_RECORD_DELETED) + continue; + break; + } + if (table->vers_end_field()->is_max()) + { + rc= HA_ERR_INTERNAL_ERROR; + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_NON_HISTORICAL, + ER_THD(thd, WARN_VERS_PART_NON_HISTORICAL), + part->partition_name); + break; + } + if (table->versioned(VERS_TRX_ID)) + { + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, &table->vers_end_field()->field_name, NULL, 6); + if (!vers_trx_id_to_ts(thd, table->vers_end_field(), fld)) + { + vers_stat_trx(STAT_TRX_END, part).update_unguarded(&fld); + } + } + else + { + vers_stat_trx(STAT_TRX_END, part).update_unguarded(table->vers_end_field()); + } + } + file->ha_rnd_end(); + } + file->update_partition(part_id); + if (rc != HA_ERR_END_OF_FILE) + { + // TODO: print rc code + my_error(ER_INTERNAL_ERROR, MYF(0), "min/max scan failed in versioned partitions setup (see warnings)"); + return true; + } + } + return false; +} + +void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, partition_element *el1) +{ + MYSQL_TIME t; + memset(&t, 0, sizeof(t)); + DBUG_ASSERT(table && table->s && table->s->stat_trx); + DBUG_ASSERT(!el0 || el1->id == el0->id + 1); + const uint idx= el1->id * num_columns; + my_time_t ts; + part_column_list_val *col_val; + Item_datetime_literal *val_item; + Vers_min_max_stats *stat_trx_x; + for (uint i= 0; i < num_columns; ++i) + { + stat_trx_x= table->s->stat_trx[idx + i]; + if (el0) + { + ts= stat_trx_x->min_time(); + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + col_val= &el0->get_col_val(i); + val_item= static_cast<Item_datetime_literal*>(col_val->item_expression); + DBUG_ASSERT(val_item); + if (*val_item > t) + { + val_item->set_time(&t); + col_val->fixed= 0; + } + } + col_val= &el1->get_col_val(i); + if (!col_val->max_value) + { + ts= stat_trx_x->max_time() + 1; + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + val_item= static_cast<Item_datetime_literal*>(col_val->item_expression); + DBUG_ASSERT(val_item); + if (*val_item < t) + { + val_item->set_time(&t); + col_val->fixed= 0; + } + } + } +} + + +// setup at open() phase (TABLE_SHARE is initialized) +bool partition_info::vers_setup_stats(THD * thd, bool is_create_table_ind) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + DBUG_ASSERT(vers_info && vers_info->initialized(false)); + DBUG_ASSERT(table && table->s); + + bool error= false; + + TABLE_LIST tl(table, TL_READ); + MDL_auto_lock mdl_lock(thd, tl); + if (mdl_lock.acquire_error()) + return true; + + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + + DBUG_ASSERT(part_field_list.elements == num_columns); + + bool dont_stat= true; + bool col_val_updated= false; + // initialize stat_trx + if (!table->s->stat_trx) + { + DBUG_ASSERT(partitions.elements > 1); + table->s->stat_trx= static_cast<Vers_min_max_stats**>( + alloc_root(&table->s->mem_root, sizeof(void *) * (partitions.elements * num_columns + 1))); + table->s->stat_trx[partitions.elements * num_columns]= NULL; + dont_stat= false; + } + + // build freelist, scan min/max, assign hist_part + List_iterator<partition_element> it(partitions); + partition_element *el= NULL, *prev; + while ((prev= el, el= it++)) + { + if (el->type() == partition_element::HISTORY && dont_stat) + { + if (el->id == table->s->hist_part_id) + { + vers_info->hist_part= el; + break; + } + continue; + } + + { + Vers_min_max_stats *stat_trx_end= new (&table->s->mem_root) + Vers_min_max_stats(&table->s->vers_end_field()->field_name, table->s); + table->s->stat_trx[el->id * num_columns + STAT_TRX_END]= stat_trx_end; + } + + if (!is_create_table_ind) + { + if (el->type() == partition_element::CURRENT) + { + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, &table->vers_end_field()->field_name, NULL, 6); + fld.set_max(); + vers_stat_trx(STAT_TRX_END, el).update_unguarded(&fld); + el->empty= false; + } + else if (vers_scan_min_max(thd, el)) + { + table->s->stat_trx= NULL; // may be a leak on endless table open + error= true; + break; + } + if (!el->empty) + { + vers_update_col_vals(thd, prev, el); + col_val_updated= true; + } + } + + if (el->type() == partition_element::CURRENT) + break; + + DBUG_ASSERT(el->type() == partition_element::HISTORY); + + if (vers_info->hist_part) + { + if (!el->empty) + goto set_hist_part; + } + else + { + set_hist_part: + vers_info->hist_part= el; + continue; + } + } // while + + if (!error && !dont_stat) + { + if (col_val_updated) + table->s->stat_serial++; + + table->s->hist_part_id= vers_info->hist_part->id; + } + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + return error; +} + /* Check that the partition/subpartition is setup to use the correct @@ -962,7 +1534,7 @@ error: called for RANGE PARTITIONed tables. */ -bool partition_info::check_range_constants(THD *thd) +bool partition_info::check_range_constants(THD *thd, bool alloc) { partition_element* part_def; bool first= TRUE; @@ -979,12 +1551,15 @@ bool partition_info::check_range_constants(THD *thd) part_column_list_val *UNINIT_VAR(current_largest_col_val); uint num_column_values= part_field_list.elements; uint size_entries= sizeof(part_column_list_val) * num_column_values; - range_col_array= (part_column_list_val*) thd->calloc(num_parts * - size_entries); - if (unlikely(range_col_array == NULL)) + if (alloc) { - mem_alloc_error(num_parts * size_entries); - goto end; + range_col_array= (part_column_list_val*) thd->calloc(num_parts * + size_entries); + if (unlikely(range_col_array == NULL)) + { + mem_alloc_error(num_parts * size_entries); + goto end; + } } loc_range_col_array= range_col_array; i= 0; @@ -1017,11 +1592,14 @@ bool partition_info::check_range_constants(THD *thd) longlong part_range_value; bool signed_flag= !part_expr->unsigned_flag; - range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong)); - if (unlikely(range_int_array == NULL)) + if (alloc) { - mem_alloc_error(num_parts * sizeof(longlong)); - goto end; + range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong)); + if (unlikely(range_int_array == NULL)) + { + mem_alloc_error(num_parts * sizeof(longlong)); + goto end; + } } i= 0; do @@ -1379,12 +1957,14 @@ static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem) bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, - bool add_or_reorg_part) + partition_info *add_or_reorg_part) { handlerton *table_engine= default_engine_type; uint i, tot_partitions; bool result= TRUE, table_engine_set; const char *same_name; + uint32 hist_parts= 0; + uint32 now_parts= 0; DBUG_ENTER("partition_info::check_partition_info"); DBUG_ASSERT(default_engine_type != partition_hton); @@ -1426,7 +2006,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (unlikely(is_sub_partitioned() && (!(part_type == RANGE_PARTITION || - part_type == LIST_PARTITION)))) + part_type == LIST_PARTITION || + part_type == VERSIONING_PARTITION)))) { /* Only RANGE and LIST partitioning can be subpartitioned */ my_error(ER_SUBPARTITION_ERROR, MYF(0)); @@ -1488,6 +2069,19 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; } + + if (part_type == VERSIONING_PARTITION) + { + DBUG_ASSERT(vers_info); + if (num_parts < 2 || !vers_info->now_part) + { + DBUG_ASSERT(info && info->alias.str); + my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias.str); + goto end; + } + DBUG_ASSERT(vers_info->initialized(false)); + DBUG_ASSERT(num_parts == partitions.elements); + } i= 0; { List_iterator<partition_element> part_it(partitions); @@ -1568,6 +2162,18 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } } } + if (part_type == VERSIONING_PARTITION) + { + if (part_elem->type() == partition_element::HISTORY) + { + hist_parts++; + } + else + { + DBUG_ASSERT(part_elem->type() == partition_element::CURRENT); + now_parts++; + } + } } while (++i < num_parts); if (!table_engine_set && num_parts_not_set != 0 && @@ -1585,6 +2191,24 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, goto end; } + if (hist_parts > 1) + { + if (unlikely(vers_info->limit == 0 && vers_info->interval == 0)) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PARAMETERS, + ER_THD(thd, WARN_VERS_PARAMETERS), + "no rotation condition for multiple HISTORY partitions."); + } + } + if (unlikely(now_parts > 1)) + { + my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias.str); + goto end; + } + + DBUG_ASSERT(table_engine != partition_hton && default_engine_type == table_engine); if (eng_type) @@ -1599,12 +2223,16 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, if (add_or_reorg_part) { - if (unlikely((part_type == RANGE_PARTITION && + if (unlikely(part_type == VERSIONING_PARTITION && + vers_setup_expression(thd, add_or_reorg_part->partitions.elements))) + goto end; + if (unlikely(((part_type == RANGE_PARTITION || part_type == VERSIONING_PARTITION) && check_range_constants(thd)) || (part_type == LIST_PARTITION && check_list_constants(thd)))) goto end; } + result= FALSE; end: DBUG_RETURN(result); @@ -1629,8 +2257,8 @@ void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag) THD *thd= current_thd; bzero(&table_list, sizeof(table_list)); - table_list.db= table_arg->s->db.str; - table_list.table_name= table_arg->s->table_name.str; + table_list.db= table_arg->s->db; + table_list.table_name= table_arg->s->table_name; if (check_single_table_access(thd, SELECT_ACL, &table_list, TRUE)) @@ -2389,6 +3017,7 @@ bool partition_info::error_if_requires_values() const switch (part_type) { case NOT_A_PARTITION: case HASH_PARTITION: + case VERSIONING_PARTITION: break; case RANGE_PARTITION: my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); @@ -2834,6 +3463,30 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) } +bool partition_info::vers_trx_id_to_ts(THD* thd, Field* in_trx_id, Field_timestamp& out_ts) +{ + DBUG_ASSERT(table); + handlerton *hton= plugin_hton(table->s->db_plugin); + DBUG_ASSERT(hton); + ulonglong trx_id= in_trx_id->val_int(); + TR_table trt(thd); + bool found= trt.query(trx_id); + if (!found) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_TRX_MISSING, + ER_THD(thd, WARN_VERS_TRX_MISSING), + trx_id); + return true; + } + MYSQL_TIME ts; + trt[TR_table::FLD_COMMIT_TS]->get_date(&ts, 0); + out_ts.store_time_dec(&ts, 6); + return false; +} + + void partition_info::print_debug(const char *str, uint *value) { DBUG_ENTER("print_debug"); diff --git a/sql/partition_info.h b/sql/partition_info.h index fc13d48e5ee..26955682dda 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -22,6 +22,7 @@ #include "sql_class.h" #include "partition_element.h" +#include "sql_partition.h" class partition_info; struct TABLE_LIST; @@ -34,6 +35,45 @@ typedef int (*get_subpart_id_func)(partition_info *part_info, struct st_ddl_log_memory_entry; +struct Vers_part_info : public Sql_alloc +{ + Vers_part_info() : + interval(0), + limit(0), + now_part(NULL), + hist_part(NULL), + stat_serial(0) + { + } + Vers_part_info(Vers_part_info &src) : + interval(src.interval), + limit(src.limit), + now_part(NULL), + hist_part(NULL), + stat_serial(src.stat_serial) + { + } + bool initialized(bool fully= true) + { + if (now_part) + { + DBUG_ASSERT(now_part->id != UINT_MAX32); + DBUG_ASSERT(now_part->type() == partition_element::CURRENT); + DBUG_ASSERT(!fully || (bool) hist_part); + DBUG_ASSERT(!hist_part || ( + hist_part->id != UINT_MAX32 && + hist_part->type() == partition_element::HISTORY)); + return true; + } + return false; + } + my_time_t interval; + ulonglong limit; + partition_element *now_part; + partition_element *hist_part; + ulonglong stat_serial; +}; + class partition_info : public Sql_alloc { public: @@ -143,6 +183,8 @@ public: part_column_list_val *range_col_array; part_column_list_val *list_col_array; }; + + Vers_part_info *vers_info; /******************************************** * INTERVAL ANALYSIS @@ -250,7 +292,7 @@ public: part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), bitmaps_are_initialized(FALSE), - list_array(NULL), err_value(0), + list_array(NULL), vers_info(NULL), err_value(0), part_info_string(NULL), curr_part_elem(NULL), current_partition(NULL), curr_list_object(0), num_columns(0), table(NULL), @@ -282,8 +324,9 @@ public: ~partition_info() {} partition_info *get_clone(THD *thd); - bool set_named_partition_bitmap(const char *part_name, uint length); - bool set_partition_bitmaps(TABLE_LIST *table_list); + bool set_named_partition_bitmap(const char *part_name, size_t length); + bool set_partition_bitmaps(List<String> *partition_names); + bool set_partition_bitmaps_from_table(TABLE_LIST *table_list); /* Answers the question if subpartitioning is used for a certain table */ bool is_sub_partitioned() { @@ -302,11 +345,11 @@ public: const char *find_duplicate_field(); char *find_duplicate_name(); bool check_engine_mix(handlerton *engine_type, bool default_engine); - bool check_range_constants(THD *thd); + bool check_range_constants(THD *thd, bool alloc= true); bool check_list_constants(THD *thd); bool check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, - bool check_partition_function); + partition_info *add_or_reorg_part= NULL); void print_no_partition_found(TABLE *table, myf errflag); void print_debug(const char *str, uint*); Item* get_column_item(Item *item, Field *field); @@ -343,10 +386,170 @@ private: uint start_no); char *create_default_subpartition_name(THD *thd, uint subpart_no, const char *part_name); - bool prune_partition_bitmaps(TABLE_LIST *table_list); - bool add_named_partition(const char *part_name, uint length); + // FIXME: prune_partition_bitmaps() is duplicate of set_read_partitions() + bool prune_partition_bitmaps(List<String> *partition_names); + bool add_named_partition(const char *part_name, size_t length); public: + bool set_read_partitions(List<char> *partition_names); bool has_unique_name(partition_element *element); + + bool vers_init_info(THD *thd); + bool vers_set_interval(const INTERVAL &i); + bool vers_set_limit(ulonglong limit); + partition_element* vers_part_rotate(THD *thd); + bool vers_set_expression(THD *thd, partition_element *el, MYSQL_TIME &t); + bool vers_setup_expression(THD *thd, uint32 alter_add= 0); /* Stage 1. */ + bool vers_setup_stats(THD *thd, bool is_create_table_ind); /* Stage 2. */ + bool vers_scan_min_max(THD *thd, partition_element *part); + void vers_update_col_vals(THD *thd, partition_element *el0, partition_element *el1); + + partition_element *vers_hist_part() + { + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table->s->hist_part_id != UINT_MAX32); + if (table->s->hist_part_id == vers_info->hist_part->id) + return vers_info->hist_part; + + List_iterator<partition_element> it(partitions); + partition_element *el; + while ((el= it++)) + { + DBUG_ASSERT(el->type() != partition_element::CONVENTIONAL); + if (el->type() == partition_element::HISTORY && + el->id == table->s->hist_part_id) + { + vers_info->hist_part= el; + return vers_info->hist_part; + } + } + DBUG_ASSERT(0); + return NULL; + } + partition_element *get_partition(uint part_id) + { + List_iterator<partition_element> it(partitions); + partition_element *el; + while ((el= it++)) + { + if (el->id == part_id) + return el; + } + return NULL; + } + bool vers_limit_exceed(partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->limit) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + // TODO: cache thread-shared part_recs and increment on INSERT + return table->file->part_records(part) >= vers_info->limit; + } + Vers_min_max_stats& vers_stat_trx(stat_trx_field fld, uint32 part_element_id) + { + DBUG_ASSERT(table && table->s && table->s->stat_trx); + Vers_min_max_stats* res= table->s->stat_trx[part_element_id * num_columns + fld]; + DBUG_ASSERT(res); + return *res; + } + Vers_min_max_stats& vers_stat_trx(stat_trx_field fld, partition_element *part) + { + DBUG_ASSERT(part); + return vers_stat_trx(fld, part->id); + } + bool vers_interval_exceed(my_time_t max_time, partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->interval) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + my_time_t min_time= vers_stat_trx(STAT_TRX_END, part).min_time(); + return max_time - min_time > vers_info->interval; + } + bool vers_interval_exceed(partition_element *part) + { + return vers_interval_exceed(vers_stat_trx(STAT_TRX_END, part).max_time(), part); + } + bool vers_interval_exceed() + { + return vers_interval_exceed(vers_hist_part()); + } + bool vers_trx_id_to_ts(THD *thd, Field *in_trx_id, Field_timestamp &out_ts); + void vers_update_stats(THD *thd, partition_element *el) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(el && el->type() == partition_element::HISTORY); + bool updated; + mysql_rwlock_wrlock(&table->s->LOCK_stat_serial); + el->empty= false; + if (table->versioned(VERS_TRX_ID)) + { + // transaction is not yet pushed to VTQ, so we use now-time + my_time_t end_ts= my_time_t(0); + + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, &table->vers_end_field()->field_name, NULL, 6); + fld.store_TIME(end_ts, 0); + updated= + vers_stat_trx(STAT_TRX_END, el->id).update(&fld); + } + else + { + updated= + vers_stat_trx(STAT_TRX_END, el->id).update(table->vers_end_field()); + } + if (updated) + table->s->stat_serial++; + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + if (updated) + { + vers_update_col_vals(thd, + el->id > 0 ? get_partition(el->id - 1) : NULL, + el); + } + } + void vers_update_stats(THD *thd, uint part_id) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + if (part_id < vers_info->now_part->id) + vers_update_stats(thd, get_partition(part_id)); + } + bool vers_update_range_constants(THD *thd) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table && table->s); + + mysql_rwlock_rdlock(&table->s->LOCK_stat_serial); + if (vers_info->stat_serial == table->s->stat_serial) + { + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + return false; + } + + bool result= false; + for (uint i= 0; i < num_columns; ++i) + { + Field *f= part_field_array[i]; + bitmap_set_bit(f->table->write_set, f->field_index); + } + MEM_ROOT *old_root= thd->mem_root; + thd->mem_root= &table->mem_root; + result= check_range_constants(thd, false); + thd->mem_root= old_root; + vers_info->stat_serial= table->s->stat_serial; + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + return result; + } }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); diff --git a/sql/password.c b/sql/password.c index 1f0a55a10fe..5e9684acb25 100644 --- a/sql/password.c +++ b/sql/password.c @@ -60,7 +60,7 @@ *****************************************************************************/ -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #include <m_string.h> #include <password.h> @@ -296,7 +296,7 @@ void make_password_from_salt_323(char *to, const ulong *salt) buf+len*2 */ -char *octet2hex(char *to, const char *str, uint len) +char *octet2hex(char *to, const char *str, size_t len) { const char *str_end= str + len; for (; str != str_end; ++str) diff --git a/sql/procedure.cc b/sql/procedure.cc index 3d36b7adfa3..d5b93433f92 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -20,7 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "procedure.h" #include "sql_analyse.h" // Includes procedure @@ -96,7 +96,7 @@ setup_procedure(THD *thd,ORDER *param,select_result *result, DBUG_RETURN(proc); } } - my_error(ER_UNKNOWN_PROCEDURE, MYF(0), (*param->item)->name); + my_error(ER_UNKNOWN_PROCEDURE, MYF(0), (*param->item)->name.str); *error=1; DBUG_RETURN(0); } diff --git a/sql/procedure.h b/sql/procedure.h index 23e5751a008..1c24901791c 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -63,7 +63,7 @@ public: { return type_handler()->Item_get_date(this, ltime, fuzzydate); } - Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + Item* get_copy(THD *thd) { return 0; } }; class Item_proc_real :public Item_proc diff --git a/sql/protocol.cc b/sql/protocol.cc index d2f91d51910..032e79c9289 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -25,7 +25,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "protocol.h" #include "sql_class.h" // THD @@ -86,7 +86,7 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length, { uint dummy_errors; /* Calculate maxumum possible result length */ - uint conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen; + size_t conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen; if (conv_length > 250) { @@ -106,8 +106,8 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length, net_store_data((const uchar*) convert->ptr(), convert->length())); } - ulong packet_length= packet->length(); - ulong new_length= packet_length + conv_length + 1; + size_t packet_length= packet->length(); + size_t new_length= packet_length + conv_length + 1; if (new_length > packet->alloced_length() && packet->realloc(new_length)) return 1; @@ -480,8 +480,9 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err, - ulonglong for bigger numbers. */ -static uchar *net_store_length_fast(uchar *packet, uint length) +static uchar *net_store_length_fast(uchar *packet, size_t length) { + DBUG_ASSERT(length < UINT_MAX16); if (length < 251) { *packet=(uchar) length; @@ -661,7 +662,7 @@ void net_send_progress_packet(THD *thd) { uchar buff[200], *pos; const char *proc_info= thd->proc_info ? thd->proc_info : ""; - uint length= strlen(proc_info); + size_t length= strlen(proc_info); ulonglong progress; DBUG_ENTER("net_send_progress_packet"); @@ -1015,7 +1016,7 @@ bool Protocol::store(const char *from, CHARSET_INFO *cs) { if (!from) return store_null(); - uint length= strlen(from); + size_t length= strlen(from); return store(from, length, cs); } @@ -1235,7 +1236,7 @@ bool Protocol_text::store(Field *field) char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff), &my_charset_bin); CHARSET_INFO *tocs= this->thd->variables.character_set_results; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS TABLE *table= field->table; my_bitmap_map *old_map= 0; if (table->file) @@ -1243,7 +1244,7 @@ bool Protocol_text::store(Field *field) #endif field->val_str(&str); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (old_map) dbug_tmp_restore_column_map(table->read_set, old_map); #endif diff --git a/sql/proxy_protocol.cc b/sql/proxy_protocol.cc new file mode 100644 index 00000000000..dbdb1566bc7 --- /dev/null +++ b/sql/proxy_protocol.cc @@ -0,0 +1,494 @@ +/* Copyright (c) 2017, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +#include <mariadb.h> +#include <mysql.h> +#include <mysql_com.h> +#include <mysqld_error.h> +#include <my_sys.h> +#include <m_string.h> +#include <my_net.h> +#include <violite.h> +#include <proxy_protocol.h> +#include <log.h> + +#define PROXY_PROTOCOL_V1_SIGNATURE "PROXY" +#define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" +#define MAX_PROXY_HEADER_LEN 256 + +/* + Parse proxy protocol version 1 header (text) +*/ +static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info) +{ + char address_family[MAX_PROXY_HEADER_LEN + 1]; + char client_address[MAX_PROXY_HEADER_LEN + 1]; + char server_address[MAX_PROXY_HEADER_LEN + 1]; + int client_port; + int server_port; + + int ret = sscanf(hdr, "PROXY %s %s %s %d %d", + address_family, client_address, server_address, + &client_port, &server_port); + + if (ret != 5) + { + if (ret >= 1 && !strcmp(address_family, "UNKNOWN")) + { + peer_info->is_local_command= true; + return 0; + } + return -1; + } + + if (client_port < 0 || client_port > 0xffff + || server_port < 0 || server_port > 0xffff) + return -1; + + if (!strcmp(address_family, "UNKNOWN")) + { + peer_info->is_local_command= true; + return 0; + } + else if (!strcmp(address_family, "TCP4")) + { + /* Initialize IPv4 peer address.*/ + peer_info->peer_addr.ss_family= AF_INET; + if (!inet_pton(AF_INET, client_address, + &((struct sockaddr_in *)(&peer_info->peer_addr))->sin_addr)) + return -1; + } + else if (!strcmp(address_family, "TCP6")) + { + /* Initialize IPv6 peer address.*/ + peer_info->peer_addr.ss_family= AF_INET6; + if (!inet_pton(AF_INET6, client_address, + &((struct sockaddr_in6 *)(&peer_info->peer_addr))->sin6_addr)) + return -1; + } + peer_info->port= client_port; + /* Check if server address is legal.*/ + char addr_bin[16]; + if (!inet_pton(peer_info->peer_addr.ss_family, + server_address, addr_bin)) + return -1; + + return 0; +} + + +/* + Parse proxy protocol V2 (binary) header +*/ +static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info) +{ + /* V2 Signature */ + if (memcmp(hdr, PROXY_PROTOCOL_V2_SIGNATURE, 12)) + return -1; + + /* version + command */ + uint8 ver= (hdr[12] & 0xF0); + if (ver != 0x20) + return -1; /* Wrong version*/ + + uint cmd= (hdr[12] & 0xF); + + /* Address family */ + uchar fam= hdr[13]; + + if (cmd == 0) + { + /* LOCAL command*/ + peer_info->is_local_command= true; + return 0; + } + + if (cmd != 0x01) + { + /* Not PROXY COMMAND */ + return -1; + } + + struct sockaddr_in *sin= (struct sockaddr_in *)(&peer_info->peer_addr); + struct sockaddr_in6 *sin6= (struct sockaddr_in6 *)(&peer_info->peer_addr); + switch (fam) + { + case 0x11: /* TCPv4 */ + sin->sin_family= AF_INET; + memcpy(&(sin->sin_addr), hdr + 16, 4); + peer_info->port= (hdr[24] << 8) + hdr[25]; + break; + case 0x21: /* TCPv6 */ + sin6->sin6_family= AF_INET6; + memcpy(&(sin6->sin6_addr), hdr + 16, 16); + peer_info->port= (hdr[48] << 8) + hdr[49]; + break; + case 0x31: /* AF_UNIX, stream */ + peer_info->peer_addr.ss_family= AF_UNIX; + break; + default: + return -1; + } + return 0; +} + + +bool has_proxy_protocol_header(NET *net) +{ + compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V1_SIGNATURE)); + compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V2_SIGNATURE)); + + const uchar *preread_bytes= net->buff + net->where_b; + return !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE)|| + !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE); +} + + +/** + Try to parse proxy header. + https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + + Whenever this function is called, client is connecting, and + we have have pre-read 4 bytes (NET_HEADER_SIZE) from the network already. + These 4 bytes did not match MySQL packet header, and (unless the client + is buggy), those bytes must be proxy header. + + @param[in] net - vio and already preread bytes from the header + @param[out] peer_info - parsed proxy header with client host and port + @return 0 in case of success, -1 if error. +*/ +int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info) +{ + uchar hdr[MAX_PROXY_HEADER_LEN]; + size_t pos= 0; + + DBUG_ASSERT(!net->compress); + const uchar *preread_bytes= net->buff + net->where_b; + bool have_v1_header= !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE); + bool have_v2_header= + !have_v1_header && !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE); + if (!have_v1_header && !have_v2_header) + { + // not a proxy protocol header + return -1; + } + memcpy(hdr, preread_bytes, NET_HEADER_SIZE); + pos= NET_HEADER_SIZE; + Vio *vio= net->vio; + memset(peer_info, 0, sizeof (*peer_info)); + + if (have_v1_header) + { + /* Read until end of header (newline character)*/ + while(pos < sizeof(hdr)) + { + long len= (long)vio_read(vio, hdr + pos, 1); + if (len < 0) + return -1; + pos++; + if (hdr[pos-1] == '\n') + break; + } + hdr[pos]= 0; + + if (parse_v1_header((char *)hdr, pos, peer_info)) + return -1; + } + else // if (have_v2_header) + { +#define PROXY_V2_HEADER_LEN 16 + /* read off 16 bytes of the header.*/ + ssize_t len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos); + if (len < 0) + return -1; + // 2 last bytes are the length in network byte order of the part following header + ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] >> 8) + hdr[PROXY_V2_HEADER_LEN-1]; + if (trail_len > sizeof(hdr) - PROXY_V2_HEADER_LEN) + return -1; + if (trail_len > 0) + { + len= vio_read(vio, hdr + PROXY_V2_HEADER_LEN, trail_len); + if (len < 0) + return -1; + } + pos= PROXY_V2_HEADER_LEN + trail_len; + if (parse_v2_header(hdr, pos, peer_info)) + return -1; + } + + if (peer_info->peer_addr.ss_family == AF_INET6) + { + /* + Normalize IPv4 compatible or mapped IPv6 addresses. + They will be treated as IPv4. + */ + sockaddr_storage tmp; + memset(&tmp, 0, sizeof(tmp)); + vio_get_normalized_ip((const struct sockaddr *)&peer_info->peer_addr, + sizeof(sockaddr_storage), (struct sockaddr *)&tmp); + memcpy(&peer_info->peer_addr, &tmp, sizeof(tmp)); + } + return 0; +} + + +/** + CIDR address matching etc (for the proxy_protocol_networks parameter) +*/ + +/** + Subnetwork address in CIDR format, e.g + 192.168.1.0/24 or 2001:db8::/32 +*/ +struct subnet +{ + char addr[16]; /* Binary representation of the address, big endian*/ + unsigned short family; /* Address family, AF_INET or AF_INET6 */ + unsigned short bits; /* subnetwork size */ +}; + +static subnet* proxy_protocol_subnets; +size_t proxy_protocol_subnet_count; + +#define MAX_MASK_BITS(family) (family == AF_INET ? 32 : 128) + + +/** Convert IPv4 that are compat or mapped IPv4 to "normal" IPv4 */ +static int normalize_subnet(struct subnet *subnet) +{ + unsigned char *addr= (unsigned char*)subnet->addr; + if (subnet->family == AF_INET6) + { + const struct in6_addr *src_ip6=(in6_addr *)addr; + if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6)) + { + /* Copy the actual IPv4 address (4 last bytes) */ + if (subnet->bits < 96) + return -1; + subnet->family= AF_INET; + memcpy(addr, addr+12, 4); + subnet->bits -= 96; + } + } + return 0; +} + +/** + Convert string representation of a subnet to subnet struct. +*/ +static int parse_subnet(char *addr_str, struct subnet *subnet) +{ + if (strchr(addr_str, ':')) + subnet->family= AF_INET6; + else if (strchr(addr_str, '.')) + subnet->family= AF_INET; + else if (!strcmp(addr_str, "localhost")) + { + subnet->family= AF_UNIX; + subnet->bits= 0; + return 0; + } + + char *pmask= strchr(addr_str, '/'); + if (!pmask) + { + subnet->bits= MAX_MASK_BITS(subnet->family); + } + else + { + *pmask= 0; + pmask++; + int b= 0; + + do + { + if (*pmask < '0' || *pmask > '9') + return -1; + b= 10 * b + *pmask - '0'; + if (b > MAX_MASK_BITS(subnet->family)) + return -1; + pmask++; + } + while (*pmask); + + subnet->bits= (unsigned short)b; + } + + if (!inet_pton(subnet->family, addr_str, subnet->addr)) + return -1; + + if (normalize_subnet(subnet)) + return -1; + + return 0; +} + +/** + Parse comma separated string subnet list into subnets array, + which is stored in 'proxy_protocol_subnets' variable + + @param[in] subnets_str : networks in CIDR format, + separated by comma and/or space + + @return 0 if success, otherwise -1 +*/ +int set_proxy_protocol_networks(const char *subnets_str) +{ + if (!subnets_str || !*subnets_str) + return 0; + + size_t max_subnets= MY_MAX(3,strlen(subnets_str)/2); + proxy_protocol_subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL); + + /* Check for special case '*'. */ + if (strcmp(subnets_str, "*") == 0) + { + + proxy_protocol_subnets[0].family= AF_INET; + proxy_protocol_subnets[1].family= AF_INET6; + proxy_protocol_subnets[2].family= AF_UNIX; + proxy_protocol_subnet_count= 3; + return 0; + } + + char token[256]; + const char *p= subnets_str; + for(proxy_protocol_subnet_count= 0;; proxy_protocol_subnet_count++) + { + while(*p && (*p ==',' || *p == ' ')) + p++; + if (!*p) + break; + + size_t cnt= 0; + while(*p && *p != ',' && *p != ' ' && cnt < sizeof(token)-1) + token[cnt++]= *p++; + + token[cnt++]=0; + if (cnt == sizeof(token)) + return -1; + + if (parse_subnet(token, &proxy_protocol_subnets[proxy_protocol_subnet_count])) + { + sql_print_error("Error parsing proxy_protocol_networks parameter, near '%s'",token); + return -1; + } + } + return 0; +} + +/** + Compare memory areas, in memcmp().similar fashion. + The difference to memcmp() is that size parameter is the + bit count, not byte count. +*/ +static int compare_bits(const void *s1, const void *s2, int bit_count) +{ + int result= 0; + int byte_count= bit_count / 8; + if (byte_count && (result= memcmp(s1, s2, byte_count))) + return result; + int rem= byte_count % 8; + if (rem) + { + // compare remaining bits i.e partial bytes. + unsigned char s1_bits= (((char *)s1)[byte_count]) >> (8 - rem); + unsigned char s2_bits= (((char *)s2)[byte_count]) >> (8 - rem); + if (s1_bits > s2_bits) + return 1; + if (s1_bits < s2_bits) + return -1; + } + return 0; +} + +/** + Check whether networks address matches network. +*/ +bool addr_matches_subnet(const sockaddr *sock_addr, const subnet *subnet) +{ + DBUG_ASSERT(subnet->family == AF_UNIX || + subnet->family == AF_INET || + subnet->family == AF_INET6); + + if (sock_addr->sa_family != subnet->family) + return false; + + if (subnet->family == AF_UNIX) + return true; + + void *addr= (subnet->family == AF_INET) ? + (void *)&((struct sockaddr_in *)sock_addr)->sin_addr : + (void *)&((struct sockaddr_in6 *)sock_addr)->sin6_addr; + + return (compare_bits(subnet->addr, addr, subnet->bits) == 0); +} + + +/** + Check whether proxy header from client is allowed, as per + specification in 'proxy_protocol_networks' server variable. + + The non-TCP "localhost" clients (unix socket, shared memory, pipes) + are accepted whenever 127.0.0.1 accepted in 'proxy_protocol_networks' +*/ +bool is_proxy_protocol_allowed(const sockaddr *addr) +{ + if (proxy_protocol_subnet_count == 0) + return false; + + sockaddr_storage addr_storage; + struct sockaddr *normalized_addr= (struct sockaddr *)&addr_storage; + + /* + Non-TCP addresses (unix domain socket, windows pipe and shared memory + gets tranlated to TCP4 localhost address. + + Note, that vio remote addresses are initialized with binary zeros + for these protocols (which is AF_UNSPEC everywhere). + */ + switch(addr->sa_family) + { + case AF_UNSPEC: + case AF_UNIX: + normalized_addr->sa_family= AF_UNIX; + break; + case AF_INET: + case AF_INET6: + { + size_t len= + (addr->sa_family == AF_INET)?sizeof(sockaddr_in):sizeof (sockaddr_in6); + vio_get_normalized_ip(addr, len,normalized_addr); + } + break; + default: + DBUG_ASSERT(0); + } + + for (size_t i= 0; i < proxy_protocol_subnet_count; i++) + if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i])) + return true; + + return false; +} + + +void cleanup_proxy_protocol_networks() +{ + my_free(proxy_protocol_subnets); + proxy_protocol_subnets= 0; + proxy_protocol_subnet_count= 0; +} + diff --git a/sql/proxy_protocol.h b/sql/proxy_protocol.h new file mode 100644 index 00000000000..6f9bcf73307 --- /dev/null +++ b/sql/proxy_protocol.h @@ -0,0 +1,15 @@ +#include "my_net.h" + +struct proxy_peer_info +{ + struct sockaddr_storage peer_addr; + int port; + bool is_local_command; +}; + +extern bool has_proxy_protocol_header(NET *net); +extern int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info); +extern bool is_proxy_protocol_allowed(const sockaddr *remote_addr); + +extern int set_proxy_protocol_networks(const char *spec); +extern void cleanup_proxy_protocol_networks(); diff --git a/sql/records.cc b/sql/records.cc index f16bdcff6e6..ac84ca84ab6 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -26,7 +26,7 @@ Functions for easy reading of records, possible through a cache */ -#include <my_global.h> +#include "mariadb.h" #include "records.h" #include "sql_priv.h" #include "records.h" @@ -89,8 +89,8 @@ bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, table->file->print_error(error, MYF(0)); } - /* read_record will be changed to rr_index in rr_index_first */ - info->read_record= reverse ? rr_index_last : rr_index_first; + /* read_record_func will be changed to rr_index in rr_index_first */ + info->read_record_func= reverse ? rr_index_last : rr_index_first; DBUG_RETURN(error != 0); } @@ -205,14 +205,14 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (addon_field) { info->rec_buf= (uchar*) filesort->addon_buf.str; - info->ref_length= filesort->addon_buf.length; + info->ref_length= (uint)filesort->addon_buf.length; info->unpack= filesort->unpack; } else { empty_record(table); info->record= table->record[0]; - info->ref_length= table->file->ref_length; + info->ref_length= (uint)table->file->ref_length; } info->select=select; info->print_error=print_error; @@ -229,8 +229,8 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (tempfile && !(select && select->quick)) { DBUG_PRINT("info",("using rr_from_tempfile")); - info->read_record= (addon_field ? - rr_unpack_from_tempfile : rr_from_tempfile); + info->read_record_func= + addon_field ? rr_unpack_from_tempfile : rr_from_tempfile; info->io_cache= tempfile; reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0); info->ref_pos=table->file->ref; @@ -260,14 +260,14 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (! init_rr_cache(thd, info)) { DBUG_PRINT("info",("using rr_from_cache")); - info->read_record=rr_from_cache; + info->read_record_func= rr_from_cache; } } } else if (select && select->quick) { DBUG_PRINT("info",("using rr_quick")); - info->read_record=rr_quick; + info->read_record_func= rr_quick; } else if (filesort && filesort->record_pointers) { @@ -277,13 +277,13 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->cache_pos= filesort->record_pointers; info->cache_end= (info->cache_pos+ filesort->return_rows * info->ref_length); - info->read_record= (addon_field ? - rr_unpack_from_buffer : rr_from_pointers); + info->read_record_func= + addon_field ? rr_unpack_from_buffer : rr_from_pointers; } else if (table->file->keyread_enabled()) { int error; - info->read_record= rr_index_first; + info->read_record_func= rr_index_first; if (!table->file->inited && (error= table->file->ha_index_init(table->file->keyread, 1))) { @@ -295,7 +295,7 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, else { DBUG_PRINT("info",("using rr_sequential")); - info->read_record=rr_sequential; + info->read_record_func= rr_sequential; if (table->file->ha_rnd_init_with_error(1)) DBUG_RETURN(1); /* We can use record cache if we don't update dynamic length tables */ @@ -331,7 +331,7 @@ void end_read_record(READ_RECORD *info) { if (info->table->is_created()) (void) info->table->file->extra(HA_EXTRA_NO_CACHE); - if (info->read_record != rr_quick) // otherwise quick_range does it + if (info->read_record_func != rr_quick) // otherwise quick_range does it (void) info->table->file->ha_index_or_rnd_end(); info->table=0; } @@ -399,7 +399,7 @@ static int rr_index_first(READ_RECORD *info) } tmp= info->table->file->ha_index_first(info->record); - info->read_record= rr_index; + info->read_record_func= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -422,7 +422,7 @@ static int rr_index_first(READ_RECORD *info) static int rr_index_last(READ_RECORD *info) { int tmp= info->table->file->ha_index_last(info->record); - info->read_record= rr_index_desc; + info->read_record_func= rr_index_desc; if (tmp) tmp= rr_handle_error(info, tmp); return tmp; diff --git a/sql/records.h b/sql/records.h index 473cb610be5..940c88ca0c7 100644 --- a/sql/records.h +++ b/sql/records.h @@ -55,7 +55,7 @@ struct READ_RECORD //handler *file; TABLE **forms; /* head and ref forms */ Unlock_row_func unlock_row; - Read_func read_record; + Read_func read_record_func; THD *thd; SQL_SELECT *select; uint cache_records; @@ -70,6 +70,8 @@ struct READ_RECORD bool print_error, ignore_not_found_rows; void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); + int read_record() { return read_record_func(this); } + /* SJ-Materialization runtime may need to read fields from the materialized table and unpack them into original table fields: diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 4cf7df5227f..68c7158e9e5 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -24,7 +24,7 @@ functions like register_slave()) are working. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // check_access #ifdef HAVE_REPLICATION @@ -115,7 +115,7 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex) 1 Error. Error message sent to client */ -int register_slave(THD* thd, uchar* packet, uint packet_length) +int register_slave(THD* thd, uchar* packet, size_t packet_length) { int res; SLAVE_INFO *si; diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index 2cc031a462d..967d81bcf0d 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -19,7 +19,7 @@ #ifdef HAVE_REPLICATION #include "mysql.h" -#include "my_sys.h" +#include <my_sys.h> #include "slave.h" typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE, @@ -41,7 +41,7 @@ extern HASH slave_list; bool show_slave_hosts(THD* thd); void init_slave_list(); void end_slave_list(); -int register_slave(THD* thd, uchar* packet, uint packet_length); +int register_slave(THD* thd, uchar* packet, size_t packet_length); void unregister_slave(THD* thd, bool only_mine, bool need_mutex); #endif /* HAVE_REPLICATION */ diff --git a/sql/replication.h b/sql/replication.h index 4731c2246ef..d8672310110 100644 --- a/sql/replication.h +++ b/sql/replication.h @@ -18,16 +18,14 @@ /*************************************************************************** NOTE: plugin locking. - This API was created specifically for the semisync plugin and its locking - logic is also matches semisync plugin usage pattern. In particular, a plugin - is locked on Binlog_transmit_observer::transmit_start and is unlocked after - Binlog_transmit_observer::transmit_stop. All other master observable events - happen between these two and don't lock the plugin at all. This works well - for the semisync_master plugin. + + The plugin is locked on Binlog_transmit_observer::transmit_start and is + unlocked after Binlog_transmit_observer::transmit_stop. All other + master observable events happen between these two and don't lock the + plugin at all. Also a plugin is locked on Binlog_relay_IO_observer::thread_start - and unlocked after Binlog_relay_IO_observer::thread_stop. This works well for - the semisync_slave plugin. + and unlocked after Binlog_relay_IO_observer::thread_stop. ***************************************************************************/ #include <mysql.h> diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index 0ab4c62235f..3fda2761430 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "mysqld.h" // system_charset_info #include "rpl_filter.h" @@ -105,9 +105,9 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables) if (!tables->updating) continue; some_tables_updating= 1; - end= strmov(hash_key, tables->db ? tables->db : db); + end= strmov(hash_key, tables->db.str ? tables->db.str : db); *end++= '.'; - len= (uint) (strmov(end, tables->table_name) - hash_key); + len= (uint) (strmov(end, tables->table_name.str) - hash_key); if (do_table_inited) // if there are any do's { if (my_hash_search(&do_table, (uchar*) hash_key, len)) diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index bb0f6bf2233..d7e43a8fb39 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -16,24 +16,24 @@ /* Definitions for MariaDB global transaction ID (GTID). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" -#include "my_sys.h" #include "unireg.h" -#include "my_global.h" +#include "mariadb.h" #include "sql_base.h" #include "sql_parse.h" #include "key.h" #include "rpl_gtid.h" #include "rpl_rli.h" +#include "slave.h" #include "log_event.h" -const LEX_STRING rpl_gtid_slave_state_table_name= - { C_STRING_WITH_LEN("gtid_slave_pos") }; +const LEX_CSTRING rpl_gtid_slave_state_table_name= + { STRING_WITH_LEN("gtid_slave_pos") }; void -rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, +rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton, rpl_group_info *rgi) { int err; @@ -45,7 +45,7 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, it is even committed. */ mysql_mutex_lock(&LOCK_slave_state); - err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi); + err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, hton, rgi); mysql_mutex_unlock(&LOCK_slave_state); if (err) { @@ -74,12 +74,14 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi) if (rgi->gtid_pending) { uint64 sub_id= rgi->gtid_sub_id; + void *hton= NULL; + 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)) + if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false, &hton)) DBUG_RETURN(1); - update_state_hash(sub_id, &rgi->current_gtid, rgi); + update_state_hash(sub_id, &rgi->current_gtid, hton, rgi); } rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL; } @@ -243,7 +245,7 @@ rpl_slave_state_free_element(void *arg) rpl_slave_state::rpl_slave_state() - : last_sub_id(0), loaded(false) + : last_sub_id(0), gtid_pos_tables(0), loaded(false) { mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state, MY_MUTEX_INIT_SLOW); @@ -255,6 +257,7 @@ rpl_slave_state::rpl_slave_state() rpl_slave_state::~rpl_slave_state() { + free_gtid_pos_tables((struct gtid_pos_table *)gtid_pos_tables); truncate_hash(); my_hash_free(&hash); delete_dynamic(>id_sort_array); @@ -286,11 +289,12 @@ rpl_slave_state::truncate_hash() int rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, - uint64 seq_no, rpl_group_info *rgi) + uint64 seq_no, void *hton, rpl_group_info *rgi) { element *elem= NULL; list_element *list_elem= NULL; + DBUG_ASSERT(hton || !loaded); if (!(elem= get_element(domain_id))) return 1; @@ -313,7 +317,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, { if (rgi->gtid_ignore_duplicate_state==rpl_group_info::GTID_DUPLICATE_OWNER) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS Relay_log_info *rli= rgi->rli; #endif uint32 count= elem->owner_count; @@ -335,6 +339,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id, list_elem->server_id= server_id; list_elem->sub_id= sub_id; list_elem->seq_no= seq_no; + list_elem->hton= hton; elem->add(list_elem); if (last_sub_id < sub_id) @@ -396,10 +401,7 @@ rpl_slave_state::truncate_state_table(THD *thd) int err= 0; tmp_disable_binlog(thd); - tlist.init_one_table(STRING_WITH_LEN("mysql"), - rpl_gtid_slave_state_table_name.str, - rpl_gtid_slave_state_table_name.length, - NULL, TL_WRITE); + 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))) { err= tlist.table->file->ha_truncate(); @@ -425,17 +427,17 @@ rpl_slave_state::truncate_state_table(THD *thd) static const TABLE_FIELD_TYPE mysql_rpl_slave_state_coltypes[4]= { - { { C_STRING_WITH_LEN("domain_id") }, - { C_STRING_WITH_LEN("int(10) unsigned") }, + { { STRING_WITH_LEN("domain_id") }, + { STRING_WITH_LEN("int(10) unsigned") }, {NULL, 0} }, - { { C_STRING_WITH_LEN("sub_id") }, - { C_STRING_WITH_LEN("bigint(20) unsigned") }, + { { STRING_WITH_LEN("sub_id") }, + { STRING_WITH_LEN("bigint(20) unsigned") }, {NULL, 0} }, - { { C_STRING_WITH_LEN("server_id") }, - { C_STRING_WITH_LEN("int(10) unsigned") }, + { { STRING_WITH_LEN("server_id") }, + { STRING_WITH_LEN("int(10) unsigned") }, {NULL, 0} }, - { { C_STRING_WITH_LEN("seq_no") }, - { C_STRING_WITH_LEN("bigint(20) unsigned") }, + { { STRING_WITH_LEN("seq_no") }, + { STRING_WITH_LEN("bigint(20) unsigned") }, {NULL, 0} }, }; @@ -466,6 +468,94 @@ gtid_check_rpl_slave_state_table(TABLE *table) /* + Attempt to find a mysql.gtid_slave_posXXX table that has a storage engine + that is already in use by the current transaction, if any. +*/ +void +rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename) +{ + struct gtid_pos_table *list, *table_entry, *default_entry; + + /* + See comments on rpl_slave_state::gtid_pos_tables for rules around proper + access to the list. + */ + list= (struct gtid_pos_table *) + my_atomic_loadptr_explicit(>id_pos_tables, MY_MEMORY_ORDER_ACQUIRE); + + Ha_trx_info *ha_info; + uint count = 0; + for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next()) + { + void *trx_hton= ha_info->ht(); + table_entry= list; + + if (!ha_info->is_trx_read_write() || trx_hton == binlog_hton) + continue; + while (table_entry) + { + if (table_entry->table_hton == trx_hton) + { + if (likely(table_entry->state == GTID_POS_AVAILABLE)) + { + *out_tablename= table_entry->table_name; + /* + Check if this is a cross-engine transaction, so we can correctly + maintain the rpl_transactions_multi_engine status variable. + */ + if (count >= 1) + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + else + { + for (;;) + { + ha_info= ha_info->next(); + if (!ha_info) + break; + if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton) + { + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + break; + } + } + } + return; + } + /* + This engine is marked to automatically create the table. + We cannot easily do this here (possibly in the middle of a + transaction). But we can request the slave background thread + to create it, and in a short while it should become available + for following transactions. + */ +#ifdef HAVE_REPLICATION + slave_background_gtid_pos_create_request(table_entry); +#endif + break; + } + table_entry= table_entry->next; + } + ++count; + } + /* + If we cannot find any table whose engine matches an engine that is + already active in the transaction, or if there is no current transaction + engines available, we return the default gtid_slave_pos table. + */ + default_entry= (struct gtid_pos_table *) + my_atomic_loadptr_explicit(&default_gtid_pos_table, MY_MEMORY_ORDER_ACQUIRE); + *out_tablename= default_entry->table_name; + /* Record in status that we failed to find a suitable gtid_pos table. */ + if (count > 0) + { + statistic_increment(transactions_gtid_foreign_engine, LOCK_status); + if (count > 1) + statistic_increment(rpl_transactions_multi_engine, LOCK_status); + } +} + + +/* 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 @@ -481,19 +571,24 @@ gtid_check_rpl_slave_state_table(TABLE *table) */ int rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement) + bool in_transaction, bool in_statement, + void **out_hton) { TABLE_LIST tlist; - int err= 0; + int err= 0, not_sql_thread; bool table_opened= false; TABLE *table; - list_element *elist= 0, *next; + list_element *delete_list= 0, *next, *cur, **next_ptr_ptr, **best_ptr_ptr; + uint64 best_sub_id; element *elem; ulonglong thd_saved_option= thd->variables.option_bits; Query_tables_list lex_backup; wait_for_commit* suspended_wfc; + void *hton= NULL; + LEX_CSTRING gtid_pos_table_name; DBUG_ENTER("record_gtid"); + *out_hton= NULL; if (unlikely(!loaded)) { /* @@ -509,6 +604,25 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, if (!in_statement) thd->reset_for_next_command(); + /* + Only the SQL thread can call select_gtid_pos_table without a mutex + Other threads needs to use a mutex and take into account that the + result may change during execution, so we have to make a copy. + */ + + if ((not_sql_thread= (thd->system_thread != SYSTEM_THREAD_SLAVE_SQL))) + mysql_mutex_lock(&LOCK_slave_state); + select_gtid_pos_table(thd, >id_pos_table_name); + if (not_sql_thread) + { + LEX_CSTRING *tmp= thd->make_clex_string(gtid_pos_table_name.str, + gtid_pos_table_name.length); + mysql_mutex_unlock(&LOCK_slave_state); + if (!tmp) + DBUG_RETURN(1); + gtid_pos_table_name= *tmp; + } + DBUG_EXECUTE_IF("gtid_inject_record_gtid", { my_error(ER_CANNOT_UPDATE_GTID_STATE, MYF(0)); @@ -538,14 +652,12 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, */ suspended_wfc= thd->suspend_subsequent_commits(); thd->lex->reset_n_backup_query_tables_list(&lex_backup); - tlist.init_one_table(STRING_WITH_LEN("mysql"), - rpl_gtid_slave_state_table_name.str, - rpl_gtid_slave_state_table_name.length, - NULL, TL_WRITE); + tlist.init_one_table(&MYSQL_SCHEMA_NAME, >id_pos_table_name, NULL, TL_WRITE); if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) goto end; table_opened= true; table= tlist.table; + hton= table->s->db_type(); if ((err= gtid_check_rpl_slave_state_table(table))) goto end; @@ -581,6 +693,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->file->print_error(err, MYF(0)); goto end; } + *out_hton= hton; if(opt_bin_log && (err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id, @@ -598,36 +711,62 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, err= 1; goto end; } - if ((elist= elem->grab_list()) != NULL) + + /* Now pull out all GTIDs that were recorded in this engine. */ + delete_list = NULL; + next_ptr_ptr= &elem->list; + cur= elem->list; + best_sub_id= 0; + best_ptr_ptr= NULL; + while (cur) { - /* Delete any old stuff, but keep around the most recent one. */ - list_element *cur= elist; - uint64 best_sub_id= cur->sub_id; - list_element **best_ptr_ptr= &elist; - while ((next= cur->next)) + list_element *next= cur->next; + if (cur->hton == hton) { - if (next->sub_id > best_sub_id) + /* Belongs to same engine, so move it to the delete list. */ + cur->next= delete_list; + delete_list= cur; + if (cur->sub_id > best_sub_id) { - best_sub_id= next->sub_id; + best_sub_id= cur->sub_id; + best_ptr_ptr= &delete_list; + } + else if (best_ptr_ptr == &delete_list) best_ptr_ptr= &cur->next; + } + else + { + /* Another engine, leave it in the list. */ + if (cur->sub_id > best_sub_id) + { + best_sub_id= cur->sub_id; + /* Current best is not on the delete list. */ + best_ptr_ptr= NULL; } - cur= next; + *next_ptr_ptr= cur; + next_ptr_ptr= &cur->next; } - /* - Delete the highest sub_id element from the old list, and put it back as - the single-element new list. - */ + cur= next; + } + *next_ptr_ptr= NULL; + /* + If the highest sub_id element is on the delete list, put it back on the + original list, to preserve the highest sub_id element in the table for + GTID position recovery. + */ + if (best_ptr_ptr) + { cur= *best_ptr_ptr; *best_ptr_ptr= cur->next; - cur->next= NULL; + cur->next= elem->list; elem->list= cur; } mysql_mutex_unlock(&LOCK_slave_state); - if (!elist) + if (!delete_list) goto end; - /* Now delete any already committed rows. */ + /* Now delete any already committed GTIDs. */ bitmap_set_bit(table->read_set, table->field[0]->field_index); bitmap_set_bit(table->read_set, table->field[1]->field_index); @@ -636,7 +775,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->file->print_error(err, MYF(0)); goto end; } - while (elist) + while (delete_list) { uchar key_buffer[4+8]; @@ -646,9 +785,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= elist->next; + next= delete_list->next; - table->field[1]->store(elist->sub_id, true); + table->field[1]->store(delete_list->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, @@ -662,8 +801,8 @@ 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(elist); - elist= next; + my_free(delete_list); + delete_list= next; if (err) break; } @@ -681,13 +820,13 @@ end: if (err || (err= ha_commit_trans(thd, FALSE))) { /* - If error, we need to put any remaining elist back into the HASH so we - can do another delete attempt later. + If error, we need to put any remaining delete_list back into the HASH + so we can do another delete attempt later. */ - if (elist) + if (delete_list) { mysql_mutex_lock(&LOCK_slave_state); - put_back_list(gtid->domain_id, elist); + put_back_list(gtid->domain_id, delete_list); mysql_mutex_unlock(&LOCK_slave_state); } @@ -1077,11 +1216,12 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len, { rpl_gtid gtid; uint64 sub_id; + void *hton= NULL; 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) || - update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL)) + record_gtid(thd, >id, sub_id, false, in_statement, &hton) || + update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL)) return 1; if (state_from_master == end) break; @@ -1115,6 +1255,75 @@ rpl_slave_state::is_empty() } +void +rpl_slave_state::free_gtid_pos_tables(struct rpl_slave_state::gtid_pos_table *list) +{ + struct gtid_pos_table *cur, *next; + + cur= list; + while (cur) + { + next= cur->next; + my_free(cur); + cur= next; + } +} + + +/* + Replace the list of available mysql.gtid_slave_posXXX tables with a new list. + The caller must be holding LOCK_slave_state. Additionally, this function + must only be called while all SQL threads are stopped. +*/ +void +rpl_slave_state::set_gtid_pos_tables_list(rpl_slave_state::gtid_pos_table *new_list, + rpl_slave_state::gtid_pos_table *default_entry) +{ + gtid_pos_table *old_list; + + mysql_mutex_assert_owner(&LOCK_slave_state); + old_list= (struct gtid_pos_table *)gtid_pos_tables; + my_atomic_storeptr_explicit(>id_pos_tables, new_list, MY_MEMORY_ORDER_RELEASE); + my_atomic_storeptr_explicit(&default_gtid_pos_table, default_entry, + MY_MEMORY_ORDER_RELEASE); + free_gtid_pos_tables(old_list); +} + + +void +rpl_slave_state::add_gtid_pos_table(rpl_slave_state::gtid_pos_table *entry) +{ + mysql_mutex_assert_owner(&LOCK_slave_state); + entry->next= (struct gtid_pos_table *)gtid_pos_tables; + my_atomic_storeptr_explicit(>id_pos_tables, entry, MY_MEMORY_ORDER_RELEASE); +} + + +struct rpl_slave_state::gtid_pos_table * +rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton, + rpl_slave_state::gtid_pos_table_state state) +{ + struct gtid_pos_table *p; + char *allocated_str; + + if (!my_multi_malloc(MYF(MY_WME), + &p, sizeof(*p), + &allocated_str, table_name->length+1, + NULL)) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*p) + table_name->length+1)); + return NULL; + } + memcpy(allocated_str, table_name->str, table_name->length+1); // Also copy '\0' + p->next = NULL; + p->table_hton= hton; + p->table_name.str= allocated_str; + p->table_name.length= table_name->length; + p->state= state; + return p; +} + + void rpl_binlog_state::init() { my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id), @@ -2046,15 +2255,14 @@ void slave_connection_state::remove(const rpl_gtid *in_gtid) { uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool err; rpl_gtid *slave_gtid= &((entry *)rec)->gtid; DBUG_ASSERT(rec /* We should never try to remove not present domain_id. */); DBUG_ASSERT(slave_gtid->server_id == in_gtid->server_id); DBUG_ASSERT(slave_gtid->seq_no == in_gtid->seq_no); + err= #endif - - IF_DBUG(err=, ) my_hash_delete(&hash, rec); DBUG_ASSERT(!err); } diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 19ff0f3f977..604289acd6b 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -23,7 +23,7 @@ /* Definitions for MariaDB global transaction ID (GTID). */ -extern const LEX_STRING rpl_gtid_slave_state_table_name; +extern const LEX_CSTRING rpl_gtid_slave_state_table_name; class String; @@ -120,6 +120,12 @@ struct rpl_slave_state uint64 sub_id; uint64 seq_no; uint32 server_id; + /* + hton of mysql.gtid_slave_pos* table used to record this GTID. + Can be NULL if the gtid table failed to load (eg. missing + mysql.gtid_slave_pos table following an upgrade). + */ + void *hton; }; /* Elements in the HASH that hold the state for one domain_id. */ @@ -163,6 +169,26 @@ struct rpl_slave_state } }; + /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */ + enum gtid_pos_table_state { + GTID_POS_AUTO_CREATE, + GTID_POS_CREATE_REQUESTED, + GTID_POS_CREATE_IN_PROGRESS, + GTID_POS_AVAILABLE + }; + struct gtid_pos_table { + struct gtid_pos_table *next; + /* + Use a void * here, rather than handlerton *, to make explicit that we + are not using the value to access any functionality in the engine. It + is just used as an opaque value to identify which engine we are using + for each GTID row. + */ + void *table_hton; + LEX_CSTRING table_name; + uint8 state; + }; + /* Mapping from domain_id to its element. */ HASH hash; /* Mutex protecting access to the state. */ @@ -171,6 +197,30 @@ struct rpl_slave_state DYNAMIC_ARRAY gtid_sort_array; uint64 last_sub_id; + /* + List of tables available for durably storing the slave GTID position. + + Accesses to this table is protected by LOCK_slave_state. However for + efficiency, there is also a provision for read access to it from a running + slave without lock. + + An element can be added at the head of a list by storing the new + gtid_pos_tables pointer atomically with release semantics, to ensure that + the next pointer of the new element is visible to readers of the new list. + Other changes (like deleting or replacing elements) must happen only while + all SQL driver threads are stopped. LOCK_slave_state must be held in any + case. + + The list can be read without lock by an SQL driver thread or worker thread + by reading the gtid_pos_tables pointer atomically with acquire semantics, + to ensure that it will see the correct next pointer of a new head element. + + The type is struct gtid_pos_table *, but needs to be void * to allow using + my_atomic operations without violating C strict aliasing semantics. + */ + void * volatile gtid_pos_tables; + /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */ + void * volatile default_gtid_pos_table; bool loaded; rpl_slave_state(); @@ -179,10 +229,11 @@ struct rpl_slave_state void truncate_hash(); ulong count() const { return hash.records; } int update(uint32 domain_id, uint32 server_id, uint64 sub_id, - uint64 seq_no, rpl_group_info *rgi); + uint64 seq_no, void *hton, rpl_group_info *rgi); 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); + bool in_transaction, 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, @@ -196,10 +247,17 @@ struct rpl_slave_state element *get_element(uint32 domain_id); int put_back_list(uint32 domain_id, list_element *list); - void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi); + void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton, + rpl_group_info *rgi); int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi); int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi); void release_domain_owner(rpl_group_info *rgi); + void set_gtid_pos_tables_list(gtid_pos_table *new_list, + gtid_pos_table *default_entry); + void add_gtid_pos_table(gtid_pos_table *entry); + struct gtid_pos_table *alloc_gtid_pos_table(LEX_CSTRING *table_name, + void *hton, rpl_slave_state::gtid_pos_table_state state); + void free_gtid_pos_tables(struct gtid_pos_table *list); }; diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc deleted file mode 100644 index 520fb61d8c4..00000000000 --- a/sql/rpl_handler.cc +++ /dev/null @@ -1,553 +0,0 @@ -/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - - 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 */ - -#include <my_global.h> -#include "sql_priv.h" -#include "unireg.h" - -#include "rpl_mi.h" -#include "sql_repl.h" -#include "log_event.h" -#include "rpl_filter.h" -#include <my_dir.h> -#include "rpl_handler.h" - -Trans_delegate *transaction_delegate; -Binlog_storage_delegate *binlog_storage_delegate; -#ifdef HAVE_REPLICATION -Binlog_transmit_delegate *binlog_transmit_delegate; -Binlog_relay_IO_delegate *binlog_relay_io_delegate; -#endif /* HAVE_REPLICATION */ - -/* - structure to save transaction log filename and position -*/ -typedef struct Trans_binlog_info { - my_off_t log_pos; - char log_file[FN_REFLEN]; -} Trans_binlog_info; - -int get_user_var_int(const char *name, - long long int *value, int *null_value) -{ - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - *value= entry->val_int(&null_val); - if (null_value) - *null_value= null_val; - return 0; -} - -int get_user_var_real(const char *name, - double *value, int *null_value) -{ - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - *value= entry->val_real(&null_val); - if (null_value) - *null_value= null_val; - return 0; -} - -int get_user_var_str(const char *name, char *value, - size_t len, unsigned int precision, int *null_value) -{ - String str; - bool null_val; - user_var_entry *entry= - (user_var_entry*) my_hash_search(¤t_thd->user_vars, - (uchar*) name, strlen(name)); - if (!entry) - return 1; - entry->val_str(&null_val, &str, precision); - strncpy(value, str.c_ptr(), len); - if (null_value) - *null_value= null_val; - return 0; -} - -int delegates_init() -{ - static my_aligned_storage<sizeof(Trans_delegate), MY_ALIGNOF(long)> trans_mem; - static my_aligned_storage<sizeof(Binlog_storage_delegate), - MY_ALIGNOF(long)> storage_mem; -#ifdef HAVE_REPLICATION - static my_aligned_storage<sizeof(Binlog_transmit_delegate), - MY_ALIGNOF(long)> transmit_mem; - static my_aligned_storage<sizeof(Binlog_relay_IO_delegate), - MY_ALIGNOF(long)> relay_io_mem; -#endif - - void *place_trans_mem= trans_mem.data; - void *place_storage_mem= storage_mem.data; - - transaction_delegate= new (place_trans_mem) Trans_delegate; - - if (!transaction_delegate->is_inited()) - { - sql_print_error("Initialization of transaction delegates failed. " - "Please report a bug."); - return 1; - } - - binlog_storage_delegate= new (place_storage_mem) Binlog_storage_delegate; - - if (!binlog_storage_delegate->is_inited()) - { - sql_print_error("Initialization binlog storage delegates failed. " - "Please report a bug."); - return 1; - } - -#ifdef HAVE_REPLICATION - void *place_transmit_mem= transmit_mem.data; - void *place_relay_io_mem= relay_io_mem.data; - - binlog_transmit_delegate= new (place_transmit_mem) Binlog_transmit_delegate; - - if (!binlog_transmit_delegate->is_inited()) - { - sql_print_error("Initialization of binlog transmit delegates failed. " - "Please report a bug."); - return 1; - } - - binlog_relay_io_delegate= new (place_relay_io_mem) Binlog_relay_IO_delegate; - - if (!binlog_relay_io_delegate->is_inited()) - { - sql_print_error("Initialization binlog relay IO delegates failed. " - "Please report a bug."); - return 1; - } -#endif - - return 0; -} - -void delegates_destroy() -{ - if (transaction_delegate) - transaction_delegate->~Trans_delegate(); - if (binlog_storage_delegate) - binlog_storage_delegate->~Binlog_storage_delegate(); -#ifdef HAVE_REPLICATION - if (binlog_transmit_delegate) - binlog_transmit_delegate->~Binlog_transmit_delegate(); - if (binlog_relay_io_delegate) - binlog_relay_io_delegate->~Binlog_relay_IO_delegate(); -#endif /* HAVE_REPLICATION */ -} - -/* - This macro is used by almost all the Delegate methods to iterate - over all the observers running given callback function of the - delegate. - */ -#define FOREACH_OBSERVER(r, f, do_lock, args) \ - param.server_id= thd->variables.server_id; \ - read_lock(); \ - Observer_info_iterator iter= observer_info_iter(); \ - Observer_info *info= iter++; \ - for (; info; info= iter++) \ - { \ - if (do_lock) plugin_lock(thd, plugin_int_to_ref(info->plugin_int)); \ - if (((Observer *)info->observer)->f \ - && ((Observer *)info->observer)->f args) \ - { \ - r= 1; \ - sql_print_error("Run function '" #f "' in plugin '%s' failed", \ - info->plugin_int->name.str); \ - break; \ - } \ - } \ - unlock(); - - -int Trans_delegate::after_commit(THD *thd, bool all) -{ - Trans_param param; - Trans_binlog_info *log_info; - bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - int ret= 0; - - param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - - log_info= thd->semisync_info; - - param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; - param.log_pos= log_info ? log_info->log_pos : 0; - - FOREACH_OBSERVER(ret, after_commit, false, (¶m)); - - /* - This is the end of a real transaction or autocommit statement, we - can mark the memory unused. - */ - if (is_real_trans && log_info) - { - log_info->log_file[0]= 0; - log_info->log_pos= 0; - } - return ret; -} - -int Trans_delegate::after_rollback(THD *thd, bool all) -{ - Trans_param param; - Trans_binlog_info *log_info; - bool is_real_trans= (all || thd->transaction.all.ha_list == 0); - int ret= 0; - - param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0; - - log_info= thd->semisync_info; - - param.log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; - param.log_pos= log_info ? log_info->log_pos : 0; - - FOREACH_OBSERVER(ret, after_rollback, false, (¶m)); - - /* - This is the end of a real transaction or autocommit statement, we - can mark the memory unused. - */ - if (is_real_trans && log_info) - { - log_info->log_file[0]= 0; - log_info->log_pos= 0; - } - return ret; -} - -int Binlog_storage_delegate::after_flush(THD *thd, - const char *log_file, - my_off_t log_pos, - bool synced, - bool first_in_group, - bool last_in_group) -{ - Binlog_storage_param param; - Trans_binlog_info *log_info; - uint32 flags=0; - int ret= 0; - - if (synced) - flags |= BINLOG_STORAGE_IS_SYNCED; - if (first_in_group) - flags|= BINLOG_GROUP_COMMIT_LEADER; - if (last_in_group) - flags|= BINLOG_GROUP_COMMIT_TRAILER; - - if (!(log_info= thd->semisync_info)) - { - if(!(log_info= - (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0)))) - return 1; - thd->semisync_info= log_info; - } - - strmake_buf(log_info->log_file, log_file+dirname_length(log_file)); - log_info->log_pos = log_pos; - - FOREACH_OBSERVER(ret, after_flush, false, - (¶m, log_info->log_file, log_info->log_pos, flags)); - return ret; -} - -int Binlog_storage_delegate::after_sync(THD *thd, - const char *log_file, - my_off_t log_pos, - bool first_in_group, - bool last_in_group) -{ - Binlog_storage_param param; - uint32 flags=0; - - if (first_in_group) - flags|= BINLOG_GROUP_COMMIT_LEADER; - if (last_in_group) - flags|= BINLOG_GROUP_COMMIT_TRAILER; - - int ret= 0; - FOREACH_OBSERVER(ret, after_sync, false, - (¶m, log_file+dirname_length(log_file), log_pos, flags)); - - return ret; -} - -#ifdef HAVE_REPLICATION -int Binlog_transmit_delegate::transmit_start(THD *thd, ushort flags, - const char *log_file, - my_off_t log_pos) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, transmit_start, true, (¶m, log_file, log_pos)); - return ret; -} - -int Binlog_transmit_delegate::transmit_stop(THD *thd, ushort flags) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, transmit_stop, false, (¶m)); - return ret; -} - -int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags, - String *packet) -{ - /* NOTE2ME: Maximum extra header size for each observer, I hope 32 - bytes should be enough for each Observer to reserve their extra - header. If later found this is not enough, we can increase this - /HEZX - */ -#define RESERVE_HEADER_SIZE 32 - unsigned char header[RESERVE_HEADER_SIZE]; - ulong hlen; - Binlog_transmit_param param; - param.flags= flags; - param.server_id= thd->variables.server_id; - - int ret= 0; - read_lock(); - Observer_info_iterator iter= observer_info_iter(); - Observer_info *info= iter++; - for (; info; info= iter++) - { - hlen= 0; - if (((Observer *)info->observer)->reserve_header - && ((Observer *)info->observer)->reserve_header(¶m, - header, - RESERVE_HEADER_SIZE, - &hlen)) - { - ret= 1; - break; - } - if (hlen == 0) - continue; - if (hlen > RESERVE_HEADER_SIZE || packet->append((char *)header, hlen)) - { - ret= 1; - break; - } - } - unlock(); - return ret; -} - -int Binlog_transmit_delegate::before_send_event(THD *thd, ushort flags, - String *packet, - const char *log_file, - my_off_t log_pos) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, before_send_event, false, - (¶m, (uchar *)packet->c_ptr(), - packet->length(), - log_file+dirname_length(log_file), log_pos)); - return ret; -} - -int Binlog_transmit_delegate::after_send_event(THD *thd, ushort flags, - String *packet) -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, after_send_event, false, - (¶m, packet->c_ptr(), packet->length())); - return ret; -} - -int Binlog_transmit_delegate::after_reset_master(THD *thd, ushort flags) - -{ - Binlog_transmit_param param; - param.flags= flags; - - int ret= 0; - FOREACH_OBSERVER(ret, after_reset_master, false, (¶m)); - return ret; -} - -void Binlog_relay_IO_delegate::init_param(Binlog_relay_IO_param *param, - Master_info *mi) -{ - param->mysql= mi->mysql; - param->user= mi->user; - param->host= mi->host; - param->port= mi->port; - param->master_log_name= mi->master_log_name; - param->master_log_pos= mi->master_log_pos; -} - -int Binlog_relay_IO_delegate::thread_start(THD *thd, Master_info *mi) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, thread_start, true, (¶m)); - return ret; -} - - -int Binlog_relay_IO_delegate::thread_stop(THD *thd, Master_info *mi) -{ - - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, thread_stop, false, (¶m)); - return ret; -} - -int Binlog_relay_IO_delegate::before_request_transmit(THD *thd, - Master_info *mi, - ushort flags) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, before_request_transmit, false, (¶m, (uint32)flags)); - return ret; -} - -int Binlog_relay_IO_delegate::after_read_event(THD *thd, Master_info *mi, - const char *packet, ulong len, - const char **event_buf, - ulong *event_len) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, after_read_event, false, - (¶m, packet, len, event_buf, event_len)); - return ret; -} - -int Binlog_relay_IO_delegate::after_queue_event(THD *thd, Master_info *mi, - const char *event_buf, - ulong event_len, - bool synced) -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - uint32 flags=0; - if (synced) - flags |= BINLOG_STORAGE_IS_SYNCED; - - int ret= 0; - FOREACH_OBSERVER(ret, after_queue_event, false, - (¶m, event_buf, event_len, flags)); - return ret; -} - -int Binlog_relay_IO_delegate::after_reset_slave(THD *thd, Master_info *mi) - -{ - Binlog_relay_IO_param param; - init_param(¶m, mi); - - int ret= 0; - FOREACH_OBSERVER(ret, after_reset_slave, false, (¶m)); - return ret; -} -#endif /* HAVE_REPLICATION */ - -int register_trans_observer(Trans_observer *observer, void *p) -{ - return transaction_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_trans_observer(Trans_observer *observer, void *p) -{ - return transaction_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -int register_binlog_storage_observer(Binlog_storage_observer *observer, void *p) -{ - return binlog_storage_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_storage_observer(Binlog_storage_observer *observer, void *p) -{ - return binlog_storage_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -#ifdef HAVE_REPLICATION -int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return binlog_transmit_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return binlog_transmit_delegate->remove_observer(observer, (st_plugin_int *)p); -} - -int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return binlog_relay_io_delegate->add_observer(observer, (st_plugin_int *)p); -} - -int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return binlog_relay_io_delegate->remove_observer(observer, (st_plugin_int *)p); -} -#else -int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return 0; -} - -int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p) -{ - return 0; -} - -int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return 0; -} - -int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p) -{ - return 0; -} -#endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h deleted file mode 100644 index 71550f0a577..00000000000 --- a/sql/rpl_handler.h +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. - - 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 RPL_HANDLER_H -#define RPL_HANDLER_H - -#include "sql_priv.h" -#include "rpl_mi.h" -#include "rpl_rli.h" -#include "sql_plugin.h" -#include "replication.h" - -class Observer_info { -public: - void *observer; - st_plugin_int *plugin_int; - - Observer_info(void *ob, st_plugin_int *p) - :observer(ob), plugin_int(p) - { } -}; - -class Delegate { -public: - typedef List<Observer_info> Observer_info_list; - typedef List_iterator<Observer_info> Observer_info_iterator; - - int add_observer(void *observer, st_plugin_int *plugin) - { - int ret= FALSE; - if (!inited) - return TRUE; - write_lock(); - Observer_info_iterator iter(observer_info_list); - Observer_info *info= iter++; - while (info && info->observer != observer) - info= iter++; - if (!info) - { - info= new Observer_info(observer, plugin); - if (!info || observer_info_list.push_back(info, &memroot)) - ret= TRUE; - } - else - ret= TRUE; - unlock(); - return ret; - } - - int remove_observer(void *observer, st_plugin_int *plugin) - { - int ret= FALSE; - if (!inited) - return TRUE; - write_lock(); - Observer_info_iterator iter(observer_info_list); - Observer_info *info= iter++; - while (info && info->observer != observer) - info= iter++; - if (info) - { - iter.remove(); - delete info; - } - else - ret= TRUE; - unlock(); - return ret; - } - - inline Observer_info_iterator observer_info_iter() - { - return Observer_info_iterator(observer_info_list); - } - - inline bool is_empty() - { - return observer_info_list.is_empty(); - } - - inline int read_lock() - { - if (!inited) - return TRUE; - return rw_rdlock(&lock); - } - - inline int write_lock() - { - if (!inited) - return TRUE; - return rw_wrlock(&lock); - } - - inline int unlock() - { - if (!inited) - return TRUE; - return rw_unlock(&lock); - } - - inline bool is_inited() - { - return inited; - } - - Delegate() - { - inited= FALSE; - if (my_rwlock_init(&lock, NULL)) - return; - init_sql_alloc(&memroot, "Delegate", 1024, 0, MYF(0)); - inited= TRUE; - } - ~Delegate() - { - inited= FALSE; - rwlock_destroy(&lock); - free_root(&memroot, MYF(0)); - } - -private: - Observer_info_list observer_info_list; - rw_lock_t lock; - MEM_ROOT memroot; - bool inited; -}; - -class Trans_delegate - :public Delegate { -public: - typedef Trans_observer Observer; - int before_commit(THD *thd, bool all); - int before_rollback(THD *thd, bool all); - int after_commit(THD *thd, bool all); - int after_rollback(THD *thd, bool all); -}; - -class Binlog_storage_delegate - :public Delegate { -public: - typedef Binlog_storage_observer Observer; - int after_flush(THD *thd, const char *log_file, - my_off_t log_pos, bool synced, - bool first_in_group, bool last_in_group); - int after_sync(THD *thd, const char *log_file, my_off_t log_pos, - bool first_in_group, bool last_in_group); -}; - -#ifdef HAVE_REPLICATION -class Binlog_transmit_delegate - :public Delegate { -public: - typedef Binlog_transmit_observer Observer; - int transmit_start(THD *thd, ushort flags, - const char *log_file, my_off_t log_pos); - int transmit_stop(THD *thd, ushort flags); - int reserve_header(THD *thd, ushort flags, String *packet); - int before_send_event(THD *thd, ushort flags, - String *packet, const - char *log_file, my_off_t log_pos ); - int after_send_event(THD *thd, ushort flags, - String *packet); - int after_reset_master(THD *thd, ushort flags); -}; - -class Binlog_relay_IO_delegate - :public Delegate { -public: - typedef Binlog_relay_IO_observer Observer; - int thread_start(THD *thd, Master_info *mi); - int thread_stop(THD *thd, Master_info *mi); - int before_request_transmit(THD *thd, Master_info *mi, ushort flags); - int after_read_event(THD *thd, Master_info *mi, - const char *packet, ulong len, - const char **event_buf, ulong *event_len); - int after_queue_event(THD *thd, Master_info *mi, - const char *event_buf, ulong event_len, - bool synced); - int after_reset_slave(THD *thd, Master_info *mi); -private: - void init_param(Binlog_relay_IO_param *param, Master_info *mi); -}; -#endif /* HAVE_REPLICATION */ - -int delegates_init(); -void delegates_destroy(); - -extern Trans_delegate *transaction_delegate; -extern Binlog_storage_delegate *binlog_storage_delegate; -#ifdef HAVE_REPLICATION -extern Binlog_transmit_delegate *binlog_transmit_delegate; -extern Binlog_relay_IO_delegate *binlog_relay_io_delegate; -#endif /* HAVE_REPLICATION */ - -/* - if there is no observers in the delegate, we can return 0 - immediately. -*/ -#define RUN_HOOK(group, hook, args) \ - (group ##_delegate->is_empty() ? \ - 0 : group ##_delegate->hook args) - -#endif /* RPL_HANDLER_H */ diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index bff0da26862..b855dec35f9 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_injector.h" #include "transaction.h" @@ -185,7 +185,8 @@ int injector::record_incident(THD *thd, Incident incident) return mysql_bin_log.rotate_and_purge(true); } -int injector::record_incident(THD *thd, Incident incident, LEX_STRING const message) +int injector::record_incident(THD *thd, Incident incident, + const LEX_CSTRING *message) { Incident_log_event ev(thd, incident, message); if (int error= mysql_bin_log.write(&ev)) diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 41e1fcf460c..bfb53a38d90 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -17,7 +17,6 @@ #define INJECTOR_H /* Pull in 'byte', 'my_off_t', and 'uint32' */ -#include <my_global.h> #include <my_bitmap.h> #include "rpl_constants.h" @@ -303,7 +302,7 @@ public: void new_trans(THD *, transaction *); int record_incident(THD*, Incident incident); - int record_incident(THD*, Incident incident, LEX_STRING const message); + int record_incident(THD*, Incident incident, const LEX_CSTRING *message); private: explicit injector(); diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 96d9ee4a1b4..55a66719e56 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> // For HAVE_REPLICATION +#include "mariadb.h" // For HAVE_REPLICATION #include "sql_priv.h" #include <my_dir.h> #include "rpl_mi.h" @@ -1360,7 +1360,7 @@ Master_info_index::get_master_info(const LEX_CSTRING *connection_name, { Master_info *mi; char buff[MAX_CONNECTION_NAME+1], *res; - uint buff_length; + size_t buff_length; DBUG_ENTER("get_master_info"); DBUG_PRINT("enter", ("connection_name: '%.*s'", (int) connection_name->length, @@ -1557,6 +1557,9 @@ bool give_error_if_slave_running(bool already_locked) /** any_slave_sql_running() + @param + already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked + @return 0 No Slave SQL thread is running # Number of slave SQL thread running @@ -1567,26 +1570,28 @@ bool give_error_if_slave_running(bool already_locked) hash entries can't be accessed. */ -uint any_slave_sql_running() +uint any_slave_sql_running(bool already_locked) { uint count= 0; HASH *hash; DBUG_ENTER("any_slave_sql_running"); - mysql_mutex_lock(&LOCK_active_mi); + if (!already_locked) + mysql_mutex_lock(&LOCK_active_mi); if (unlikely(shutdown_in_progress || !master_info_index)) + count= 1; + else { - mysql_mutex_unlock(&LOCK_active_mi); - DBUG_RETURN(1); - } - hash= &master_info_index->master_info_hash; - for (uint i= 0; i< hash->records; ++i) - { - Master_info *mi= (Master_info *)my_hash_element(hash, i); - if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) - count++; + hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) + { + Master_info *mi= (Master_info *)my_hash_element(hash, i); + if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + count++; + } } - mysql_mutex_unlock(&LOCK_active_mi); + if (!already_locked) + mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(count); } @@ -1909,7 +1914,7 @@ char *Domain_id_filter::as_string(enum_list_type type) return NULL; // Store the total number of elements followed by the individual elements. - ulong cur_len= sprintf(buf, "%u", ids->elements); + size_t cur_len= sprintf(buf, "%u", ids->elements); sz-= cur_len; for (uint i= 0; i < ids->elements; i++) diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index b81c1fb398a..260c35e8c04 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -20,7 +20,7 @@ #include "rpl_rli.h" #include "rpl_reporting.h" -#include "my_sys.h" +#include <my_sys.h> #include "rpl_filter.h" #include "keycaches.h" @@ -332,6 +332,11 @@ class Master_info : public Slave_reporting_capability /* The parallel replication mode. */ enum_slave_parallel_mode parallel_mode; + /* + semi_ack is used to identify if the current binlog event needs an + ACK from slave, or if delay_master is enabled. + */ + int semi_ack; }; int init_master_info(Master_info* mi, const char* master_info_fname, @@ -400,7 +405,7 @@ void create_logfile_name_with_suffix(char *res_file_name, size_t length, uchar *get_key_master_info(Master_info *mi, size_t *length, my_bool not_used __attribute__((unused))); void free_key_master_info(Master_info *mi); -uint any_slave_sql_running(); +uint any_slave_sql_running(bool already_locked); bool give_error_if_slave_running(bool already_lock); #endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 34f9113f7fe..b2b13c5467b 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1,4 +1,4 @@ -#include "my_global.h" +#include "mariadb.h" #include "rpl_parallel.h" #include "slave.h" #include "rpl_mi.h" @@ -255,8 +255,8 @@ signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi, int err) rgi->rli->abort_slave= true; rgi->rli->stop_for_until= false; mysql_mutex_lock(rgi->rli->relay_log.get_log_lock()); + rgi->rli->relay_log.signal_relay_log_update(); mysql_mutex_unlock(rgi->rli->relay_log.get_log_lock()); - rgi->rli->relay_log.signal_update(); } @@ -821,7 +821,7 @@ do_retry: for (;;) { old_offset= cur_offset; - ev= Log_event::read_log_event(&rlog, 0, description_event, + ev= Log_event::read_log_event(&rlog, description_event, opt_slave_sql_verify_checksum); cur_offset= my_b_tell(&rlog); @@ -1002,8 +1002,7 @@ handle_rpl_parallel_thread(void *arg) thd->security_ctx->skip_grants(); thd->variables.max_allowed_packet= slave_max_allowed_packet; thd->slave_thread= 1; - thd->variables.sql_log_slow= opt_log_slow_slave_statements; - thd->variables.log_slow_filter= global_system_variables.log_slow_filter; + set_slave_thread_options(thd); thd->client_capabilities = CLIENT_LOCAL_FILES; thd->net.reading_or_writing= 0; @@ -1394,7 +1393,7 @@ handle_rpl_parallel_thread(void *arg) thd->clear_error(); thd->catalog= 0; thd->reset_query(); - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); thd_proc_info(thd, "Slave worker thread exiting"); thd->temporary_tables= 0; @@ -1462,7 +1461,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, */ if (!new_count && !force) { - if (any_slave_sql_running()) + if (any_slave_sql_running(false)) { DBUG_PRINT("warning", ("SQL threads running while trying to reset parallel pool")); @@ -1617,7 +1616,7 @@ err: int rpl_parallel_resize_pool_if_no_slaves(void) { /* master_info_index is set to NULL on shutdown */ - if (opt_slave_parallel_threads > 0 && !any_slave_sql_running()) + if (opt_slave_parallel_threads > 0 && !any_slave_sql_running(false)) return rpl_parallel_inactivate_pool(&global_rpl_thread_pool); return 0; } diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 1b1059cc529..6da3f15cfb9 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "rpl_rli.h" diff --git a/sql/rpl_record.h b/sql/rpl_record.h index be69716d9d5..8d565845c4b 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -18,7 +18,6 @@ #define RPL_RECORD_H #include <rpl_reporting.h> -#include "my_global.h" /* uchar */ struct rpl_group_info; struct TABLE; diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index fd37c6f9142..523049cf959 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_rli.h" #include "rpl_record_old.h" diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc index ad949402511..800682fab91 100644 --- a/sql/rpl_reporting.cc +++ b/sql/rpl_reporting.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "rpl_reporting.h" #include "log.h" // sql_print_error, sql_print_warning, diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h index d90b7ad6650..17748f587b1 100644 --- a/sql/rpl_reporting.h +++ b/sql/rpl_reporting.h @@ -16,7 +16,7 @@ #ifndef RPL_REPORTING_H #define RPL_REPORTING_H -#include "my_sys.h" /* loglevel */ +#include <my_sys.h> /* loglevel */ /** Maximum size of an error message from a slave thread. diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index ed9f0369f5d..04109ddadb4 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" // HAVE_* #include "rpl_mi.h" @@ -31,6 +31,8 @@ #include "slave.h" #include <mysql/plugin.h> #include <mysql/service_thd_wait.h> +#include "lock.h" +#include "sql_table.h" static int count_relay_log_space(Relay_log_info* rli); @@ -68,10 +70,12 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) relay_log_state.init(); #ifdef HAVE_PSI_INTERFACE relay_log.set_psi_keys(key_RELAYLOG_LOCK_index, - key_RELAYLOG_update_cond, + key_RELAYLOG_COND_relay_log_updated, + key_RELAYLOG_COND_bin_log_updated, key_file_relaylog, key_file_relaylog_index, - key_RELAYLOG_COND_queue_busy); + key_RELAYLOG_COND_queue_busy, + key_LOCK_relaylog_end_pos); #endif group_relay_log_name[0]= event_relay_log_name[0]= @@ -536,7 +540,7 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, if (my_b_tell(cur_log) >= start_pos) break; - if (!(ev= Log_event::read_log_event(cur_log, 0, fdev, + if (!(ev= Log_event::read_log_event(cur_log, fdev, opt_slave_sql_verify_checksum))) { DBUG_PRINT("info",("could not read event, cur_log->error=%d", @@ -1509,41 +1513,21 @@ Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count) #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -int -rpl_load_gtid_slave_state(THD *thd) +struct gtid_pos_element { uint64 sub_id; rpl_gtid gtid; void *hton; }; + +static int +scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array, + LEX_CSTRING *tablename, void **out_hton) { TABLE_LIST tlist; TABLE *table; bool table_opened= false; bool table_scanned= false; - bool array_inited= false; - struct local_element { uint64 sub_id; rpl_gtid gtid; }; - struct local_element tmp_entry, *entry; - HASH hash; - DYNAMIC_ARRAY array; + struct gtid_pos_element tmp_entry, *entry; int err= 0; - uint32 i; - DBUG_ENTER("rpl_load_gtid_slave_state"); - - mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); - bool loaded= rpl_global_gtid_slave_state->loaded; - mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); - if (loaded) - DBUG_RETURN(0); - - my_hash_init(&hash, &my_charset_bin, 32, - offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id), - sizeof(uint32), NULL, my_free, HASH_UNIQUE); - if ((err= my_init_dynamic_array(&array, sizeof(local_element), 0, 0, MYF(0)))) - goto end; - array_inited= true; thd->reset_for_next_command(); - - tlist.init_one_table(STRING_WITH_LEN("mysql"), - rpl_gtid_slave_state_table_name.str, - rpl_gtid_slave_state_table_name.length, - NULL, TL_READ); + tlist.init_one_table(&MYSQL_SCHEMA_NAME, tablename, NULL, TL_READ); if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) goto end; table_opened= true; @@ -1589,26 +1573,28 @@ rpl_load_gtid_slave_state(THD *thd) tmp_entry.gtid.domain_id= domain_id; tmp_entry.gtid.server_id= server_id; tmp_entry.gtid.seq_no= seq_no; - if ((err= insert_dynamic(&array, (uchar *)&tmp_entry))) + tmp_entry.hton= table->s->db_type(); + if ((err= insert_dynamic(array, (uchar *)&tmp_entry))) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto end; } - if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0))) + if ((rec= my_hash_search(hash, (const uchar *)&domain_id, 0))) { - entry= (struct local_element *)rec; + entry= (struct gtid_pos_element *)rec; if (entry->sub_id >= sub_id) continue; entry->sub_id= sub_id; DBUG_ASSERT(entry->gtid.domain_id == domain_id); entry->gtid.server_id= server_id; entry->gtid.seq_no= seq_no; + entry->hton= table->s->db_type(); } else { - if (!(entry= (struct local_element *)my_malloc(sizeof(*entry), - MYF(MY_WME)))) + if (!(entry= (struct gtid_pos_element *)my_malloc(sizeof(*entry), + MYF(MY_WME)))) { my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry)); err= 1; @@ -1618,7 +1604,8 @@ rpl_load_gtid_slave_state(THD *thd) entry->gtid.domain_id= domain_id; entry->gtid.server_id= server_id; entry->gtid.seq_no= seq_no; - if ((err= my_hash_insert(&hash, (uchar *)entry))) + entry->hton= table->s->db_type(); + if ((err= my_hash_insert(hash, (uchar *)entry))) { my_free(entry); my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -1626,6 +1613,248 @@ rpl_load_gtid_slave_state(THD *thd) } } } + err= 0; /* Clear HA_ERR_END_OF_FILE */ + +end: + if (table_scanned) + { + table->file->ha_index_or_rnd_end(); + ha_commit_trans(thd, FALSE); + ha_commit_trans(thd, TRUE); + } + if (table_opened) + { + *out_hton= table->s->db_type(); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + } + return err; +} + + +/* + Look for all tables mysql.gtid_slave_pos*. Read all rows from each such + table found into ARRAY. For each domain id, put the row with highest sub_id + into HASH. +*/ +static int +scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_CSTRING *, void *), + void *cb_data) +{ + char path[FN_REFLEN]; + MY_DIR *dirp; + + thd->reset_for_next_command(); + if (lock_schema_name(thd, MYSQL_SCHEMA_NAME.str)) + return 1; + + build_table_filename(path, sizeof(path) - 1, MYSQL_SCHEMA_NAME.str, "", "", 0); + if (!(dirp= my_dir(path, MYF(MY_DONT_SORT)))) + { + my_error(ER_FILE_NOT_FOUND, MYF(0), path, my_errno); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + return 1; + } + else + { + size_t i; + Dynamic_array<LEX_CSTRING*> files(dirp->number_of_files); + Discovered_table_list tl(thd, &files); + int err; + + err= ha_discover_table_names(thd, &MYSQL_SCHEMA_NAME, dirp, &tl, false); + my_dirend(dirp); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + if (err) + return err; + + for (i = 0; i < files.elements(); ++i) + { + if (strncmp(files.at(i)->str, + rpl_gtid_slave_state_table_name.str, + rpl_gtid_slave_state_table_name.length) == 0) + { + if ((err= (*cb)(thd, files.at(i), cb_data))) + return err; + } + } + } + + return 0; +} + + +struct load_gtid_state_cb_data { + HASH *hash; + DYNAMIC_ARRAY *array; + struct rpl_slave_state::gtid_pos_table *table_list; + struct rpl_slave_state::gtid_pos_table *default_entry; +}; + +static int +process_gtid_pos_table(THD *thd, LEX_CSTRING *table_name, void *hton, + struct load_gtid_state_cb_data *data) +{ + struct rpl_slave_state::gtid_pos_table *p, *entry, **next_ptr; + bool is_default= + (strcmp(table_name->str, rpl_gtid_slave_state_table_name.str) == 0); + + /* + Ignore tables with duplicate storage engine, with a warning. + Prefer the default mysql.gtid_slave_pos over another table + mysql.gtid_slave_posXXX with the same storage engine. + */ + next_ptr= &data->table_list; + entry= data->table_list; + while (entry) + { + if (entry->table_hton == hton) + { + static const char *warning_msg= "Ignoring redundant table mysql.%s " + "since mysql.%s has the same storage engine"; + if (!is_default) + { + /* Ignore the redundant table. */ + sql_print_warning(warning_msg, table_name->str, entry->table_name.str); + return 0; + } + else + { + sql_print_warning(warning_msg, entry->table_name.str, table_name->str); + /* Delete the redundant table, and proceed to add this one instead. */ + *next_ptr= entry->next; + my_free(entry); + break; + } + } + next_ptr= &entry->next; + entry= entry->next; + } + + p= rpl_global_gtid_slave_state->alloc_gtid_pos_table(table_name, + hton, rpl_slave_state::GTID_POS_AVAILABLE); + if (!p) + return 1; + p->next= data->table_list; + data->table_list= p; + if (is_default) + data->default_entry= p; + return 0; +} + + +/* + Put tables corresponding to @@gtid_pos_auto_engines at the end of the list, + marked to be auto-created if needed. +*/ +static int +gtid_pos_auto_create_tables(rpl_slave_state::gtid_pos_table **list_ptr) +{ + plugin_ref *auto_engines; + int err= 0; + mysql_mutex_lock(&LOCK_global_system_variables); + for (auto_engines= opt_gtid_pos_auto_plugins; + !err && auto_engines && *auto_engines; + ++auto_engines) + { + void *hton= plugin_hton(*auto_engines); + char buf[FN_REFLEN+1]; + LEX_CSTRING table_name; + char *p; + rpl_slave_state::gtid_pos_table *entry, **next_ptr; + + /* See if this engine is already in the list. */ + next_ptr= list_ptr; + entry= *list_ptr; + while (entry) + { + if (entry->table_hton == hton) + break; + next_ptr= &entry->next; + entry= entry->next; + } + if (entry) + continue; + + /* Add an auto-create entry for this engine at end of list. */ + p= strmake(buf, rpl_gtid_slave_state_table_name.str, FN_REFLEN); + p= strmake(p, "_", FN_REFLEN - (p - buf)); + p= strmake(p, plugin_name(*auto_engines)->str, FN_REFLEN - (p - buf)); + table_name.str= buf; + table_name.length= p - buf; + entry= rpl_global_gtid_slave_state->alloc_gtid_pos_table + (&table_name, hton, rpl_slave_state::GTID_POS_AUTO_CREATE); + if (!entry) + { + err= 1; + break; + } + *next_ptr= entry; + } + mysql_mutex_unlock(&LOCK_global_system_variables); + return err; +} + + +static int +load_gtid_state_cb(THD *thd, LEX_CSTRING *table_name, void *arg) +{ + int err; + load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg); + void *hton; + + if ((err= scan_one_gtid_slave_pos_table(thd, data->hash, data->array, + table_name, &hton))) + return err; + return process_gtid_pos_table(thd, table_name, hton, data); +} + + +int +rpl_load_gtid_slave_state(THD *thd) +{ + bool array_inited= false; + struct gtid_pos_element tmp_entry, *entry; + HASH hash; + DYNAMIC_ARRAY array; + int err= 0; + uint32 i; + load_gtid_state_cb_data cb_data; + DBUG_ENTER("rpl_load_gtid_slave_state"); + + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + bool loaded= rpl_global_gtid_slave_state->loaded; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (loaded) + DBUG_RETURN(0); + + cb_data.table_list= NULL; + cb_data.default_entry= NULL; + my_hash_init(&hash, &my_charset_bin, 32, + offsetof(gtid_pos_element, gtid) + offsetof(rpl_gtid, domain_id), + sizeof(uint32), NULL, my_free, HASH_UNIQUE); + if ((err= my_init_dynamic_array(&array, sizeof(gtid_pos_element), 0, 0, MYF(0)))) + goto end; + array_inited= true; + + cb_data.hash = &hash; + cb_data.array = &array; + if ((err= scan_all_gtid_slave_pos_table(thd, load_gtid_state_cb, &cb_data))) + goto end; + + if (!cb_data.default_entry) + { + /* + If the mysql.gtid_slave_pos table does not exist, but at least one other + table is available, arbitrarily pick the first in the list to use as + default. + */ + cb_data.default_entry= cb_data.table_list; + } + if ((err= gtid_pos_auto_create_tables(&cb_data.table_list))) + goto end; mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); if (rpl_global_gtid_slave_state->loaded) @@ -1634,14 +1863,24 @@ rpl_load_gtid_slave_state(THD *thd) goto end; } + if (!cb_data.table_list) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql", + rpl_gtid_slave_state_table_name.str); + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + err= 1; + goto end; + } + for (i= 0; i < array.elements; ++i) { get_dynamic(&array, (uchar *)&tmp_entry, i); if ((err= rpl_global_gtid_slave_state->update(tmp_entry.gtid.domain_id, - tmp_entry.gtid.server_id, - tmp_entry.sub_id, - tmp_entry.gtid.seq_no, - NULL))) + tmp_entry.gtid.server_id, + tmp_entry.sub_id, + tmp_entry.gtid.seq_no, + tmp_entry.hton, + NULL))) { mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -1651,7 +1890,7 @@ rpl_load_gtid_slave_state(THD *thd) for (i= 0; i < hash.records; ++i) { - entry= (struct local_element *)my_hash_element(&hash, i); + entry= (struct gtid_pos_element *)my_hash_element(&hash, i); if (opt_bin_log && mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id, entry->gtid.seq_no)) @@ -1662,27 +1901,174 @@ rpl_load_gtid_slave_state(THD *thd) } } + rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list, + cb_data.default_entry); + cb_data.table_list= NULL; rpl_global_gtid_slave_state->loaded= true; mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); - err= 0; /* Clear HA_ERR_END_OF_FILE */ +end: + if (array_inited) + delete_dynamic(&array); + my_hash_free(&hash); + if (cb_data.table_list) + rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list); + DBUG_RETURN(err); +} + + +static int +find_gtid_pos_tables_cb(THD *thd, LEX_CSTRING *table_name, void *arg) +{ + load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg); + TABLE_LIST tlist; + TABLE *table= NULL; + int err; + + thd->reset_for_next_command(); + tlist.init_one_table(&MYSQL_SCHEMA_NAME, table_name, NULL, TL_READ); + if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0))) + goto end; + table= tlist.table; + + if ((err= gtid_check_rpl_slave_state_table(table))) + goto end; + err= process_gtid_pos_table(thd, table_name, table->s->db_type(), data); end: - if (table_scanned) + if (table) { - table->file->ha_index_or_rnd_end(); ha_commit_trans(thd, FALSE); ha_commit_trans(thd, TRUE); - } - if (table_opened) - { close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); } - if (array_inited) - delete_dynamic(&array); - my_hash_free(&hash); - DBUG_RETURN(err); + + return err; +} + + +/* + Re-compute the list of available mysql.gtid_slave_posXXX tables. + + This is done at START SLAVE to pick up any newly created tables without + requiring server restart. +*/ +int +find_gtid_slave_pos_tables(THD *thd) +{ + int err= 0; + load_gtid_state_cb_data cb_data; + uint num_running; + + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + bool loaded= rpl_global_gtid_slave_state->loaded; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (!loaded) + return 0; + + cb_data.table_list= NULL; + cb_data.default_entry= NULL; + if ((err= scan_all_gtid_slave_pos_table(thd, find_gtid_pos_tables_cb, &cb_data))) + goto end; + + if (!cb_data.table_list) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql", + rpl_gtid_slave_state_table_name.str); + err= 1; + goto end; + } + if (!cb_data.default_entry) + { + /* + If the mysql.gtid_slave_pos table does not exist, but at least one other + table is available, arbitrarily pick the first in the list to use as + default. + */ + cb_data.default_entry= cb_data.table_list; + } + if ((err= gtid_pos_auto_create_tables(&cb_data.table_list))) + goto end; + + mysql_mutex_lock(&LOCK_active_mi); + num_running= any_slave_sql_running(true); + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (num_running <= 1) + { + /* + If no slave is running now, the count will be 1, since this SQL thread + which is starting is included in the count. In this case, we can safely + replace the list, no-one can be trying to read it without lock. + */ + DBUG_ASSERT(num_running == 1); + rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list, + cb_data.default_entry); + cb_data.table_list= NULL; + } + else + { + /* + If there are SQL threads running, we cannot safely remove the old list. + However we can add new entries, and warn about any tables that + disappeared, but may still be visible to running SQL threads. + */ + rpl_slave_state::gtid_pos_table *old_entry, *new_entry, **next_ptr_ptr; + + old_entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (old_entry) + { + new_entry= cb_data.table_list; + while (new_entry) + { + if (new_entry->table_hton == old_entry->table_hton) + break; + new_entry= new_entry->next; + } + if (!new_entry) + sql_print_warning("The table mysql.%s was removed. " + "This change will not take full effect " + "until all SQL threads have been restarted", + old_entry->table_name.str); + old_entry= old_entry->next; + } + next_ptr_ptr= &cb_data.table_list; + new_entry= cb_data.table_list; + while (new_entry) + { + /* Check if we already have a table with this storage engine. */ + old_entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (old_entry) + { + if (new_entry->table_hton == old_entry->table_hton) + break; + old_entry= old_entry->next; + } + if (old_entry) + { + /* This new_entry is already available in the list. */ + next_ptr_ptr= &new_entry->next; + new_entry= new_entry->next; + } + else + { + /* Move this new_entry to the list. */ + rpl_slave_state::gtid_pos_table *next= new_entry->next; + rpl_global_gtid_slave_state->add_gtid_pos_table(new_entry); + *next_ptr_ptr= next; + new_entry= next; + } + } + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + mysql_mutex_unlock(&LOCK_active_mi); + +end: + if (cb_data.table_list) + rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list); + return err; } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index d88bc9f6ecd..b8b153c34be 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -752,7 +752,7 @@ struct rpl_group_info Runtime state for printing a note when slave is taking too long while processing a row event. */ - time_t row_stmt_start_timestamp; + longlong row_stmt_start_timestamp; bool long_find_row_note_printed; /* Needs room for "Gtid D-S-N\x00". */ char gtid_info_buf[5+10+1+10+1+20+1]; @@ -898,17 +898,15 @@ struct rpl_group_info char *gtid_info(); void unmark_start_commit(); - time_t get_row_stmt_start_timestamp() + longlong get_row_stmt_start_timestamp() { return row_stmt_start_timestamp; } - time_t set_row_stmt_start_timestamp() + void set_row_stmt_start_timestamp() { if (row_stmt_start_timestamp == 0) - row_stmt_start_timestamp= my_time(0); - - return row_stmt_start_timestamp; + row_stmt_start_timestamp= microsecond_interval_timer(); } void reset_row_stmt_start_timestamp() @@ -967,6 +965,7 @@ extern struct rpl_slave_state *rpl_global_gtid_slave_state; extern gtid_waiting rpl_global_gtid_waiting; int rpl_load_gtid_slave_state(THD *thd); +int find_gtid_slave_pos_tables(THD *thd); int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev); void delete_or_keep_event_post_apply(rpl_group_info *rgi, Log_event_type typ, Log_event *ev); diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index 06e1d19d0c8..7284e4be15c 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #ifdef HAVE_REPLICATION diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index dd7a6fd85c7..58711078db6 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <my_bit.h> #include "rpl_utility.h" #include "log_event.h" @@ -128,6 +128,8 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata) case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: return metadata; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + return metadata - 1; /* The actual length for these types does not really matter since @@ -145,6 +147,7 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata) return (uint32)my_set_bits(3 * 8); case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: /* For the blob type, Field::real_type() lies and say that all blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look @@ -294,6 +297,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const break; } case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length() length+= length == 1 ? (uint32) *master_data : uint2korr(master_data); @@ -303,6 +307,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_GEOMETRY: { /* @@ -406,11 +411,14 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { CHARSET_INFO *cs= str->charset(); - uint32 length= + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u)", metadata); + "varchar(%u)%s", metadata, + type == MYSQL_TYPE_VARCHAR_COMPRESSED ? " compressed" + : ""); str->length(length); } break; @@ -419,7 +427,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ { CHARSET_INFO *cs= str->charset(); int bit_length= 8 * (metadata >> 8) + (metadata & 0xFF); - uint32 length= + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), "bit(%d)", bit_length); str->length(length); @@ -429,7 +437,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ case MYSQL_TYPE_DECIMAL: { CHARSET_INFO *cs= str->charset(); - uint32 length= + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), "decimal(%d,?)/*old*/", metadata); str->length(length); @@ -439,7 +447,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ case MYSQL_TYPE_NEWDECIMAL: { CHARSET_INFO *cs= str->charset(); - uint32 length= + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), "decimal(%d,%d)", metadata >> 8, metadata & 0xff); str->length(length); @@ -455,6 +463,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ break; case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: /* Field::real_type() lies regarding the actual type of a BLOB, so it is necessary to check the pack length to figure out what kind @@ -482,6 +491,9 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ DBUG_ASSERT(0); break; } + + if (type == MYSQL_TYPE_BLOB_COMPRESSED) + str->append(STRING_WITH_LEN(" compressed")); break; case MYSQL_TYPE_STRING: @@ -491,7 +503,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_ */ CHARSET_INFO *cs= str->charset(); uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff); - uint32 length= + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), "char(%d)", bytes / field_cs->mbmaxlen); str->length(length); @@ -583,6 +595,7 @@ can_convert_field_to(Field *field, int *order_var) { DBUG_ENTER("can_convert_field_to"); + bool same_type; #ifndef DBUG_OFF char field_type_buf[MAX_FIELD_WIDTH]; String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1); @@ -590,11 +603,30 @@ can_convert_field_to(Field *field, DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x", field_type.c_ptr_safe(), field->real_type(), source_type, metadata)); #endif + /** + @todo + Implement Field_varstring_cmopressed::real_type() and + Field_blob_compressed::real_type() properly. All occurencies + of Field::real_type() have to be inspected and adjusted if needed. + + Until it is not ready we have to compare source_type against + binlog_type() when replicating from or to compressed data types. + + @sa Comment for Field::binlog_type() + */ + if (source_type == MYSQL_TYPE_VARCHAR_COMPRESSED || + source_type == MYSQL_TYPE_BLOB_COMPRESSED || + field->binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || + field->binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED) + same_type= field->binlog_type() == source_type; + else + same_type= field->real_type() == source_type; + /* If the real type is the same, we need to check the metadata to decide if conversions are allowed. */ - if (field->real_type() == source_type) + if (same_type) { if (metadata == 0) // Metadata can only be zero if no metadata was provided { @@ -731,18 +763,22 @@ can_convert_field_to(Field *field, case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: switch (field->real_type()) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: *order_var= compare_lengths(field, source_type, metadata); /* Here we know that the types are different, so if the order @@ -1036,6 +1072,7 @@ table_def::table_def(unsigned char *types, ulong size, switch (binlog_type(i)) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_DOUBLE: @@ -1066,6 +1103,7 @@ table_def::table_def(unsigned char *types, ulong size, break; } case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VARCHAR_COMPRESSED: { /* These types store two bytes. @@ -1127,7 +1165,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, enum enum_binlog_che if (event_buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS int8 fd_alg= event_buf[event_len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN]; #endif diff --git a/sql/scheduler.cc b/sql/scheduler.cc index de472ae2504..818bb3818f4 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -22,6 +22,7 @@ #pragma implementation #endif +#include "mariadb.h" #include "mysqld.h" #include "sql_connect.h" // init_new_connection_handler_thread #include "scheduler.h" diff --git a/sql/scheduler.h b/sql/scheduler.h index 71553372999..b067763d9b4 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -25,8 +25,6 @@ #pragma interface #endif -#include <my_global.h> - class THD; /* Functions used when manipulating threads */ diff --git a/sql/semisync.cc b/sql/semisync.cc new file mode 100644 index 00000000000..a8a11f091db --- /dev/null +++ b/sql/semisync.cc @@ -0,0 +1,32 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (C) 2008 MySQL AB + Use is subject to license terms + + 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 */ + + +#include <my_global.h> +#include "semisync.h" + +const unsigned char Repl_semi_sync_base::k_packet_magic_num= 0xef; +const unsigned char Repl_semi_sync_base::k_packet_flag_sync= 0x01; + + +const unsigned long Trace::k_trace_general= 0x0001; +const unsigned long Trace::k_trace_detail= 0x0010; +const unsigned long Trace::k_trace_net_wait= 0x0020; +const unsigned long Trace::k_trace_function= 0x0040; + +const unsigned char Repl_semi_sync_base::k_sync_header[2]= + {Repl_semi_sync_base::k_packet_magic_num, 0}; diff --git a/sql/semisync.h b/sql/semisync.h new file mode 100644 index 00000000000..9deb6c5fd01 --- /dev/null +++ b/sql/semisync.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (C) 2008 MySQL AB + + 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 SEMISYNC_H +#define SEMISYNC_H + +#include "mysqld.h" +#include "log_event.h" +#include "replication.h" + +/** + This class is used to trace function calls and other process + information +*/ +class Trace { +public: + static const unsigned long k_trace_function; + static const unsigned long k_trace_general; + static const unsigned long k_trace_detail; + static const unsigned long k_trace_net_wait; + + unsigned long m_trace_level; /* the level for tracing */ + + Trace() + :m_trace_level(0L) + {} + Trace(unsigned long trace_level) + :m_trace_level(trace_level) + {} +}; + +/** + Base class for semi-sync master and slave classes +*/ +class Repl_semi_sync_base + :public Trace { +public: + static const unsigned char k_sync_header[2]; /* three byte packet header */ + + /* Constants in network packet header. */ + static const unsigned char k_packet_magic_num; + static const unsigned char k_packet_flag_sync; +}; + +/* The layout of a semisync slave reply packet: + 1 byte for the magic num + 8 bytes for the binlog positon + n bytes for the binlog filename, terminated with a '\0' +*/ +#define REPLY_MAGIC_NUM_LEN 1 +#define REPLY_BINLOG_POS_LEN 8 +#define REPLY_BINLOG_NAME_LEN (FN_REFLEN + 1) +#define REPLY_MAGIC_NUM_OFFSET 0 +#define REPLY_BINLOG_POS_OFFSET (REPLY_MAGIC_NUM_OFFSET + REPLY_MAGIC_NUM_LEN) +#define REPLY_BINLOG_NAME_OFFSET (REPLY_BINLOG_POS_OFFSET + REPLY_BINLOG_POS_LEN) +#define REPLY_MESSAGE_MAX_LENGTH \ + (REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN) + +#endif /* SEMISYNC_H */ diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc new file mode 100644 index 00000000000..3c88bdddad4 --- /dev/null +++ b/sql/semisync_master.cc @@ -0,0 +1,1352 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (c) 2008, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2016, 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 */ + + +#include <my_global.h> +#include "semisync_master.h" + +#define TIME_THOUSAND 1000 +#define TIME_MILLION 1000000 +#define TIME_BILLION 1000000000 + +/* This indicates whether semi-synchronous replication is enabled. */ +my_bool rpl_semi_sync_master_enabled= 0; +unsigned long long rpl_semi_sync_master_request_ack = 0; +unsigned long long rpl_semi_sync_master_get_ack = 0; +my_bool rpl_semi_sync_master_wait_no_slave = 1; +my_bool rpl_semi_sync_master_status = 0; +ulong rpl_semi_sync_master_wait_point = + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT; +ulong rpl_semi_sync_master_timeout; +ulong rpl_semi_sync_master_trace_level; +ulong rpl_semi_sync_master_yes_transactions = 0; +ulong rpl_semi_sync_master_no_transactions = 0; +ulong rpl_semi_sync_master_off_times = 0; +ulong rpl_semi_sync_master_timefunc_fails = 0; +ulong rpl_semi_sync_master_wait_timeouts = 0; +ulong rpl_semi_sync_master_wait_sessions = 0; +ulong rpl_semi_sync_master_wait_pos_backtraverse = 0; +ulong rpl_semi_sync_master_avg_trx_wait_time = 0; +ulonglong rpl_semi_sync_master_trx_wait_num = 0; +ulong rpl_semi_sync_master_avg_net_wait_time = 0; +ulonglong rpl_semi_sync_master_net_wait_num = 0; +ulong rpl_semi_sync_master_clients = 0; +ulonglong rpl_semi_sync_master_net_wait_time = 0; +ulonglong rpl_semi_sync_master_trx_wait_time = 0; + +Repl_semi_sync_master repl_semisync_master; +Ack_receiver ack_receiver; + +/* + structure to save transaction log filename and position +*/ +typedef struct Trans_binlog_info { + my_off_t log_pos; + char log_file[FN_REFLEN]; +} Trans_binlog_info; + +static int get_wait_time(const struct timespec& start_ts); + +static ulonglong timespec_to_usec(const struct timespec *ts) +{ + return (ulonglong) ts->tv_sec * TIME_MILLION + ts->tv_nsec / TIME_THOUSAND; +} + +/******************************************************************************* + * + * <Active_tranx> class : manage all active transaction nodes + * + ******************************************************************************/ + +Active_tranx::Active_tranx(mysql_mutex_t *lock, + ulong trace_level) + : Trace(trace_level), m_allocator(max_connections), + m_num_entries(max_connections << 1), /* Transaction hash table size + * is set to double the size + * of max_connections */ + m_lock(lock) +{ + /* No transactions are in the list initially. */ + m_trx_front = NULL; + m_trx_rear = NULL; + + /* Create the hash table to find a transaction's ending event. */ + m_trx_htb = new Tranx_node *[m_num_entries]; + for (int idx = 0; idx < m_num_entries; ++idx) + m_trx_htb[idx] = NULL; + + sql_print_information("Semi-sync replication initialized for transactions."); +} + +Active_tranx::~Active_tranx() +{ + delete [] m_trx_htb; + m_trx_htb = NULL; + m_num_entries = 0; +} + +unsigned int Active_tranx::calc_hash(const unsigned char *key, size_t length) +{ + unsigned int nr = 1, nr2 = 4; + + /* The hash implementation comes from calc_hashnr() in mysys/hash.c. */ + while (length--) + { + nr ^= (((nr & 63)+nr2)*((unsigned int) (unsigned char) *key++))+ (nr << 8); + nr2 += 3; + } + return((unsigned int) nr); +} + +unsigned int Active_tranx::get_hash_value(const char *log_file_name, + my_off_t log_file_pos) +{ + unsigned int hash1 = calc_hash((const unsigned char *)log_file_name, + strlen(log_file_name)); + unsigned int hash2 = calc_hash((const unsigned char *)(&log_file_pos), + sizeof(log_file_pos)); + + return (hash1 + hash2) % m_num_entries; +} + +int Active_tranx::compare(const char *log_file_name1, my_off_t log_file_pos1, + const char *log_file_name2, my_off_t log_file_pos2) +{ + int cmp = strcmp(log_file_name1, log_file_name2); + + if (cmp != 0) + return cmp; + + if (log_file_pos1 > log_file_pos2) + return 1; + else if (log_file_pos1 < log_file_pos2) + return -1; + return 0; +} + +int Active_tranx::insert_tranx_node(const char *log_file_name, + my_off_t log_file_pos) +{ + Tranx_node *ins_node; + int result = 0; + unsigned int hash_val; + + DBUG_ENTER("Active_tranx:insert_tranx_node"); + + ins_node = m_allocator.allocate_node(); + if (!ins_node) + { + sql_print_error("%s: transaction node allocation failed for: (%s, %lu)", + "Active_tranx:insert_tranx_node", + log_file_name, (ulong)log_file_pos); + result = -1; + goto l_end; + } + + /* insert the binlog position in the active transaction list. */ + strncpy(ins_node->log_name, log_file_name, FN_REFLEN-1); + ins_node->log_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + ins_node->log_pos = log_file_pos; + + if (!m_trx_front) + { + /* The list is empty. */ + m_trx_front = m_trx_rear = ins_node; + } + else + { + int cmp = compare(ins_node, m_trx_rear); + if (cmp > 0) + { + /* Compare with the tail first. If the transaction happens later in + * binlog, then make it the new tail. + */ + m_trx_rear->next = ins_node; + m_trx_rear = ins_node; + } + else + { + /* Otherwise, it is an error because the transaction should hold the + * mysql_bin_log.LOCK_log when appending events. + */ + sql_print_error("%s: binlog write out-of-order, tail (%s, %lu), " + "new node (%s, %lu)", "Active_tranx:insert_tranx_node", + m_trx_rear->log_name, (ulong)m_trx_rear->log_pos, + ins_node->log_name, (ulong)ins_node->log_pos); + result = -1; + goto l_end; + } + } + + hash_val = get_hash_value(ins_node->log_name, ins_node->log_pos); + ins_node->hash_next = m_trx_htb[hash_val]; + m_trx_htb[hash_val] = ins_node; + + DBUG_PRINT("semisync", ("%s: insert (%s, %lu) in entry(%u)", + "Active_tranx:insert_tranx_node", + ins_node->log_name, (ulong)ins_node->log_pos, + hash_val)); + l_end: + + DBUG_RETURN(result); +} + +bool Active_tranx::is_tranx_end_pos(const char *log_file_name, + my_off_t log_file_pos) +{ + DBUG_ENTER("Active_tranx::is_tranx_end_pos"); + + unsigned int hash_val = get_hash_value(log_file_name, log_file_pos); + Tranx_node *entry = m_trx_htb[hash_val]; + + while (entry != NULL) + { + if (compare(entry, log_file_name, log_file_pos) == 0) + break; + + entry = entry->hash_next; + } + + DBUG_PRINT("semisync", ("%s: probe (%s, %lu) in entry(%u)", + "Active_tranx::is_tranx_end_pos", + log_file_name, (ulong)log_file_pos, hash_val)); + + DBUG_RETURN(entry != NULL); +} + +int Active_tranx::clear_active_tranx_nodes(const char *log_file_name, + my_off_t log_file_pos) +{ + Tranx_node *new_front; + + DBUG_ENTER("Active_tranx::::clear_active_tranx_nodes"); + + if (log_file_name != NULL) + { + new_front = m_trx_front; + + while (new_front) + { + if (compare(new_front, log_file_name, log_file_pos) > 0) + break; + new_front = new_front->next; + } + } + else + { + /* If log_file_name is NULL, clear everything. */ + new_front = NULL; + } + + if (new_front == NULL) + { + /* No active transaction nodes after the call. */ + + /* Clear the hash table. */ + memset(m_trx_htb, 0, m_num_entries * sizeof(Tranx_node *)); + m_allocator.free_all_nodes(); + + /* Clear the active transaction list. */ + if (m_trx_front != NULL) + { + m_trx_front = NULL; + m_trx_rear = NULL; + } + + DBUG_PRINT("semisync", ("%s: cleared all nodes", + "Active_tranx::::clear_active_tranx_nodes")); + } + else if (new_front != m_trx_front) + { + Tranx_node *curr_node, *next_node; + + /* Delete all transaction nodes before the confirmation point. */ + int n_frees = 0; + curr_node = m_trx_front; + while (curr_node != new_front) + { + next_node = curr_node->next; + n_frees++; + + /* Remove the node from the hash table. */ + unsigned int hash_val = get_hash_value(curr_node->log_name, curr_node->log_pos); + Tranx_node **hash_ptr = &(m_trx_htb[hash_val]); + while ((*hash_ptr) != NULL) + { + if ((*hash_ptr) == curr_node) + { + (*hash_ptr) = curr_node->hash_next; + break; + } + hash_ptr = &((*hash_ptr)->hash_next); + } + + curr_node = next_node; + } + + m_trx_front = new_front; + m_allocator.free_nodes_before(m_trx_front); + + DBUG_PRINT("semisync", ("%s: cleared %d nodes back until pos (%s, %lu)", + "Active_tranx::::clear_active_tranx_nodes", + n_frees, + m_trx_front->log_name, (ulong)m_trx_front->log_pos)); + } + + DBUG_RETURN(0); +} + + +/******************************************************************************* + * + * <Repl_semi_sync_master> class: the basic code layer for syncsync master. + * <Repl_semi_sync_slave> class: the basic code layer for syncsync slave. + * + * The most important functions during semi-syn replication listed: + * + * Master: + * . report_reply_binlog(): called by the binlog dump thread when it receives + * the slave's status information. + * . update_sync_header(): based on transaction waiting information, decide + * whether to request the slave to reply. + * . write_tranx_in_binlog(): called by the transaction thread when it finishes + * writing all transaction events in binlog. + * . commit_trx(): transaction thread wait for the slave reply. + * + * Slave: + * . slave_read_sync_header(): read the semi-sync header from the master, get + * the sync status and get the payload for events. + * . slave_reply(): reply to the master about the replication progress. + * + ******************************************************************************/ + +Repl_semi_sync_master::Repl_semi_sync_master() + : m_active_tranxs(NULL), + m_init_done(false), + m_reply_file_name_inited(false), + m_reply_file_pos(0L), + m_wait_file_name_inited(false), + m_wait_file_pos(0), + m_master_enabled(false), + m_wait_timeout(0L), + m_state(0), + m_wait_point(0) +{ + strcpy(m_reply_file_name, ""); + strcpy(m_wait_file_name, ""); +} + +int Repl_semi_sync_master::init_object() +{ + int result; + + m_init_done = true; + + /* References to the parameter works after set_options(). */ + set_wait_timeout(rpl_semi_sync_master_timeout); + set_trace_level(rpl_semi_sync_master_trace_level); + set_wait_point(rpl_semi_sync_master_wait_point); + + /* Mutex initialization can only be done after MY_INIT(). */ + mysql_mutex_init(key_LOCK_binlog, + &LOCK_binlog, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_COND_binlog_send, + &COND_binlog_send, NULL); + + if (rpl_semi_sync_master_enabled) + { + result = enable_master(); + if (!result) + result= ack_receiver.start(); /* Start the ACK thread. */ + } + else + { + result = disable_master(); + } + + /* + If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily + switch off semisync to avoid hang if there's none active slave. + */ + if (!rpl_semi_sync_master_wait_no_slave) + switch_off(); + + return result; +} + +int Repl_semi_sync_master::enable_master() +{ + int result = 0; + + /* Must have the lock when we do enable of disable. */ + lock(); + + if (!get_master_enabled()) + { + m_active_tranxs = new Active_tranx(&LOCK_binlog, m_trace_level); + if (m_active_tranxs != NULL) + { + m_commit_file_name_inited = false; + m_reply_file_name_inited = false; + m_wait_file_name_inited = false; + + set_master_enabled(true); + m_state = true; + sql_print_information("Semi-sync replication enabled on the master."); + } + else + { + sql_print_error("Cannot allocate memory to enable semi-sync on the master."); + result = -1; + } + } + + unlock(); + + return result; +} + +int Repl_semi_sync_master::disable_master() +{ + /* Must have the lock when we do enable of disable. */ + lock(); + + if (get_master_enabled()) + { + /* Switch off the semi-sync first so that waiting transaction will be + * waken up. + */ + switch_off(); + + assert(m_active_tranxs != NULL); + delete m_active_tranxs; + m_active_tranxs = NULL; + + m_reply_file_name_inited = false; + m_wait_file_name_inited = false; + m_commit_file_name_inited = false; + + set_master_enabled(false); + sql_print_information("Semi-sync replication disabled on the master."); + } + + unlock(); + + return 0; +} + +void Repl_semi_sync_master::cleanup() +{ + if (m_init_done) + { + mysql_mutex_destroy(&LOCK_binlog); + mysql_cond_destroy(&COND_binlog_send); + m_init_done= 0; + } + + delete m_active_tranxs; +} + +void Repl_semi_sync_master::lock() +{ + mysql_mutex_lock(&LOCK_binlog); +} + +void Repl_semi_sync_master::unlock() +{ + mysql_mutex_unlock(&LOCK_binlog); +} + +void Repl_semi_sync_master::cond_broadcast() +{ + mysql_cond_broadcast(&COND_binlog_send); +} + +int Repl_semi_sync_master::cond_timewait(struct timespec *wait_time) +{ + int wait_res; + + DBUG_ENTER("Repl_semi_sync_master::cond_timewait()"); + + wait_res= mysql_cond_timedwait(&COND_binlog_send, + &LOCK_binlog, wait_time); + + DBUG_RETURN(wait_res); +} + +void Repl_semi_sync_master::add_slave() +{ + lock(); + rpl_semi_sync_master_clients++; + unlock(); +} + +void Repl_semi_sync_master::remove_slave() +{ + lock(); + rpl_semi_sync_master_clients--; + + /* Only switch off if semi-sync is enabled and is on */ + if (get_master_enabled() && is_on()) + { + /* If user has chosen not to wait if no semi-sync slave available + and the last semi-sync slave exits, turn off semi-sync on master + immediately. + */ + if (!rpl_semi_sync_master_wait_no_slave && + rpl_semi_sync_master_clients == 0) + switch_off(); + } + unlock(); +} + +int Repl_semi_sync_master::report_reply_packet(uint32 server_id, + const uchar *packet, + ulong packet_len) +{ + int result= -1; + char log_file_name[FN_REFLEN+1]; + my_off_t log_file_pos; + ulong log_file_len = 0; + + DBUG_ENTER("Repl_semi_sync_master::report_reply_packet"); + + if (unlikely(packet[REPLY_MAGIC_NUM_OFFSET] != + Repl_semi_sync_master::k_packet_magic_num)) + { + sql_print_error("Read semi-sync reply magic number error"); + goto l_end; + } + + if (unlikely(packet_len < REPLY_BINLOG_NAME_OFFSET)) + { + sql_print_error("Read semi-sync reply length error: packet is too small"); + goto l_end; + } + + log_file_pos = uint8korr(packet + REPLY_BINLOG_POS_OFFSET); + log_file_len = packet_len - REPLY_BINLOG_NAME_OFFSET; + if (unlikely(log_file_len >= FN_REFLEN)) + { + sql_print_error("Read semi-sync reply binlog file length too large"); + goto l_end; + } + strncpy(log_file_name, (const char*)packet + REPLY_BINLOG_NAME_OFFSET, log_file_len); + log_file_name[log_file_len] = 0; + + DBUG_ASSERT(dirname_length(log_file_name) == 0); + + DBUG_PRINT("semisync", ("%s: Got reply(%s, %lu) from server %u", + "Repl_semi_sync_master::report_reply_packet", + log_file_name, (ulong)log_file_pos, server_id)); + + rpl_semi_sync_master_get_ack++; + report_reply_binlog(server_id, log_file_name, log_file_pos); + +l_end: + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::report_reply_binlog(uint32 server_id, + const char *log_file_name, + my_off_t log_file_pos) +{ + int cmp; + bool can_release_threads = false; + bool need_copy_send_pos = true; + + DBUG_ENTER("Repl_semi_sync_master::report_reply_binlog"); + + if (!(get_master_enabled())) + DBUG_RETURN(0); + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + goto l_end; + + if (!is_on()) + /* We check to see whether we can switch semi-sync ON. */ + try_switch_on(server_id, log_file_name, log_file_pos); + + /* The position should increase monotonically, if there is only one + * thread sending the binlog to the slave. + * In reality, to improve the transaction availability, we allow multiple + * sync replication slaves. So, if any one of them get the transaction, + * the transaction session in the primary can move forward. + */ + if (m_reply_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_reply_file_name, m_reply_file_pos); + + /* If the requested position is behind the sending binlog position, + * would not adjust sending binlog position. + * We based on the assumption that there are multiple semi-sync slave, + * and at least one of them shou/ld be up to date. + * If all semi-sync slaves are behind, at least initially, the primary + * can find the situation after the waiting timeout. After that, some + * slaves should catch up quickly. + */ + if (cmp < 0) + { + /* If the position is behind, do not copy it. */ + need_copy_send_pos = false; + } + } + + if (need_copy_send_pos) + { + strmake_buf(m_reply_file_name, log_file_name); + m_reply_file_pos = log_file_pos; + m_reply_file_name_inited = true; + + /* Remove all active transaction nodes before this point. */ + assert(m_active_tranxs != NULL); + m_active_tranxs->clear_active_tranx_nodes(log_file_name, log_file_pos); + + DBUG_PRINT("semisync", ("%s: Got reply at (%s, %lu)", + "Repl_semi_sync_master::report_reply_binlog", + log_file_name, (ulong)log_file_pos)); + } + + if (rpl_semi_sync_master_wait_sessions > 0) + { + /* Let us check if some of the waiting threads doing a trx + * commit can now proceed. + */ + cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos, + m_wait_file_name, m_wait_file_pos); + if (cmp >= 0) + { + /* Yes, at least one waiting thread can now proceed: + * let us release all waiting threads with a broadcast + */ + can_release_threads = true; + m_wait_file_name_inited = false; + } + } + + l_end: + unlock(); + + if (can_release_threads) + { + DBUG_PRINT("semisync", ("%s: signal all waiting threads.", + "Repl_semi_sync_master::report_reply_binlog")); + + cond_broadcast(); + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::wait_after_sync(const char *log_file, my_off_t log_pos) +{ + if (!get_master_enabled()) + return 0; + + int ret= 0; + if(log_pos && + wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC) + ret= commit_trx(log_file + dirname_length(log_file), log_pos); + + return ret; +} + +int Repl_semi_sync_master::wait_after_commit(THD* thd, bool all) +{ + if (!get_master_enabled()) + return 0; + + int ret= 0; + const char *log_file; + my_off_t log_pos; + + bool is_real_trans= + (all || thd->transaction.all.ha_list == 0); + /* + The coordinates are propagated to this point having been computed + in report_binlog_update + */ + Trans_binlog_info *log_info= thd->semisync_info; + log_file= log_info && log_info->log_file[0] ? log_info->log_file : 0; + log_pos= log_info ? log_info->log_pos : 0; + + DBUG_ASSERT(!log_file || dirname_length(log_file) == 0); + + if (is_real_trans && + log_pos && + wait_point() == SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT) + ret= commit_trx(log_file, log_pos); + + if (is_real_trans && log_info) + { + log_info->log_file[0]= 0; + log_info->log_pos= 0; + } + + return ret; +} + +int Repl_semi_sync_master::wait_after_rollback(THD *thd, bool all) +{ + return wait_after_commit(thd, all); +} + +/** + The method runs after flush to binary log is done. +*/ +int Repl_semi_sync_master::report_binlog_update(THD* thd, const char *log_file, + my_off_t log_pos) +{ + if (get_master_enabled()) + { + Trans_binlog_info *log_info; + + if (!(log_info= thd->semisync_info)) + { + if(!(log_info= + (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0)))) + return 1; + thd->semisync_info= log_info; + } + strcpy(log_info->log_file, log_file + dirname_length(log_file)); + log_info->log_pos = log_pos; + + return write_tranx_in_binlog(log_info->log_file, log_pos); + } + + return 0; +} + +int Repl_semi_sync_master::dump_start(THD* thd, + const char *log_file, + my_off_t log_pos) +{ + if (!thd->semi_sync_slave) + return 0; + + if (ack_receiver.add_slave(thd)) + { + sql_print_error("Failed to register slave to semi-sync ACK receiver " + "thread. Turning off semisync"); + thd->semi_sync_slave= 0; + return 1; + } + + add_slave(); + report_reply_binlog(thd->variables.server_id, + log_file + dirname_length(log_file), log_pos); + sql_print_information("Start semi-sync binlog_dump to slave " + "(server_id: %ld), pos(%s, %lu)", + (long) thd->variables.server_id, log_file, + (ulong) log_pos); + + return 0; +} + +void Repl_semi_sync_master::dump_end(THD* thd) +{ + if (!thd->semi_sync_slave) + return; + + sql_print_information("Stop semi-sync binlog_dump to slave (server_id: %ld)", + (long) thd->variables.server_id); + + remove_slave(); + ack_receiver.remove_slave(thd); + + return; +} + +int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, + my_off_t trx_wait_binlog_pos) +{ + + DBUG_ENTER("Repl_semi_sync_master::commit_trx"); + + if (get_master_enabled() && trx_wait_binlog_name) + { + struct timespec start_ts; + struct timespec abstime; + int wait_result; + PSI_stage_info old_stage; + + set_timespec(start_ts, 0); + + DEBUG_SYNC(current_thd, "rpl_semisync_master_commit_trx_before_lock"); + /* Acquire the mutex. */ + lock(); + + /* This must be called after acquired the lock */ + THD_ENTER_COND(NULL, &COND_binlog_send, &LOCK_binlog, + & stage_waiting_for_semi_sync_ack_from_slave, + & old_stage); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled() || !is_on()) + goto l_end; + + DBUG_PRINT("semisync", ("%s: wait pos (%s, %lu), repl(%d)\n", + "Repl_semi_sync_master::commit_trx", + trx_wait_binlog_name, (ulong)trx_wait_binlog_pos, + (int)is_on())); + + while (is_on() && !thd_killed(current_thd)) + { + if (m_reply_file_name_inited) + { + int cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos, + trx_wait_binlog_name, + trx_wait_binlog_pos); + if (cmp >= 0) + { + /* We have already sent the relevant binlog to the slave: no need to + * wait here. + */ + DBUG_PRINT("semisync", ("%s: Binlog reply is ahead (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_reply_file_name, + (ulong)m_reply_file_pos)); + break; + } + } + + /* Let us update the info about the minimum binlog position of waiting + * threads. + */ + if (m_wait_file_name_inited) + { + int cmp = Active_tranx::compare(trx_wait_binlog_name, + trx_wait_binlog_pos, + m_wait_file_name, m_wait_file_pos); + if (cmp <= 0) + { + /* This thd has a lower position, let's update the minimum info. */ + strmake_buf(m_wait_file_name, trx_wait_binlog_name); + m_wait_file_pos = trx_wait_binlog_pos; + + rpl_semi_sync_master_wait_pos_backtraverse++; + DBUG_PRINT("semisync", ("%s: move back wait position (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_wait_file_name, (ulong)m_wait_file_pos)); + } + } + else + { + strmake_buf(m_wait_file_name, trx_wait_binlog_name); + m_wait_file_pos = trx_wait_binlog_pos; + m_wait_file_name_inited = true; + + DBUG_PRINT("semisync", ("%s: init wait position (%s, %lu),", + "Repl_semi_sync_master::commit_trx", + m_wait_file_name, (ulong)m_wait_file_pos)); + } + + /* Calcuate the waiting period. */ + long diff_secs = (long) (m_wait_timeout / TIME_THOUSAND); + long diff_nsecs = (long) ((m_wait_timeout % TIME_THOUSAND) * TIME_MILLION); + long nsecs = start_ts.tv_nsec + diff_nsecs; + abstime.tv_sec = start_ts.tv_sec + diff_secs + nsecs/TIME_BILLION; + abstime.tv_nsec = nsecs % TIME_BILLION; + + /* In semi-synchronous replication, we wait until the binlog-dump + * thread has received the reply on the relevant binlog segment from the + * replication slave. + * + * Let us suspend this thread to wait on the condition; + * when replication has progressed far enough, we will release + * these waiting threads. + */ + rpl_semi_sync_master_wait_sessions++; + + DBUG_PRINT("semisync", ("%s: wait %lu ms for binlog sent (%s, %lu)", + "Repl_semi_sync_master::commit_trx", + m_wait_timeout, + m_wait_file_name, (ulong)m_wait_file_pos)); + + wait_result = cond_timewait(&abstime); + rpl_semi_sync_master_wait_sessions--; + + if (wait_result != 0) + { + /* This is a real wait timeout. */ + sql_print_warning("Timeout waiting for reply of binlog (file: %s, pos: %lu), " + "semi-sync up to file %s, position %lu.", + trx_wait_binlog_name, (ulong)trx_wait_binlog_pos, + m_reply_file_name, (ulong)m_reply_file_pos); + rpl_semi_sync_master_wait_timeouts++; + + /* switch semi-sync off */ + switch_off(); + } + else + { + int wait_time; + + wait_time = get_wait_time(start_ts); + if (wait_time < 0) + { + DBUG_PRINT("semisync", ("Replication semi-sync getWaitTime fail at " + "wait position (%s, %lu)", + trx_wait_binlog_name, + (ulong)trx_wait_binlog_pos)); + rpl_semi_sync_master_timefunc_fails++; + } + else + { + rpl_semi_sync_master_trx_wait_num++; + rpl_semi_sync_master_trx_wait_time += wait_time; + } + } + } + + /* + At this point, the binlog file and position of this transaction + must have been removed from Active_tranx. + m_active_tranxs may be NULL if someone disabled semi sync during + cond_timewait() + */ + assert(thd_killed(current_thd) || !m_active_tranxs || + !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name, + trx_wait_binlog_pos)); + + l_end: + /* Update the status counter. */ + if (is_on()) + rpl_semi_sync_master_yes_transactions++; + else + rpl_semi_sync_master_no_transactions++; + + /* The lock held will be released by thd_exit_cond, so no need to + call unlock() here */ + THD_EXIT_COND(NULL, & old_stage); + } + + DBUG_RETURN(0); +} + +/* Indicate that semi-sync replication is OFF now. + * + * What should we do when it is disabled? The problem is that we want + * the semi-sync replication enabled again when the slave catches up + * later. But, it is not that easy to detect that the slave has caught + * up. This is caused by the fact that MySQL's replication protocol is + * asynchronous, meaning that if the master does not use the semi-sync + * protocol, the slave would not send anything to the master. + * Still, if the master is sending (N+1)-th event, we assume that it is + * an indicator that the slave has received N-th event and earlier ones. + * + * If semi-sync is disabled, all transactions still update the wait + * position with the last position in binlog. But no transactions will + * wait for confirmations and the active transaction list would not be + * maintained. In binlog dump thread, update_sync_header() checks whether + * the current sending event catches up with last wait position. If it + * does match, semi-sync will be switched on again. + */ +int Repl_semi_sync_master::switch_off() +{ + int result; + + DBUG_ENTER("Repl_semi_sync_master::switch_off"); + + m_state = false; + + /* Clear the active transaction list. */ + assert(m_active_tranxs != NULL); + result = m_active_tranxs->clear_active_tranx_nodes(NULL, 0); + + rpl_semi_sync_master_off_times++; + m_wait_file_name_inited = false; + m_reply_file_name_inited = false; + sql_print_information("Semi-sync replication switched OFF."); + cond_broadcast(); /* wake up all waiting threads */ + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::try_switch_on(int server_id, + const char *log_file_name, + my_off_t log_file_pos) +{ + bool semi_sync_on = false; + + DBUG_ENTER("Repl_semi_sync_master::try_switch_on"); + + /* If the current sending event's position is larger than or equal to the + * 'largest' commit transaction binlog position, the slave is already + * catching up now and we can switch semi-sync on here. + * If m_commit_file_name_inited indicates there are no recent transactions, + * we can enable semi-sync immediately. + */ + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + semi_sync_on = (cmp >= 0); + } + else + { + semi_sync_on = true; + } + + if (semi_sync_on) + { + /* Switch semi-sync replication on. */ + m_state = true; + + sql_print_information("Semi-sync replication switched ON with slave (server_id: %d) " + "at (%s, %lu)", + server_id, log_file_name, + (ulong)log_file_pos); + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::reserve_sync_header(String* packet) +{ + DBUG_ENTER("Repl_semi_sync_master::reserve_sync_header"); + + /* Set the magic number and the sync status. By default, no sync + * is required. + */ + packet->append(reinterpret_cast<const char*>(k_sync_header), + sizeof(k_sync_header)); + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, + const char *log_file_name, + my_off_t log_file_pos, + bool* need_sync) +{ + int cmp = 0; + bool sync = false; + + DBUG_ENTER("Repl_semi_sync_master::update_sync_header"); + + /* If the semi-sync master is not enabled, or the slave is not a semi-sync + * target, do not request replies from the slave. + */ + if (!get_master_enabled() || !thd->semi_sync_slave) + { + *need_sync = false; + DBUG_RETURN(0); + } + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + { + assert(sync == false); + goto l_end; + } + + if (is_on()) + { + /* semi-sync is ON */ + sync = false; /* No sync unless a transaction is involved. */ + + if (m_reply_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_reply_file_name, m_reply_file_pos); + if (cmp <= 0) + { + /* If we have already got the reply for the event, then we do + * not need to sync the transaction again. + */ + goto l_end; + } + } + + if (m_wait_file_name_inited) + { + cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_wait_file_name, m_wait_file_pos); + } + else + { + cmp = 1; + } + + /* If we are already waiting for some transaction replies which + * are later in binlog, do not wait for this one event. + */ + if (cmp >= 0) + { + /* + * We only wait if the event is a transaction's ending event. + */ + assert(m_active_tranxs != NULL); + sync = m_active_tranxs->is_tranx_end_pos(log_file_name, + log_file_pos); + } + } + else + { + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + sync = (cmp >= 0); + } + else + { + sync = true; + } + } + + DBUG_PRINT("semisync", ("%s: server(%lu), (%s, %lu) sync(%d), repl(%d)", + "Repl_semi_sync_master::update_sync_header", + thd->variables.server_id, log_file_name, + (ulong)log_file_pos, sync, (int)is_on())); + *need_sync= sync; + + l_end: + unlock(); + + /* We do not need to clear sync flag because we set it to 0 when we + * reserve the packet header. + */ + if (sync) + { + (packet)[2] = k_packet_flag_sync; + } + + DBUG_RETURN(0); +} + +int Repl_semi_sync_master::write_tranx_in_binlog(const char* log_file_name, + my_off_t log_file_pos) +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::write_tranx_in_binlog"); + + lock(); + + /* This is the real check inside the mutex. */ + if (!get_master_enabled()) + goto l_end; + + /* Update the 'largest' transaction commit position seen so far even + * though semi-sync is switched off. + * It is much better that we update m_commit_file* here, instead of + * inside commit_trx(). This is mostly because update_sync_header() + * will watch for m_commit_file* to decide whether to switch semi-sync + * on. The detailed reason is explained in function update_sync_header(). + */ + if (m_commit_file_name_inited) + { + int cmp = Active_tranx::compare(log_file_name, log_file_pos, + m_commit_file_name, m_commit_file_pos); + if (cmp > 0) + { + /* This is a larger position, let's update the maximum info. */ + strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1); + m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + m_commit_file_pos = log_file_pos; + } + } + else + { + strncpy(m_commit_file_name, log_file_name, FN_REFLEN-1); + m_commit_file_name[FN_REFLEN-1] = 0; /* make sure it ends properly */ + m_commit_file_pos = log_file_pos; + m_commit_file_name_inited = true; + } + + if (is_on()) + { + assert(m_active_tranxs != NULL); + if(m_active_tranxs->insert_tranx_node(log_file_name, log_file_pos)) + { + /* + if insert tranx_node failed, print a warning message + and turn off semi-sync + */ + sql_print_warning("Semi-sync failed to insert tranx_node for binlog file: %s, position: %lu", + log_file_name, (ulong)log_file_pos); + switch_off(); + } + else + { + rpl_semi_sync_master_request_ack++; + } + } + + l_end: + unlock(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::flush_net(THD *thd, + const char *event_buf) +{ + int result = -1; + NET* net= &thd->net; + + DBUG_ENTER("Repl_semi_sync_master::flush_net"); + + assert((unsigned char)event_buf[1] == k_packet_magic_num); + if ((unsigned char)event_buf[2] != k_packet_flag_sync) + { + /* current event does not require reply */ + result = 0; + goto l_end; + } + + /* We flush to make sure that the current event is sent to the network, + * instead of being buffered in the TCP/IP stack. + */ + if (net_flush(net)) + { + sql_print_error("Semi-sync master failed on net_flush() " + "before waiting for slave reply"); + goto l_end; + } + + net_clear(net, 0); + net->pkt_nr++; + result = 0; + rpl_semi_sync_master_net_wait_num++; + + l_end: + thd->clear_error(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::after_reset_master() +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::after_reset_master"); + + if (rpl_semi_sync_master_enabled) + { + sql_print_information("Enable Semi-sync Master after reset master"); + enable_master(); + } + + lock(); + + if (rpl_semi_sync_master_clients == 0 && + !rpl_semi_sync_master_wait_no_slave) + m_state = 0; + else + m_state = get_master_enabled()? 1 : 0; + + m_wait_file_name_inited = false; + m_reply_file_name_inited = false; + m_commit_file_name_inited = false; + + rpl_semi_sync_master_yes_transactions = 0; + rpl_semi_sync_master_no_transactions = 0; + rpl_semi_sync_master_off_times = 0; + rpl_semi_sync_master_timefunc_fails = 0; + rpl_semi_sync_master_wait_sessions = 0; + rpl_semi_sync_master_wait_pos_backtraverse = 0; + rpl_semi_sync_master_trx_wait_num = 0; + rpl_semi_sync_master_trx_wait_time = 0; + rpl_semi_sync_master_net_wait_num = 0; + rpl_semi_sync_master_net_wait_time = 0; + + unlock(); + + DBUG_RETURN(result); +} + +int Repl_semi_sync_master::before_reset_master() +{ + int result = 0; + + DBUG_ENTER("Repl_semi_sync_master::before_reset_master"); + + if (rpl_semi_sync_master_enabled) + disable_master(); + + DBUG_RETURN(result); +} + +void Repl_semi_sync_master::check_and_switch() +{ + lock(); + if (get_master_enabled() && is_on()) + { + if (!rpl_semi_sync_master_wait_no_slave + && rpl_semi_sync_master_clients == 0) + switch_off(); + } + unlock(); +} + +void Repl_semi_sync_master::set_export_stats() +{ + lock(); + + rpl_semi_sync_master_status = m_state; + rpl_semi_sync_master_avg_trx_wait_time= + ((rpl_semi_sync_master_trx_wait_num) ? + (ulong)((double)rpl_semi_sync_master_trx_wait_time / + ((double)rpl_semi_sync_master_trx_wait_num)) : 0); + rpl_semi_sync_master_avg_net_wait_time= + ((rpl_semi_sync_master_net_wait_num) ? + (ulong)((double)rpl_semi_sync_master_net_wait_time / + ((double)rpl_semi_sync_master_net_wait_num)) : 0); + + unlock(); +} + +/* Get the waiting time given the wait's staring time. + * + * Return: + * >= 0: the waiting time in microsecons(us) + * < 0: error in get time or time back traverse + */ +static int get_wait_time(const struct timespec& start_ts) +{ + ulonglong start_usecs, end_usecs; + struct timespec end_ts; + + /* Starting time in microseconds(us). */ + start_usecs = timespec_to_usec(&start_ts); + + /* Get the wait time interval. */ + set_timespec(end_ts, 0); + + /* Ending time in microseconds(us). */ + end_usecs = timespec_to_usec(&end_ts); + + if (end_usecs < start_usecs) + return -1; + + return (int)(end_usecs - start_usecs); +} + +void semi_sync_master_deinit() +{ + repl_semisync_master.cleanup(); + ack_receiver.cleanup(); +} diff --git a/sql/semisync_master.h b/sql/semisync_master.h new file mode 100644 index 00000000000..3b05d9e0348 --- /dev/null +++ b/sql/semisync_master.h @@ -0,0 +1,674 @@ +/* Copyright (C) 2007 Google Inc. + Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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 SEMISYNC_MASTER_H +#define SEMISYNC_MASTER_H + +#include "semisync.h" +#include "semisync_master_ack_receiver.h" + +#ifdef HAVE_PSI_INTERFACE +extern PSI_mutex_key key_LOCK_binlog; +extern PSI_cond_key key_COND_binlog_send; +#endif + +struct Tranx_node { + char log_name[FN_REFLEN]; + my_off_t log_pos; + struct Tranx_node *next; /* the next node in the sorted list */ + struct Tranx_node *hash_next; /* the next node during hash collision */ +}; + +/** + @class Tranx_node_allocator + + This class provides memory allocating and freeing methods for + Tranx_node. The main target is performance. + + @section ALLOCATE How to allocate a node + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + The list starts up empty (ie, there is no allocated Block). + + After some nodes are freed, there probably are some free nodes before + the sequence of the allocated nodes, but we do not reuse it. It is better + to keep the allocated nodes are in the sequence, for it is more efficient + for allocating and freeing Tranx_node. + + @section FREENODE How to free nodes + There are two methods for freeing nodes. They are free_all_nodes and + free_nodes_before. + + 'A Block is free' means all of its nodes are free. + @subsection free_nodes_before + As all allocated nodes are in the sequence, 'Before one node' means all + nodes before given node in the same Block and all Blocks before the Block + which containing the given node. As such, all Blocks before the given one + ('node') are free Block and moved into the rear of the Block link table. + The Block containing the given 'node', however, is not. For at least the + given 'node' is still in use. This will waste at most one Block, but it is + more efficient. + */ +#define BLOCK_TRANX_NODES 16 +class Tranx_node_allocator +{ +public: + /** + @param reserved_nodes + The number of reserved Tranx_nodes. It is used to set 'reserved_blocks' + which can contain at least 'reserved_nodes' number of Tranx_nodes. When + freeing memory, we will reserve at least reserved_blocks of Blocks not + freed. + */ + Tranx_node_allocator(uint reserved_nodes) : + reserved_blocks(reserved_nodes/BLOCK_TRANX_NODES + + (reserved_nodes%BLOCK_TRANX_NODES > 1 ? 2 : 1)), + first_block(NULL), last_block(NULL), + current_block(NULL), last_node(-1), block_num(0) {} + + ~Tranx_node_allocator() + { + Block *block= first_block; + while (block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + } + + /** + The pointer of the first node after 'last_node' in current_block is + returned. current_block will move to the next free Block when all nodes of + it are in use. A new Block is allocated and is put into the rear of the + Block link table if no Block is free. + + @return Return a Tranx_node *, or NULL if an error occurred. + */ + Tranx_node *allocate_node() + { + Tranx_node *trx_node; + Block *block= current_block; + + if (last_node == BLOCK_TRANX_NODES-1) + { + current_block= current_block->next; + last_node= -1; + } + + if (current_block == NULL && allocate_block()) + { + current_block= block; + if (current_block) + last_node= BLOCK_TRANX_NODES-1; + return NULL; + } + + trx_node= &(current_block->nodes[++last_node]); + trx_node->log_name[0] = '\0'; + trx_node->log_pos= 0; + trx_node->next= 0; + trx_node->hash_next= 0; + return trx_node; + } + + /** + All nodes are freed. + + @return Return 0, or 1 if an error occurred. + */ + int free_all_nodes() + { + current_block= first_block; + last_node= -1; + free_blocks(); + return 0; + } + + /** + All Blocks before the given 'node' are free Block and moved into the rear + of the Block link table. + + @param node All nodes before 'node' will be freed + + @return Return 0, or 1 if an error occurred. + */ + int free_nodes_before(Tranx_node* node) + { + Block *block; + Block *prev_block= NULL; + + block= first_block; + while (block != current_block->next) + { + /* Find the Block containing the given node */ + if (&(block->nodes[0]) <= node && &(block->nodes[BLOCK_TRANX_NODES]) >= node) + { + /* All Blocks before the given node are put into the rear */ + if (first_block != block) + { + last_block->next= first_block; + first_block= block; + last_block= prev_block; + last_block->next= NULL; + free_blocks(); + } + return 0; + } + prev_block= block; + block= block->next; + } + + /* Node does not find should never happen */ + DBUG_ASSERT(0); + return 1; + } + +private: + uint reserved_blocks; + + /** + A sequence memory which contains BLOCK_TRANX_NODES Tranx_nodes. + + BLOCK_TRANX_NODES The number of Tranx_nodes which are in a Block. + + next Every Block has a 'next' pointer which points to the next Block. + These linking Blocks constitute a Block link table. + */ + struct Block { + Block *next; + Tranx_node nodes[BLOCK_TRANX_NODES]; + }; + + /** + The 'first_block' is the head of the Block link table; + */ + Block *first_block; + /** + The 'last_block' is the rear of the Block link table; + */ + Block *last_block; + + /** + current_block always points the Block in the Block link table in + which the last allocated node is. The Blocks before it are all in use + and the Blocks after it are all free. + */ + Block *current_block; + + /** + It always points to the last node which has been allocated in the + current_block. + */ + int last_node; + + /** + How many Blocks are in the Block link table. + */ + uint block_num; + + /** + Allocate a block and then assign it to current_block. + */ + int allocate_block() + { + Block *block= (Block *)my_malloc(sizeof(Block), MYF(0)); + if (block) + { + block->next= NULL; + + if (first_block == NULL) + first_block= block; + else + last_block->next= block; + + /* New Block is always put into the rear */ + last_block= block; + /* New Block is always the current_block */ + current_block= block; + ++block_num; + return 0; + } + return 1; + } + + /** + Free a given Block. + @param block The Block will be freed. + */ + void free_block(Block *block) + { + my_free(block); + --block_num; + } + + + /** + If there are some free Blocks and the total number of the Blocks in the + Block link table is larger than the 'reserved_blocks', Some free Blocks + will be freed until the total number of the Blocks is equal to the + 'reserved_blocks' or there is only one free Block behind the + 'current_block'. + */ + void free_blocks() + { + if (current_block == NULL || current_block->next == NULL) + return; + + /* One free Block is always kept behind the current block */ + Block *block= current_block->next->next; + while (block_num > reserved_blocks && block != NULL) + { + Block *next= block->next; + free_block(block); + block= next; + } + current_block->next->next= block; + if (block == NULL) + last_block= current_block->next; + } +}; + +/** + This class manages memory for active transaction list. + + We record each active transaction with a Tranx_node, each session + can have only one open transaction. Because of EVENT, the total + active transaction nodes can exceed the maximum allowed + connections. +*/ +class Active_tranx + :public Trace { +private: + + Tranx_node_allocator m_allocator; + /* These two record the active transaction list in sort order. */ + Tranx_node *m_trx_front, *m_trx_rear; + + Tranx_node **m_trx_htb; /* A hash table on active transactions. */ + + int m_num_entries; /* maximum hash table entries */ + mysql_mutex_t *m_lock; /* mutex lock */ + + inline void assert_lock_owner(); + + inline unsigned int calc_hash(const unsigned char *key, size_t length); + unsigned int get_hash_value(const char *log_file_name, my_off_t log_file_pos); + + int compare(const char *log_file_name1, my_off_t log_file_pos1, + const Tranx_node *node2) { + return compare(log_file_name1, log_file_pos1, + node2->log_name, node2->log_pos); + } + int compare(const Tranx_node *node1, + const char *log_file_name2, my_off_t log_file_pos2) { + return compare(node1->log_name, node1->log_pos, + log_file_name2, log_file_pos2); + } + int compare(const Tranx_node *node1, const Tranx_node *node2) { + return compare(node1->log_name, node1->log_pos, + node2->log_name, node2->log_pos); + } + +public: + Active_tranx(mysql_mutex_t *lock, unsigned long trace_level); + ~Active_tranx(); + + /* Insert an active transaction node with the specified position. + * + * Return: + * 0: success; non-zero: error + */ + int insert_tranx_node(const char *log_file_name, my_off_t log_file_pos); + + /* Clear the active transaction nodes until(inclusive) the specified + * position. + * If log_file_name is NULL, everything will be cleared: the sorted + * list and the hash table will be reset to empty. + * + * Return: + * 0: success; non-zero: error + */ + int clear_active_tranx_nodes(const char *log_file_name, + my_off_t log_file_pos); + + /* Given a position, check to see whether the position is an active + * transaction's ending position by probing the hash table. + */ + bool is_tranx_end_pos(const char *log_file_name, my_off_t log_file_pos); + + /* Given two binlog positions, compare which one is bigger based on + * (file_name, file_position). + */ + static int compare(const char *log_file_name1, my_off_t log_file_pos1, + const char *log_file_name2, my_off_t log_file_pos2); + +}; + +/** + The extension class for the master of semi-synchronous replication +*/ +class Repl_semi_sync_master + :public Repl_semi_sync_base { + private: + Active_tranx *m_active_tranxs; /* active transaction list: the list will + be cleared when semi-sync switches off. */ + + /* True when init_object has been called */ + bool m_init_done; + + /* This cond variable is signaled when enough binlog has been sent to slave, + * so that a waiting trx can return the 'ok' to the client for a commit. + */ + mysql_cond_t COND_binlog_send; + + /* Mutex that protects the following state variables and the active + * transaction list. + * Under no cirumstances we can acquire mysql_bin_log.LOCK_log if we are + * already holding m_LOCK_binlog because it can cause deadlocks. + */ + mysql_mutex_t LOCK_binlog; + + /* This is set to true when m_reply_file_name contains meaningful data. */ + bool m_reply_file_name_inited; + + /* The binlog name up to which we have received replies from any slaves. */ + char m_reply_file_name[FN_REFLEN]; + + /* The position in that file up to which we have the reply from any slaves. */ + my_off_t m_reply_file_pos; + + /* This is set to true when we know the 'smallest' wait position. */ + bool m_wait_file_name_inited; + + /* NULL, or the 'smallest' filename that a transaction is waiting for + * slave replies. + */ + char m_wait_file_name[FN_REFLEN]; + + /* The smallest position in that file that a trx is waiting for: the trx + * can proceed and send an 'ok' to the client when the master has got the + * reply from the slave indicating that it already got the binlog events. + */ + my_off_t m_wait_file_pos; + + /* This is set to true when we know the 'largest' transaction commit + * position in the binlog file. + * We always maintain the position no matter whether semi-sync is switched + * on switched off. When a transaction wait timeout occurs, semi-sync will + * switch off. Binlog-dump thread can use the three fields to detect when + * slaves catch up on replication so that semi-sync can switch on again. + */ + bool m_commit_file_name_inited; + + /* The 'largest' binlog filename that a commit transaction is seeing. */ + char m_commit_file_name[FN_REFLEN]; + + /* The 'largest' position in that file that a commit transaction is seeing. */ + my_off_t m_commit_file_pos; + + /* All global variables which can be set by parameters. */ + volatile bool m_master_enabled; /* semi-sync is enabled on the master */ + unsigned long m_wait_timeout; /* timeout period(ms) during tranx wait */ + + bool m_state; /* whether semi-sync is switched */ + + /*Waiting for ACK before/after innodb commit*/ + ulong m_wait_point; + + void lock(); + void unlock(); + void cond_broadcast(); + int cond_timewait(struct timespec *wait_time); + + /* Is semi-sync replication on? */ + bool is_on() { + return (m_state); + } + + void set_master_enabled(bool enabled) { + m_master_enabled = enabled; + } + + /* Switch semi-sync off because of timeout in transaction waiting. */ + int switch_off(); + + /* Switch semi-sync on when slaves catch up. */ + int try_switch_on(int server_id, + const char *log_file_name, my_off_t log_file_pos); + + public: + Repl_semi_sync_master(); + ~Repl_semi_sync_master() {} + + void cleanup(); + + bool get_master_enabled() { + return m_master_enabled; + } + void set_trace_level(unsigned long trace_level) { + m_trace_level = trace_level; + if (m_active_tranxs) + m_active_tranxs->m_trace_level = trace_level; + } + + /* Set the transaction wait timeout period, in milliseconds. */ + void set_wait_timeout(unsigned long wait_timeout) { + m_wait_timeout = wait_timeout; + } + + /*set the ACK point, after binlog sync or after transaction commit*/ + void set_wait_point(unsigned long ack_point) + { + m_wait_point = ack_point; + } + + ulong wait_point() //no cover line + { + return m_wait_point; //no cover line + } + + /* Initialize this class after MySQL parameters are initialized. this + * function should be called once at bootstrap time. + */ + int init_object(); + + /* Enable the object to enable semi-sync replication inside the master. */ + int enable_master(); + + /* Enable the object to enable semi-sync replication inside the master. */ + int disable_master(); + + /* Add a semi-sync replication slave */ + void add_slave(); + + /* Remove a semi-sync replication slave */ + void remove_slave(); + + /* It parses a reply packet and call report_reply_binlog to handle it. */ + int report_reply_packet(uint32 server_id, const uchar *packet, + ulong packet_len); + + /* In semi-sync replication, reports up to which binlog position we have + * received replies from the slave indicating that it already get the events. + * + * Input: + * server_id - (IN) master server id number + * log_file_name - (IN) binlog file name + * end_offset - (IN) the offset in the binlog file up to which we have + * the replies from the slave + * + * Return: + * 0: success; non-zero: error + */ + int report_reply_binlog(uint32 server_id, + const char* log_file_name, + my_off_t end_offset); + + /* Commit a transaction in the final step. This function is called from + * InnoDB before returning from the low commit. If semi-sync is switch on, + * the function will wait to see whether binlog-dump thread get the reply for + * the events of the transaction. Remember that this is not a direct wait, + * instead, it waits to see whether the binlog-dump thread has reached the + * point. If the wait times out, semi-sync status will be switched off and + * all other transaction would not wait either. + * + * Input: (the transaction events' ending binlog position) + * trx_wait_binlog_name - (IN) ending position's file name + * trx_wait_binlog_pos - (IN) ending position's file offset + * + * Return: + * 0: success; non-zero: error + */ + int commit_trx(const char* trx_wait_binlog_name, + my_off_t trx_wait_binlog_pos); + + /*Wait for ACK after writing/sync binlog to file*/ + int wait_after_sync(const char* log_file, my_off_t log_pos); + + /*Wait for ACK after commting the transaction*/ + int wait_after_commit(THD* thd, bool all); + + /*Wait after the transaction is rollback*/ + int wait_after_rollback(THD *thd, bool all); + /*Store the current binlog position in m_active_tranxs. This position should + * be acked by slave*/ + int report_binlog_update(THD *thd, const char *log_file,my_off_t log_pos); + + int dump_start(THD* thd, + const char *log_file, + my_off_t log_pos); + + void dump_end(THD* thd); + + /* Reserve space in the replication event packet header: + * . slave semi-sync off: 1 byte - (0) + * . slave semi-sync on: 3 byte - (0, 0xef, 0/1} + * + * Input: + * packet - (IN) the header buffer + * + * Return: + * size of the bytes reserved for header + */ + int reserve_sync_header(String* packet); + + /* Update the sync bit in the packet header to indicate to the slave whether + * the master will wait for the reply of the event. If semi-sync is switched + * off and we detect that the slave is catching up, we switch semi-sync on. + * + * Input: + * THD - (IN) current dump thread + * packet - (IN) the packet containing the replication event + * log_file_name - (IN) the event ending position's file name + * log_file_pos - (IN) the event ending position's file offset + * need_sync - (IN) identify if flush_net is needed to call. + * server_id - (IN) master server id number + * + * Return: + * 0: success; non-zero: error + */ + int update_sync_header(THD* thd, unsigned char *packet, + const char *log_file_name, + my_off_t log_file_pos, + bool* need_sync); + + /* Called when a transaction finished writing binlog events. + * . update the 'largest' transactions' binlog event position + * . insert the ending position in the active transaction list if + * semi-sync is on + * + * Input: (the transaction events' ending binlog position) + * log_file_name - (IN) transaction ending position's file name + * log_file_pos - (IN) transaction ending position's file offset + * + * Return: + * 0: success; non-zero: error + */ + int write_tranx_in_binlog(const char* log_file_name, my_off_t log_file_pos); + + /* Read the slave's reply so that we know how much progress the slave makes + * on receive replication events. + */ + int flush_net(THD* thd, const char *event_buf); + + /* Export internal statistics for semi-sync replication. */ + void set_export_stats(); + + /* 'reset master' command is issued from the user and semi-sync need to + * go off for that. + */ + int after_reset_master(); + + /*called before reset master*/ + int before_reset_master(); + + void check_and_switch(); +}; + +enum rpl_semi_sync_master_wait_point_t { + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_BINLOG_SYNC, + SEMI_SYNC_MASTER_WAIT_POINT_AFTER_STORAGE_COMMIT, +}; + +extern Repl_semi_sync_master repl_semisync_master; +extern Ack_receiver ack_receiver; + +/* System and status variables for the master component */ +extern my_bool rpl_semi_sync_master_enabled; +extern my_bool rpl_semi_sync_master_status; +extern ulong rpl_semi_sync_master_wait_point; +extern ulong rpl_semi_sync_master_clients; +extern ulong rpl_semi_sync_master_timeout; +extern ulong rpl_semi_sync_master_trace_level; +extern ulong rpl_semi_sync_master_yes_transactions; +extern ulong rpl_semi_sync_master_no_transactions; +extern ulong rpl_semi_sync_master_off_times; +extern ulong rpl_semi_sync_master_wait_timeouts; +extern ulong rpl_semi_sync_master_timefunc_fails; +extern ulong rpl_semi_sync_master_num_timeouts; +extern ulong rpl_semi_sync_master_wait_sessions; +extern ulong rpl_semi_sync_master_wait_pos_backtraverse; +extern ulong rpl_semi_sync_master_avg_trx_wait_time; +extern ulong rpl_semi_sync_master_avg_net_wait_time; +extern ulonglong rpl_semi_sync_master_net_wait_num; +extern ulonglong rpl_semi_sync_master_trx_wait_num; +extern ulonglong rpl_semi_sync_master_net_wait_time; +extern ulonglong rpl_semi_sync_master_trx_wait_time; +extern unsigned long long rpl_semi_sync_master_request_ack; +extern unsigned long long rpl_semi_sync_master_get_ack; + +/* + This indicates whether we should keep waiting if no semi-sync slave + is available. + 0 : stop waiting if detected no avaialable semi-sync slave. + 1 (default) : keep waiting until timeout even no available semi-sync slave. +*/ +extern char rpl_semi_sync_master_wait_no_slave; +extern Repl_semi_sync_master repl_semisync_master; + +extern PSI_stage_info stage_waiting_for_semi_sync_ack_from_slave; +extern PSI_stage_info stage_reading_semi_sync_ack; +extern PSI_stage_info stage_waiting_for_semi_sync_slave; + +void semi_sync_master_deinit(); + +#endif /* SEMISYNC_MASTER_H */ diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc new file mode 100644 index 00000000000..ac17c7de40b --- /dev/null +++ b/sql/semisync_master_ack_receiver.cc @@ -0,0 +1,307 @@ +/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + 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 */ + +#include <my_global.h> +#include "semisync_master.h" +#include "semisync_master_ack_receiver.h" + +#ifdef HAVE_PSI_MUTEX_INTERFACE +extern PSI_mutex_key key_LOCK_ack_receiver; +extern PSI_cond_key key_COND_ack_receiver; +#endif +#ifdef HAVE_PSI_THREAD_INTERFACE +extern PSI_thread_key key_thread_ack_receiver; +#endif +extern Repl_semi_sync_master repl_semisync; + +/* Callback function of ack receive thread */ +pthread_handler_t ack_receive_handler(void *arg) +{ + Ack_receiver *recv= reinterpret_cast<Ack_receiver *>(arg); + + my_thread_init(); + recv->run(); + my_thread_end(); + + return NULL; +} + +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_cond_init(key_COND_ack_receiver, &m_cond, NULL); + m_pid= 0; + + DBUG_VOID_RETURN; +} + +void Ack_receiver::cleanup() +{ + DBUG_ENTER("Ack_receiver::~Ack_receiver"); + + stop(); + mysql_mutex_destroy(&m_mutex); + mysql_cond_destroy(&m_cond); + + DBUG_VOID_RETURN; +} + +bool Ack_receiver::start() +{ + DBUG_ENTER("Ack_receiver::start"); + + mysql_mutex_lock(&m_mutex); + if(m_status == ST_DOWN) + { + pthread_attr_t attr; + + m_status= ST_UP; + + if (DBUG_EVALUATE_IF("rpl_semisync_simulate_create_thread_failure", 1, 0) || + pthread_attr_init(&attr) != 0 || + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0 || +#ifndef _WIN32 + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0 || +#endif + mysql_thread_create(key_thread_ack_receiver, &m_pid, + &attr, ack_receive_handler, this)) + { + sql_print_error("Failed to start semi-sync ACK receiver thread, " + " could not create thread(errno:%d)", errno); + + m_status= ST_DOWN; + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(true); + } + (void) pthread_attr_destroy(&attr); + } + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(false); +} + +void Ack_receiver::stop() +{ + DBUG_ENTER("Ack_receiver::stop"); + + mysql_mutex_lock(&m_mutex); + if (m_status == ST_UP) + { + m_status= ST_STOPPING; + mysql_cond_broadcast(&m_cond); + + while (m_status == ST_STOPPING) + mysql_cond_wait(&m_cond, &m_mutex); + + DBUG_ASSERT(m_status == ST_DOWN); + + m_pid= 0; + } + mysql_mutex_unlock(&m_mutex); + + DBUG_VOID_RETURN; +} + +bool Ack_receiver::add_slave(THD *thd) +{ + Slave *slave; + DBUG_ENTER("Ack_receiver::add_slave"); + + if (!(slave= new Slave)) + DBUG_RETURN(true); + + slave->thd= thd; + slave->vio= *thd->net.vio; + slave->vio.mysql_socket.m_psi= NULL; + slave->vio.read_timeout= 1; + + mysql_mutex_lock(&m_mutex); + m_slaves.push_back(slave); + m_slaves_changed= true; + mysql_cond_broadcast(&m_cond); + mysql_mutex_unlock(&m_mutex); + + DBUG_RETURN(false); +} + +void Ack_receiver::remove_slave(THD *thd) +{ + I_List_iterator<Slave> it(m_slaves); + Slave *slave; + DBUG_ENTER("Ack_receiver::remove_slave"); + + mysql_mutex_lock(&m_mutex); + + while ((slave= it++)) + { + if (slave->thd == thd) + { + delete slave; + m_slaves_changed= true; + break; + } + } + mysql_mutex_unlock(&m_mutex); + + DBUG_VOID_RETURN; +} + +inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage) +{ + MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__); +} + +inline void Ack_receiver::wait_for_slave_connection() +{ + set_stage_info(stage_waiting_for_semi_sync_slave); + mysql_cond_wait(&m_cond, &m_mutex); +} + +my_socket Ack_receiver::get_slave_sockets(fd_set *fds, uint *count) +{ + my_socket max_fd= INVALID_SOCKET; + Slave *slave; + I_List_iterator<Slave> it(m_slaves); + + *count= 0; + FD_ZERO(fds); + while ((slave= it++)) + { + (*count)++; + my_socket fd= slave->sock_fd(); + max_fd= (fd > max_fd ? fd : max_fd); + FD_SET(fd, fds); + } + + return max_fd; +} + +/* Auxilary function to initialize a NET object with given net buffer. */ +static void init_net(NET *net, unsigned char *buff, unsigned int buff_len) +{ + memset(net, 0, sizeof(NET)); + net->max_packet= buff_len; + net->buff= buff; + net->buff_end= buff + buff_len; + net->read_pos= net->buff; +} + +void Ack_receiver::run() +{ + // skip LOCK_global_system_variables due to the 3rd arg + THD *thd= new THD(next_thread_id(), false, true); + NET net; + unsigned char net_buff[REPLY_MESSAGE_MAX_LENGTH]; + fd_set read_fds; + my_socket max_fd= INVALID_SOCKET; + Slave *slave; + + my_thread_init(); + + DBUG_ENTER("Ack_receiver::run"); + + sql_print_information("Starting ack receiver thread"); + thd->system_thread= SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND; + thd->thread_stack= (char*) &thd; + thd->store_globals(); + thd->security_ctx->skip_grants(); + thread_safe_increment32(&service_thread_count); + thd->set_command(COM_DAEMON); + init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH); + + mysql_mutex_lock(&m_mutex); + m_slaves_changed= true; + mysql_mutex_unlock(&m_mutex); + + while (1) + { + fd_set fds; + int ret; + uint slave_count; + + mysql_mutex_lock(&m_mutex); + if (unlikely(m_status == ST_STOPPING)) + goto end; + + set_stage_info(stage_waiting_for_semi_sync_ack_from_slave); + if (unlikely(m_slaves_changed)) + { + if (unlikely(m_slaves.is_empty())) + { + wait_for_slave_connection(); + mysql_mutex_unlock(&m_mutex); + continue; + } + + max_fd= get_slave_sockets(&read_fds, &slave_count); + m_slaves_changed= false; + DBUG_PRINT("info", ("fd count %u, max_fd %d", slave_count,(int) max_fd)); + } + + struct timeval tv= {1, 0}; + fds= read_fds; + /* select requires max fd + 1 for the first argument */ + ret= select((int)(max_fd+1), &fds, NULL, NULL, &tv); + if (ret <= 0) + { + mysql_mutex_unlock(&m_mutex); + + ret= DBUG_EVALUATE_IF("rpl_semisync_simulate_select_error", -1, ret); + + if (ret == -1) + sql_print_information("Failed to select() on semi-sync dump sockets, " + "error: errno=%d", socket_errno); + /* Sleep 1us, so other threads can catch the m_mutex easily. */ + my_sleep(1); + continue; + } + + set_stage_info(stage_reading_semi_sync_ack); + I_List_iterator<Slave> it(m_slaves); + + while ((slave= it++)) + { + if (FD_ISSET(slave->sock_fd(), &fds)) + { + ulong len; + + net_clear(&net, 0); + net.vio= &slave->vio; + + len= my_net_read(&net); + if (likely(len != packet_error)) + repl_semisync_master.report_reply_packet(slave->server_id(), + net.read_pos, len); + else if (net.last_errno == ER_NET_READ_ERROR) + FD_CLR(slave->sock_fd(), &read_fds); + } + } + mysql_mutex_unlock(&m_mutex); + } +end: + sql_print_information("Stopping ack receiver thread"); + m_status= ST_DOWN; + delete thd; + thread_safe_decrement32(&service_thread_count); + signal_thd_deleted(); + mysql_cond_broadcast(&m_cond); + mysql_mutex_unlock(&m_mutex); + DBUG_VOID_RETURN; +} diff --git a/sql/semisync_master_ack_receiver.h b/sql/semisync_master_ack_receiver.h new file mode 100644 index 00000000000..619748a2159 --- /dev/null +++ b/sql/semisync_master_ack_receiver.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + 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 SEMISYNC_MASTER_ACK_RECEIVER_DEFINED +#define SEMISYNC_MASTER_ACK_RECEIVER_DEFINED + +#include "my_global.h" +#include "my_pthread.h" +#include "sql_class.h" +#include "semisync.h" +/** + Ack_receiver is responsible to control ack receive thread and maintain + slave information used by ack receive thread. + + There are mainly four operations on ack receive thread: + start: start ack receive thread + stop: stop ack receive thread + add_slave: maintain a new semisync slave's information + remove_slave: remove a semisync slave's information + */ +class Ack_receiver : public Repl_semi_sync_base +{ +public: + Ack_receiver(); + ~Ack_receiver() {} + void cleanup(); + /** + Notify ack receiver to receive acks on the dump session. + + It adds the given dump thread into the slave list and wakes + up ack thread if it is waiting for any slave coming. + + @param[in] thd THD of a dump thread. + + @return it return false if succeeds, otherwise true is returned. + */ + bool add_slave(THD *thd); + + /** + Notify ack receiver not to receive ack on the dump session. + + it removes the given dump thread from slave list. + + @param[in] thd THD of a dump thread. + */ + void remove_slave(THD *thd); + + /** + Start ack receive thread + + @return it return false if succeeds, otherwise true is returned. + */ + bool start(); + + /** + Stop ack receive thread + */ + void stop(); + + /** + The core of ack receive thread. + + It monitors all slaves' sockets and receives acks when they come. + */ + void run(); + + void set_trace_level(unsigned long trace_level) + { + m_trace_level= trace_level; + } +private: + enum status {ST_UP, ST_DOWN, ST_STOPPING}; + uint8 m_status; + /* + Protect m_status, m_slaves_changed and m_slaves. ack thread and other + session may access the variables at the same time. + */ + mysql_mutex_t m_mutex; + mysql_cond_t m_cond; + /* If slave list is updated(add or remove). */ + bool m_slaves_changed; + + class Slave :public ilink + { +public: + THD *thd; + Vio vio; + + my_socket sock_fd() { return vio.mysql_socket.fd; } + uint server_id() { return thd->variables.server_id; } + }; + + I_List<Slave> m_slaves; + + pthread_t m_pid; + +/* Declare them private, so no one can copy the object. */ + Ack_receiver(const Ack_receiver &ack_receiver); + Ack_receiver& operator=(const Ack_receiver &ack_receiver); + + void set_stage_info(const PSI_stage_info &stage); + void wait_for_slave_connection(); + my_socket get_slave_sockets(fd_set *fds, uint *count); +}; + +extern Ack_receiver ack_receiver; +#endif diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc new file mode 100644 index 00000000000..86d0176dac1 --- /dev/null +++ b/sql/semisync_slave.cc @@ -0,0 +1,251 @@ +/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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 */ + + +#include <my_global.h> +#include "semisync_slave.h" + +Repl_semi_sync_slave repl_semisync_slave; + +my_bool rpl_semi_sync_slave_enabled= 0; + +char rpl_semi_sync_slave_delay_master; +my_bool rpl_semi_sync_slave_status= 0; +ulong rpl_semi_sync_slave_trace_level; + +/* + indicate whether or not the slave should send a reply to the master. + + This is set to true in repl_semi_slave_read_event if the current + event read is the last event of a transaction. And the value is + checked in repl_semi_slave_queue_event. +*/ +bool semi_sync_need_reply= false; +unsigned int rpl_semi_sync_slave_kill_conn_timeout; +unsigned long long rpl_semi_sync_slave_send_ack = 0; + +int Repl_semi_sync_slave::init_object() +{ + int result= 0; + + m_init_done = true; + + /* References to the parameter works after set_options(). */ + set_slave_enabled(rpl_semi_sync_slave_enabled); + set_trace_level(rpl_semi_sync_slave_trace_level); + set_delay_master(rpl_semi_sync_slave_delay_master); + set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout); + + return result; +} + +int Repl_semi_sync_slave::slave_read_sync_header(const char *header, + unsigned long total_len, + int *semi_flags, + const char **payload, + unsigned long *payload_len) +{ + int read_res = 0; + DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header"); + + if (rpl_semi_sync_slave_status) + { + if (DBUG_EVALUATE_IF("semislave_corrupt_log", 0, 1) + && (unsigned char)(header[0]) == k_packet_magic_num) + { + semi_sync_need_reply = (header[1] & k_packet_flag_sync); + *payload_len = total_len - 2; + *payload = header + 2; + + DBUG_PRINT("semisync", ("%s: reply - %d", + "Repl_semi_sync_slave::slave_read_sync_header", + semi_sync_need_reply)); + + if (semi_sync_need_reply) + *semi_flags |= SEMI_SYNC_NEED_ACK; + if (is_delay_master()) + *semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC; + } + else + { + sql_print_error("Missing magic number for semi-sync packet, packet " + "len: %lu", total_len); + read_res = -1; + } + } else { + *payload= header; + *payload_len= total_len; + } + + DBUG_RETURN(read_res); +} + +int Repl_semi_sync_slave::slave_start(Master_info *mi) +{ + bool semi_sync= get_slave_enabled(); + + sql_print_information("Slave I/O thread: Start %s replication to\ + master '%s@%s:%d' in log '%s' at position %lu", + semi_sync ? "semi-sync" : "asynchronous", + const_cast<char *>(mi->user), mi->host, mi->port, + const_cast<char *>(mi->master_log_name), + (unsigned long)(mi->master_log_pos)); + + if (semi_sync && !rpl_semi_sync_slave_status) + rpl_semi_sync_slave_status= 1; + + /*clear the counter*/ + rpl_semi_sync_slave_send_ack= 0; + return 0; +} + +int Repl_semi_sync_slave::slave_stop(Master_info *mi) +{ + if (rpl_semi_sync_slave_status) + rpl_semi_sync_slave_status= 0; + if (get_slave_enabled()) + kill_connection(mi->mysql); + return 0; +} + +int Repl_semi_sync_slave::reset_slave(Master_info *mi) +{ + return 0; +} + +void Repl_semi_sync_slave::kill_connection(MYSQL *mysql) +{ + if (!mysql) + return; + + char kill_buffer[30]; + MYSQL *kill_mysql = NULL; + kill_mysql = mysql_init(kill_mysql); + mysql_options(kill_mysql, MYSQL_OPT_CONNECT_TIMEOUT, &m_kill_conn_timeout); + mysql_options(kill_mysql, MYSQL_OPT_READ_TIMEOUT, &m_kill_conn_timeout); + mysql_options(kill_mysql, MYSQL_OPT_WRITE_TIMEOUT, &m_kill_conn_timeout); + + bool ret= (!mysql_real_connect(kill_mysql, mysql->host, + mysql->user, mysql->passwd,0, mysql->port, mysql->unix_socket, 0)); + if (DBUG_EVALUATE_IF("semisync_slave_failed_kill", 1, 0) || ret) + { + sql_print_information("cannot connect to master to kill slave io_thread's " + "connection"); + if (!ret) + mysql_close(kill_mysql); + return; + } + size_t kill_buffer_length = my_snprintf(kill_buffer, 30, "KILL %lu", + mysql->thread_id); + mysql_real_query(kill_mysql, kill_buffer, (ulong)kill_buffer_length); + mysql_close(kill_mysql); +} + +int Repl_semi_sync_slave::request_transmit(Master_info *mi) +{ + MYSQL *mysql= mi->mysql; + MYSQL_RES *res= 0; + MYSQL_ROW row; + const char *query; + + if (!get_slave_enabled()) + return 0; + + query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'"; + if (mysql_real_query(mysql, query, (ulong)strlen(query)) || + !(res= mysql_store_result(mysql))) + { + sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql)); + return 1; + } + + row= mysql_fetch_row(res); + if (DBUG_EVALUATE_IF("master_not_support_semisync", 1, 0) + || !row) + { + /* Master does not support semi-sync */ + sql_print_warning("Master server does not support semi-sync, " + "fallback to asynchronous replication"); + rpl_semi_sync_slave_status= 0; + mysql_free_result(res); + return 0; + } + mysql_free_result(res); + + /* + Tell master dump thread that we want to do semi-sync + replication + */ + query= "SET @rpl_semi_sync_slave= 1"; + if (mysql_real_query(mysql, query, (ulong)strlen(query))) + { + sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed"); + return 1; + } + mysql_free_result(mysql_store_result(mysql)); + rpl_semi_sync_slave_status= 1; + + return 0; +} + +int Repl_semi_sync_slave::slave_reply(Master_info *mi) +{ + MYSQL* mysql= mi->mysql; + const char *binlog_filename= const_cast<char *>(mi->master_log_name); + my_off_t binlog_filepos= mi->master_log_pos; + + NET *net= &mysql->net; + uchar reply_buffer[REPLY_MAGIC_NUM_LEN + + REPLY_BINLOG_POS_LEN + + REPLY_BINLOG_NAME_LEN]; + int reply_res = 0; + size_t name_len = strlen(binlog_filename); + + DBUG_ENTER("Repl_semi_sync_slave::slave_reply"); + + if (rpl_semi_sync_slave_status && semi_sync_need_reply) + { + /* Prepare the buffer of the reply. */ + reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num; + int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos); + memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET, + binlog_filename, + name_len + 1 /* including trailing '\0' */); + + DBUG_PRINT("semisync", ("%s: reply (%s, %lu)", + "Repl_semi_sync_slave::slave_reply", + binlog_filename, (ulong)binlog_filepos)); + + net_clear(net, 0); + /* Send the reply. */ + reply_res = my_net_write(net, reply_buffer, + name_len + REPLY_BINLOG_NAME_OFFSET); + if (!reply_res) + { + reply_res = DBUG_EVALUATE_IF("semislave_failed_net_flush", 1, net_flush(net)); + if (reply_res) + sql_print_error("Semi-sync slave net_flush() reply failed"); + rpl_semi_sync_slave_send_ack++; + } + else + { + sql_print_error("Semi-sync slave send reply failed: %s (%d)", + net->last_error, net->last_errno); + } + } + + DBUG_RETURN(reply_res); +} diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h new file mode 100644 index 00000000000..d65262f151d --- /dev/null +++ b/sql/semisync_slave.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2006 MySQL AB, 2009 Sun Microsystems, Inc. + Use is subject to license terms. + + 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 SEMISYNC_SLAVE_H +#define SEMISYNC_SLAVE_H + +#include "semisync.h" +#include "my_global.h" +#include "sql_priv.h" +#include "rpl_mi.h" +#include "mysql.h" + +class Master_info; + +/** + The extension class for the slave of semi-synchronous replication +*/ +class Repl_semi_sync_slave + :public Repl_semi_sync_base { +public: + Repl_semi_sync_slave() :m_slave_enabled(false) {} + ~Repl_semi_sync_slave() {} + + void set_trace_level(unsigned long trace_level) { + m_trace_level = trace_level; + } + + /* Initialize this class after MySQL parameters are initialized. this + * function should be called once at bootstrap time. + */ + int init_object(); + + bool get_slave_enabled() { + return m_slave_enabled; + } + + void set_slave_enabled(bool enabled) { + m_slave_enabled = enabled; + } + + bool is_delay_master(){ + return m_delay_master; + } + + void set_delay_master(bool enabled) { + m_delay_master = enabled; + } + + void set_kill_conn_timeout(unsigned int timeout) { + m_kill_conn_timeout = timeout; + } + + /* A slave reads the semi-sync packet header and separate the metadata + * from the payload data. + * + * Input: + * header - (IN) packet header pointer + * total_len - (IN) total packet length: metadata + payload + * semi_flags - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and + SEMI_SYNC_NEED_ACK + * payload - (IN) payload: the replication event + * payload_len - (IN) payload length + * + * Return: + * 0: success; non-zero: error + */ + int slave_read_sync_header(const char *header, unsigned long total_len, + int *semi_flags, + const char **payload, unsigned long *payload_len); + + /* A slave replies to the master indicating its replication process. It + * indicates that the slave has received all events before the specified + * binlog position. + */ + int slave_reply(Master_info* mi); + int slave_start(Master_info *mi); + int slave_stop(Master_info *mi); + int request_transmit(Master_info*); + void kill_connection(MYSQL *mysql); + int reset_slave(Master_info *mi); + +private: + /* True when init_object has been called */ + bool m_init_done; + bool m_slave_enabled; /* semi-sycn is enabled on the slave */ + bool m_delay_master; + unsigned int m_kill_conn_timeout; +}; + + +/* System and status variables for the slave component */ +extern my_bool rpl_semi_sync_slave_enabled; +extern my_bool rpl_semi_sync_slave_status; +extern ulong rpl_semi_sync_slave_trace_level; +extern Repl_semi_sync_slave repl_semisync_slave; + +extern char rpl_semi_sync_slave_delay_master; +extern unsigned int rpl_semi_sync_slave_kill_conn_timeout; +extern unsigned long long rpl_semi_sync_slave_send_ack; + +#endif /* SEMISYNC_SLAVE_H */ diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index 4b449f3991a..b59475645fa 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -419,7 +419,6 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, { sys_var *svar; LEX_CSTRING var; - uint not_used; lasts= (char *) memchr(token, separator, rest); @@ -433,7 +432,7 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, var.length= rest; /* Remove leading/trailing whitespace. */ - trim_whitespace(char_set, &var, ¬_used); + trim_whitespace(char_set, &var); if(!strcmp(var.str, "*")) { @@ -501,7 +500,6 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, for (;;) { LEX_CSTRING var; - uint not_used; lasts= (char *) memchr(token, separator, rest); @@ -515,7 +513,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, var.length= rest; /* Remove leading/trailing whitespace. */ - trim_whitespace(char_set, &var, ¬_used); + trim_whitespace(char_set, &var); if(!strcmp(var.str, "*") && !find_sys_var_ex(thd, var.str, var.length, throw_error, true)) @@ -974,7 +972,7 @@ bool Current_schema_tracker::store(THD *thd, String *buf) It saves length of database name and name of database name + length of saved length of database length. */ - length= db_length= thd->db_length; + length= db_length= thd->db.length; length += net_length_size(length); compile_time_assert(SESSION_TRACK_SCHEMA < 251); @@ -991,7 +989,7 @@ bool Current_schema_tracker::store(THD *thd, String *buf) buf->q_net_store_length(length); /* Length and current schema name */ - buf->q_net_store_data((const uchar *)thd->db, thd->db_length); + buf->q_net_store_data((const uchar *)thd->db.str, thd->db.length); reset(); @@ -1216,7 +1214,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf) tx_isolation_typelib as it hyphenates its items. */ buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL ")); - buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length); + buf->append(&isol[tx_isol_level - 1]); buf->append(STRING_WITH_LEN("; ")); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 77579146f23..1bdbd4e1f6e 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -16,7 +16,7 @@ /* variable declarations are in sys_vars.cc now !!! */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" #include "sql_class.h" // set_var.h: session_var_ptr #include "set_var.h" #include "sql_priv.h" @@ -324,14 +324,14 @@ do { \ sval.length= sval.str ? strlen(sval.str) : 0; \ break; \ case SHOW_LEX_STRING: \ - sval= *(LEX_STRING *) value; \ + sval= *(LEX_CSTRING *) value; \ break longlong sys_var::val_int(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base) { - LEX_STRING sval; + LEX_CSTRING sval; AutoWLock lock(&PLock_global_system_variables); const uchar *value= value_ptr(thd, type, base); *is_null= false; @@ -357,13 +357,13 @@ longlong sys_var::val_int(bool *is_null, String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value) { - static LEX_STRING bools[]= + static LEX_CSTRING bools[]= { - { C_STRING_WITH_LEN("OFF") }, - { C_STRING_WITH_LEN("ON") } + { STRING_WITH_LEN("OFF") }, + { STRING_WITH_LEN("ON") } }; - LEX_STRING sval; + LEX_CSTRING sval; switch (show_type()) { case_get_string_as_lex_string; @@ -395,7 +395,7 @@ String *sys_var::val_str(String *str, double sys_var::val_real(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base) { - LEX_STRING sval; + LEX_CSTRING sval; AutoWLock lock(&PLock_global_system_variables); const uchar *value= value_ptr(thd, type, base); *is_null= false; @@ -673,7 +673,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type scope) 0 Unknown variable (error message is given) */ -sys_var *intern_find_sys_var(const char *str, uint length) +sys_var *intern_find_sys_var(const char *str, size_t length) { sys_var *var; @@ -853,7 +853,7 @@ set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg, // names are utf8 if (!(value= new (thd->mem_root) Item_string_sys(thd, item->field_name.str, - item->field_name.length))) + (uint)item->field_name.length))) value=value_arg; /* Give error message later */ } else @@ -1162,6 +1162,7 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) { STRING_WITH_LEN("SET") }, // GET_SET 13 { STRING_WITH_LEN("DOUBLE") }, // GET_DOUBLE 14 { STRING_WITH_LEN("FLAGSET") }, // GET_FLAGSET 15 + { STRING_WITH_LEN("BOOLEAN") }, // GET_BIT 16 }; const ulong vartype= (var->option.var_type & GET_TYPE_MASK); const LEX_CSTRING *type= types + vartype; @@ -1293,3 +1294,222 @@ enum sys_var::where get_sys_var_value_origin(void *ptr) return sys_var::CONFIG; } + +/* + Find the next item in string of comma-separated items. + END_POS points at the end of the string. + ITEM_START and ITEM_END return the limits of the next item. + Returns true while items are available, false at the end. +*/ +static bool +engine_list_next_item(const char **pos, const char *end_pos, + const char **item_start, const char **item_end) +{ + if (*pos >= end_pos) + return false; + *item_start= *pos; + while (*pos < end_pos && **pos != ',') + ++*pos; + *item_end= *pos; + ++*pos; + return true; +} + + +static bool +resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx, + const char *pos, const char *pos_end, + bool error_on_unknown_engine, bool temp_copy) +{ + LEX_CSTRING item_str; + plugin_ref ref; + uint32 i; + THD *thd_or_null = (temp_copy ? thd : NULL); + + item_str.str= pos; + item_str.length= pos_end-pos; + ref= ha_resolve_by_name(thd_or_null, &item_str, false); + if (!ref) + { + if (error_on_unknown_engine) + { + ErrConvString err(pos, pos_end-pos, system_charset_info); + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr()); + return true; + } + return false; + } + /* Ignore duplicates, like --plugin-load does. */ + for (i= 0; i < *idx; ++i) + { + if (plugin_hton(list[i]) == plugin_hton(ref)) + { + if (!temp_copy) + plugin_unlock(NULL, ref); + return false; + } + } + list[*idx]= ref; + ++*idx; + return false; +} + + +/* + Helper for class Sys_var_pluginlist. + Resolve a comma-separated list of storage engine names to a null-terminated + array of plugin_ref. + + If TEMP_COPY is true, a THD must be given as well. In this case, the + allocated memory and locked plugins are registered in the THD and will + be freed / unlocked automatically. If TEMP_COPY is true, THD can be + passed as NULL, and resources must be freed explicitly later with + free_engine_list(). +*/ +plugin_ref * +resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len, + bool error_on_unknown_engine, bool temp_copy) +{ + uint32 count, idx; + const char *pos, *item_start, *item_end; + const char *str_arg_end= str_arg + str_arg_len; + plugin_ref *res; + + count= 0; + pos= str_arg; + for (;;) + { + if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end)) + break; + ++count; + } + + if (temp_copy) + res= (plugin_ref *)thd->calloc((count+1)*sizeof(*res)); + else + res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME)); + if (!res) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res))); + goto err; + } + + idx= 0; + pos= str_arg; + for (;;) + { + if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end)) + break; + DBUG_ASSERT(idx < count); + if (idx >= count) + break; + if (resolve_engine_list_item(thd, res, &idx, item_start, item_end, + error_on_unknown_engine, temp_copy)) + goto err; + } + + return res; + +err: + if (!temp_copy) + free_engine_list(res); + return NULL; +} + + +void +free_engine_list(plugin_ref *list) +{ + plugin_ref *p; + + if (!list) + return; + for (p= list; *p; ++p) + plugin_unlock(NULL, *p); + my_free(list); +} + + +plugin_ref * +copy_engine_list(plugin_ref *list) +{ + plugin_ref *p; + uint32 count, i; + + for (p= list, count= 0; *p; ++p, ++count) + ; + p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0)); + if (!p) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p))); + return NULL; + } + for (i= 0; i < count; ++i) + p[i]= my_plugin_lock(NULL, list[i]); + p[i] = NULL; + return p; +} + + +/* + Create a temporary copy of an engine list. The memory will be freed + (and the plugins unlocked) automatically, on the passed THD. +*/ +plugin_ref * +temp_copy_engine_list(THD *thd, plugin_ref *list) +{ + plugin_ref *p; + uint32 count, i; + + for (p= list, count= 0; *p; ++p, ++count) + ; + p= (plugin_ref *)thd->alloc((count+1)*sizeof(*p)); + if (!p) + { + my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p))); + return NULL; + } + for (i= 0; i < count; ++i) + p[i]= my_plugin_lock(thd, list[i]); + p[i] = NULL; + return p; +} + + +char * +pretty_print_engine_list(THD *thd, plugin_ref *list) +{ + plugin_ref *p; + size_t size; + char *buf, *pos; + + if (!list || !*list) + return thd->strmake("", 0); + + size= 0; + for (p= list; *p; ++p) + size+= plugin_name(*p)->length + 1; + buf= static_cast<char *>(thd->alloc(size)); + if (!buf) + return NULL; + pos= buf; + for (p= list; *p; ++p) + { + LEX_CSTRING *name; + size_t remain; + + remain= buf + size - pos; + DBUG_ASSERT(remain > 0); + if (remain <= 1) + break; + if (pos != buf) + { + pos= strmake(pos, ",", remain-1); + --remain; + } + name= plugin_name(*p); + pos= strmake(pos, name->str, MY_MIN(name->length, remain-1)); + } + *pos= '\0'; + return buf; +} diff --git a/sql/set_var.h b/sql/set_var.h index 17d1ff93ebc..d0143e1e524 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -158,6 +158,7 @@ public: case GET_BOOL: case GET_SET: case GET_FLAGSET: + case GET_BIT: return type != STRING_RESULT && type != INT_RESULT; case GET_DOUBLE: return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT; @@ -286,6 +287,7 @@ public: longlong longlong_value; ///< for signed integer double double_value; ///< for Sys_var_double plugin_ref plugin; ///< for Sys_var_plugin + plugin_ref *plugins; ///< for Sys_var_pluginlist Time_zone *time_zone; ///< for Sys_var_tz LEX_STRING string_value; ///< for Sys_var_charptr and others const void *ptr; ///< for Sys_var_struct @@ -424,6 +426,12 @@ int sys_var_init(); uint sys_var_elements(); int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags); void sys_var_end(void); +plugin_ref *resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len, + bool error_on_unknown_engine, bool temp_copy); +void free_engine_list(plugin_ref *list); +plugin_ref *copy_engine_list(plugin_ref *list); +plugin_ref *temp_copy_engine_list(THD *thd, plugin_ref *list); +char *pretty_print_engine_list(THD *thd, plugin_ref *list); #endif diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 7812cbd951d..c860b580a26 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -952,10 +952,10 @@ ER_OUT_OF_RESOURCES cze "Málo prostoru/paměti pro thread" dan "Udgået for tråde/hukommelse" nla "Geen thread geheugen meer; controleer of mysqld of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mysqld toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen" - eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space" + eng "Out of memory." est "Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MariaDB-le rohkema mälu kasutamise lubamine" fre "Manque de 'threads'/mémoire" - ger "Kein Speicher mehr vorhanden. Prüfen Sie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafür sorgen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten" + ger "Kein Speicher mehr vorhanden." greek "Πρόβλημα με τη διαθέσιμη μνήμη (Out of thread space/memory)" hun "Elfogyott a thread-memoria" ita "Fine dello spazio/memoria per i thread" @@ -964,14 +964,14 @@ ER_OUT_OF_RESOURCES nor "Tomt for tråd plass/minne" norwegian-ny "Tomt for tråd plass/minne" pol "Zbyt mało miejsca/pamięci dla w?tku" - por "Sem memória. Verifique se o mysqld ou algum outro processo está usando toda memória disponível. Se não, você pode ter que usar 'ulimit' para permitir ao mysqld usar mais memória ou você pode adicionar mais área de 'swap'" - rum "Out of memory; Verifica daca mysqld sau vreun alt proces foloseste toate memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui mysqld sa foloseasca mai multa memorie ori adauga mai mult spatiu pentru swap (swap space)" - rus "Недостаточно памяти; удостоверьтесь, что mysqld или какой-либо другой процесс не занимает всю доступную память. Если нет, то вы можете использовать ulimit, чтобы выделить для mysqld больше памяти, или увеличить объем файла подкачки" - serbian "Nema memorije; Proverite da li MariaDB server ili neki drugi proces koristi svu slobodnu memoriju. (UNIX: Ako ne, probajte da upotrebite 'ulimit' komandu da biste dozvolili daemon-u da koristi više memorije ili probajte da dodate više swap memorije)" + por "Sem memória." + rum "Out of memory." + rus "Недостаточно памяти." + serbian "Nema memorije." slo "Málo miesta-pamäti pre vlákno" spa "Memoria/espacio de tranpaso insuficiente" - swe "Fick slut på minnet. Kontrollera om mysqld eller någon annan process använder allt tillgängligt minne. Om inte, försök använda 'ulimit' eller allokera mera swap" - ukr "Брак пам'яті; Перевірте чи mysqld або якісь інші процеси використовують усю доступну пам'ять. Як ні, то ви можете скористатися 'ulimit', аби дозволити mysqld використовувати більше пам'яті або ви можете додати більше місця під свап" + swe "Fick slut på minnet." + ukr "Брак пам'яті." ER_BAD_HOST_ERROR 08S01 cze "Nemohu zjistit jméno stroje pro Vaši adresu" dan "Kan ikke få værtsnavn for din adresse" @@ -4528,18 +4528,18 @@ ER_TABLE_CANT_HANDLE_FT spa "El tipo de tabla usada (%s) no soporta índices FULLTEXT" swe "Tabelltypen (%s) har inte hantering av FULLTEXT-index" ukr "Використаний тип таблиці (%s) не підтримує FULLTEXT індексів" -ER_CANNOT_ADD_FOREIGN - nla "Kan foreign key beperking niet toevoegen" - eng "Cannot add foreign key constraint" - fre "Impossible d'ajouter des contraintes d'index externe" - ger "Fremdschlüssel-Beschränkung kann nicht hinzugefügt werden" - ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint)" - jpn "外部キー制約を追加できません。" - por "Não pode acrescentar uma restrição de chave estrangeira" - rus "Невозможно добавить ограничения внешнего ключа" - serbian "Ne mogu da dodam proveru spoljnog ključa" - spa "No puede adicionar clave extranjera constraint" - swe "Kan inte lägga till 'FOREIGN KEY constraint'" +ER_CANNOT_ADD_FOREIGN + nla "Kan foreign key beperking niet toevoegen vor `%s`" + eng "Cannot add foreign key constraint for `%s`" + fre "Impossible d'ajouter des contraintes d'index externe à `%s`" + ger "Fremdschlüssel-Beschränkung kann nicht hinzugefügt werden für `%s`" + ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint) a `%s`" + jpn "`%s` 外部キー制約を追加できません。" + por "Não pode acrescentar uma restrição de chave estrangeira para `%s`" + rus "Невозможно добавить ограничения внешнего ключа для `%s`" + serbian "Ne mogu da dodam proveru spoljnog ključa na `%s`" + spa "No puede adicionar clave extranjera constraint para `%s`" + swe "Kan inte lägga till 'FOREIGN KEY constraint' för `%s`'" ER_NO_REFERENCED_ROW 23000 nla "Kan onderliggende rij niet toevoegen: foreign key beperking gefaald" eng "Cannot add or update a child row: a foreign key constraint fails" @@ -5752,8 +5752,8 @@ ER_MAX_PREPARED_STMT_COUNT_REACHED 42000 eng "Can't create more than max_prepared_stmt_count statements (current value: %lu)" ger "Kann nicht mehr Anweisungen als max_prepared_stmt_count erzeugen (aktueller Wert: %lu)" ER_VIEW_RECURSIVE - eng "`%-.192s`.`%-.192s` contains view recursion" - ger "`%-.192s`.`%-.192s` enthält View-Rekursion" + eng "%`s.%`s contains view recursion" + ger "%`s.%`s enthält View-Rekursion" ER_NON_GROUPING_FIELD_USED 42000 eng "Non-grouping field '%-.192s' is used in %-.64s clause" ger "In der %-.192s-Klausel wird das die Nicht-Gruppierungsspalte '%-.64s' verwendet" @@ -6249,37 +6249,37 @@ ER_BINLOG_LOGGING_IMPOSSIBLE eng "Binary logging not possible. Message: %s" ger "Binärlogging nicht möglich. Meldung: %s" ER_VIEW_NO_CREATION_CTX - eng "View `%-.64s`.`%-.64s` has no creation context" - ger "View `%-.64s`.`%-.64s` hat keinen Erzeugungskontext" + eng "View %`s.%`s has no creation context" + ger "View %`s.%`s hat keinen Erzeugungskontext" ER_VIEW_INVALID_CREATION_CTX - eng "Creation context of view `%-.64s`.`%-.64s' is invalid" - ger "Erzeugungskontext des Views`%-.64s`.`%-.64s' ist ungültig" + eng "Creation context of view %`s.%`s is invalid" + ger "Erzeugungskontext des Views%`s.%`s ist ungültig" ER_SR_INVALID_CREATION_CTX - eng "Creation context of stored routine `%-.64s`.`%-.64s` is invalid" - ger "Erzeugungskontext der gespeicherten Routine`%-.64s`.`%-.64s` ist ungültig" + eng "Creation context of stored routine %`s.%`s is invalid" + ger "Erzeugungskontext der gespeicherten Routine%`s.%`s ist ungültig" ER_TRG_CORRUPTED_FILE - eng "Corrupted TRG file for table `%-.64s`.`%-.64s`" - ger "Beschädigte TRG-Datei für Tabelle `%-.64s`.`%-.64s`" + eng "Corrupted TRG file for table %`s.%`s" + ger "Beschädigte TRG-Datei für Tabelle %`s.%`s" ER_TRG_NO_CREATION_CTX - eng "Triggers for table `%-.64s`.`%-.64s` have no creation context" - ger "Trigger für Tabelle `%-.64s`.`%-.64s` haben keinen Erzeugungskontext" + eng "Triggers for table %`s.%`s have no creation context" + ger "Trigger für Tabelle %`s.%`s haben keinen Erzeugungskontext" ER_TRG_INVALID_CREATION_CTX - eng "Trigger creation context of table `%-.64s`.`%-.64s` is invalid" - ger "Trigger-Erzeugungskontext der Tabelle `%-.64s`.`%-.64s` ist ungültig" + eng "Trigger creation context of table %`s.%`s is invalid" + ger "Trigger-Erzeugungskontext der Tabelle %`s.%`s ist ungültig" ER_EVENT_INVALID_CREATION_CTX - eng "Creation context of event `%-.64s`.`%-.64s` is invalid" - ger "Erzeugungskontext des Events `%-.64s`.`%-.64s` ist ungültig" + eng "Creation context of event %`s.%`s is invalid" + ger "Erzeugungskontext des Events %`s.%`s ist ungültig" ER_TRG_CANT_OPEN_TABLE - eng "Cannot open table for trigger `%-.64s`.`%-.64s`" - ger "Kann Tabelle für den Trigger `%-.64s`.`%-.64s` nicht öffnen" + eng "Cannot open table for trigger %`s.%`s" + ger "Kann Tabelle für den Trigger %`s.%`s nicht öffnen" ER_CANT_CREATE_SROUTINE - eng "Cannot create stored routine `%-.64s`. Check warnings" - ger "Kann gespeicherte Routine `%-.64s` nicht erzeugen. Beachten Sie die Warnungen" + eng "Cannot create stored routine %`s. Check warnings" + ger "Kann gespeicherte Routine %`s nicht erzeugen. Beachten Sie die Warnungen" ER_UNUSED_11 eng "You should never see it" ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT - eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement" - ger "Der BINLOG-Anweisung vom Typ `%s` ging keine BINLOG-Anweisung zur Formatbeschreibung voran" + eng "The BINLOG statement of type %s was not preceded by a format description BINLOG statement" + ger "Der BINLOG-Anweisung vom Typ %s ging keine BINLOG-Anweisung zur Formatbeschreibung voran" ER_SLAVE_CORRUPT_EVENT eng "Corrupted replication event was detected" ger "Beschädigtes Replikationsereignis entdeckt" @@ -6596,7 +6596,7 @@ ER_MULTI_UPDATE_KEY_CONFLICT # When translating this error message make sure to include "ALTER TABLE" in the # message as mysqlcheck parses the error message looking for ALTER TABLE. ER_TABLE_NEEDS_REBUILD - eng "Table rebuild required. Please do \"ALTER TABLE `%-.32s` FORCE\" or dump/reload to fix it!" + eng "Table rebuild required. Please do \"ALTER TABLE %`s FORCE\" or dump/reload to fix it!" WARN_OPTION_BELOW_LIMIT eng "The value of '%s' should be no less than the value of '%s'" @@ -6899,8 +6899,8 @@ ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 ER_SLAVE_SILENT_RETRY_TRANSACTION eng "Slave must silently retry current transaction" -ER_DISCARD_FK_CHECKS_RUNNING - eng "There is a foreign key check running on table '%-.192s'. Cannot discard the table" +ER_UNUSED_22 + eng "You should never see it" ER_TABLE_SCHEMA_MISMATCH eng "Schema mismatch (%s)" @@ -7785,6 +7785,127 @@ ER_UNKNOWN_SEQUENCES 42S02 ER_UNKNOWN_VIEW 42S02 eng "Unknown VIEW: '%-.300s'" ER_WRONG_INSERT_INTO_SEQUENCE - eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a squence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead." + eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead." ER_SP_STACK_TRACE eng "At line %u in %s" +ER_COMPRESSED_COLUMN_USED_AS_KEY + eng "Compressed column '%-.192s' can't be used in key specification" +ER_UNKNOWN_COMPRESSION_METHOD + eng "Unknown compression method: %s" +ER_WRONG_NUMBER_OF_VALUES_IN_TVC + eng "The used table value constructor has a different number of values" +ER_FIELD_REFERENCE_IN_TVC + eng "Field reference '%-.192s' can't be used in table value constructor" +ER_WRONG_TYPE_FOR_PERCENTILE_FUNC + eng "Numeric datatype is required for %s function" +ER_ARGUMENT_NOT_CONSTANT + eng "Argument to the %s function is not a constant for a partition" +ER_ARGUMENT_OUT_OF_RANGE + eng "Argument to the %s function does not belong to the range [0,1]" +ER_WRONG_TYPE_OF_ARGUMENT + eng "%s function only accepts arguments that can be converted to numerical types" +ER_NOT_AGGREGATE_FUNCTION + eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)" +ER_INVALID_AGGREGATE_FUNCTION + eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function" +ER_INVALID_VALUE_TO_LIMIT + eng "Limit only accepts integer values" +ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT + eng "Invisible column %`s must have a default value" + + +# MariaDB error numbers related to System Versioning + +ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING + eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld" + +ER_VERS_FIELD_WRONG_TYPE + eng "%`s must be of type %s for system-versioned table %`s" + +ER_VERS_ENGINE_UNSUPPORTED + eng "Transaction system versioning for %`s is not supported" + +ER_UNUSED_23 + eng "You should never see it" + +ER_PARTITION_WRONG_TYPE + eng "Wrong partitioning type, expected type: %`s" + +WARN_VERS_PART_FULL + eng "Versioned table %`s.%`s: partition %`s is full, add more HISTORY partitions" + +WARN_VERS_PARAMETERS + eng "Maybe missing parameters: %s" + +WARN_VERS_PART_ROTATION + eng "Versioned table %`s.%`s: switching from partition %`s to %`s" + +WARN_VERS_TRX_MISSING + eng "VTQ missing transaction ID %lu" + +WARN_VERS_PART_NON_HISTORICAL + eng "Partition %`s contains non-historical data" + +ER_VERS_ALTER_NOT_ALLOWED + eng "Not allowed for system-versioned %`s.%`s. Change @@system_versioning_alter_history to proceed with ALTER." + +ER_VERS_ALTER_ENGINE_PROHIBITED + eng "Not allowed for system-versioned %`s.%`s. Change to/from native system versioning engine is prohibited." + +ER_VERS_RANGE_PROHIBITED + eng "SYSTEM_TIME range selector is prohibited" + +ER_VERS_VTMD_ERROR + eng "VTMD error: %s" + +ER_VERS_TABLE_MUST_HAVE_COLUMNS + eng "Table %`s must have at least one versioned column" + +ER_VERS_NOT_VERSIONED + eng "Table %`s is not system-versioned" + +ER_MISSING + eng "Wrong parameters for %`s: missing '%s'" + +ER_VERS_PERIOD_COLUMNS + eng "PERIOD FOR SYSTEM_TIME must use columns %`s and %`s" + +ER_PART_WRONG_VALUE + eng "Wrong parameters for partitioned %`s: wrong value for '%s'" + +ER_VERS_WRONG_PARTS + eng "Wrong partitions for %`s: must have at least one HISTORY and exactly one last CURRENT" + +ER_VERS_NO_TRX_ID + eng "TRX_ID %llu not found in `mysql.transaction_registry`" + +ER_VERS_ALTER_SYSTEM_FIELD + eng "Can not change system versioning field %`s" + +ER_VERS_SYS_FIELD_EXISTS + eng "System versioning field %`s exists" + +ER_NOT_LOG_TABLE + eng "Table %`s.%`s is not a log table" + +ER_VERS_TRT_IS_DISABLED + eng "Transaction registry is disabled" + +ER_VERS_DUPLICATE_ROW_START_END + eng "Duplicate ROW %s column %`s" + +ER_VERS_ALREADY_VERSIONED + eng "Table %`s is already system-versioned" + +ER_VERS_TRUNCATE_VIEW + eng "DELETE HISTORY from VIEW is prohibited" + +ER_VERS_TEMPORARY + eng "System versioning prohibited for TEMPORARY tables" + +ER_VERS_INPLACE_NOT_IMPLEMENTED + eng "Not implemented for system-versioned tables" +ER_INDEX_FILE_FULL + eng "The index file for table '%-.192s' is full" +ER_UPDATED_COLUMN_ONLY_ONCE + eng "The column %`s.%`s cannot be changed more than once in a single UPDATE statement" diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 68e801d5885..0452e181e22 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" +#include "mariadb.h" #include <signal.h> //#include "sys_vars.h" @@ -163,7 +163,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) "where mysqld died. If you see no messages after this, something went\n" "terribly wrong...\n"); my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, - (ulong)my_thread_stack_size); + (ulong)my_thread_stack_size, 0); } if (thd) { diff --git a/sql/slave.cc b/sql/slave.cc index d124b15e47c..86c9105e1a6 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -25,7 +25,7 @@ replication slave. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "slave.h" #include "sql_parse.h" // execute_init_command @@ -43,7 +43,6 @@ #include <ssl_compat.h> #include "unireg.h" #include <mysys_err.h> -#include "rpl_handler.h" #include <signal.h> #include <mysql.h> #include <myisam.h> @@ -60,6 +59,8 @@ #include "rpl_tblmap.h" #include "debug_sync.h" #include "rpl_parallel.h" +#include "sql_show.h" +#include "semisync_slave.h" #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -71,6 +72,9 @@ bool use_slave_mask = 0; MY_BITMAP slave_error_mask; char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE]; +uint *slave_transaction_retry_errors; +uint slave_transaction_retry_error_length= 0; +char slave_transaction_retry_error_names[SHOW_VAR_FUNC_BUFF_SIZE]; char* slave_load_tmpdir = 0; Master_info *active_mi= 0; @@ -155,7 +159,8 @@ static bool wait_for_relay_log_space(Relay_log_info* rli); static bool io_slave_killed(Master_info* mi); static bool sql_slave_killed(rpl_group_info *rgi); static int init_slave_thread(THD*, Master_info *, SLAVE_THD_TYPE); -static void print_slave_skip_errors(void); +static void make_slave_skip_errors_printable(void); +static void make_slave_transaction_retry_errors_printable(void); static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi); static int safe_reconnect(THD*, MYSQL*, Master_info*, bool); static int connect_to_master(THD*, MYSQL*, Master_info*, bool, bool); @@ -279,15 +284,180 @@ static void init_slave_psi_keys(void) #endif /* HAVE_PSI_INTERFACE */ +/* + Note: This definition needs to be kept in sync with the one in + mysql_system_tables.sql which is used by mysql_create_db. +*/ +static const char gtid_pos_table_definition1[]= + "CREATE TABLE "; +static const char gtid_pos_table_definition2[]= + " (domain_id INT UNSIGNED NOT NULL, " + "sub_id BIGINT UNSIGNED NOT NULL, " + "server_id INT UNSIGNED NOT NULL, " + "seq_no BIGINT UNSIGNED NOT NULL, " + "PRIMARY KEY (domain_id, sub_id)) CHARSET=latin1 " + "COMMENT='Replication slave GTID position' " + "ENGINE="; + +/* + Build a query string + CREATE TABLE mysql.gtid_slave_pos_<engine> ... ENGINE=<engine> +*/ +static bool +build_gtid_pos_create_query(THD *thd, String *query, + LEX_CSTRING *table_name, + LEX_CSTRING *engine_name) +{ + bool err= false; + err|= query->append(gtid_pos_table_definition1); + err|= append_identifier(thd, query, table_name); + err|= query->append(gtid_pos_table_definition2); + err|= append_identifier(thd, query, engine_name); + return err; +} + + +static int +gtid_pos_table_creation(THD *thd, plugin_ref engine, LEX_CSTRING *table_name) +{ + int err; + StringBuffer<sizeof(gtid_pos_table_definition1) + + sizeof(gtid_pos_table_definition1) + + 2*FN_REFLEN> query; + + if (build_gtid_pos_create_query(thd, &query, table_name, plugin_name(engine))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + + thd->set_db(&MYSQL_SCHEMA_NAME); + thd->clear_error(); + ulonglong thd_saved_option= thd->variables.option_bits; + /* This query shuold not be binlogged. */ + thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG; + thd->set_query_and_id(query.c_ptr(), query.length(), thd->charset(), + next_query_id()); + Parser_state parser_state; + err= parser_state.init(thd, thd->query(), thd->query_length()); + if (err) + goto end; + mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, + FALSE, FALSE); + if (thd->is_error()) + err= 1; +end: + thd->variables.option_bits= thd_saved_option; + thd->reset_query(); + return err; +} + + +static void +handle_gtid_pos_auto_create_request(THD *thd, void *hton) +{ + int err; + plugin_ref engine= NULL, *auto_engines; + rpl_slave_state::gtid_pos_table *entry; + StringBuffer<FN_REFLEN> loc_table_name; + LEX_CSTRING table_name; + + /* + Check that the plugin is still in @@gtid_pos_auto_engines, and lock + it. + */ + mysql_mutex_lock(&LOCK_global_system_variables); + engine= NULL; + for (auto_engines= opt_gtid_pos_auto_plugins; + auto_engines && *auto_engines; + ++auto_engines) + { + if (plugin_hton(*auto_engines) == hton) + { + engine= my_plugin_lock(NULL, *auto_engines); + break; + } + } + mysql_mutex_unlock(&LOCK_global_system_variables); + if (!engine) + { + /* The engine is gone from @@gtid_pos_auto_engines, so no action. */ + goto end; + } + + /* Find the entry for the table to auto-create. */ + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (entry) + { + if (entry->table_hton == hton && + entry->state == rpl_slave_state::GTID_POS_CREATE_REQUESTED) + break; + entry= entry->next; + } + if (entry) + { + entry->state = rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS; + err= loc_table_name.append(entry->table_name.str, entry->table_name.length); + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (!entry) + goto end; + if (err) + { + sql_print_error("Out of memory while trying to auto-create GTID position table"); + goto end; + } + table_name.str= loc_table_name.c_ptr_safe(); + table_name.length= loc_table_name.length(); + + err= gtid_pos_table_creation(thd, engine, &table_name); + if (err) + { + sql_print_error("Error auto-creating GTID position table `mysql.%s`: %s Error_code: %d", + table_name.str, thd->get_stmt_da()->message(), + thd->get_stmt_da()->sql_errno()); + thd->clear_error(); + goto end; + } + + /* Now enable the entry for the auto-created table. */ + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + entry= (rpl_slave_state::gtid_pos_table *) + rpl_global_gtid_slave_state->gtid_pos_tables; + while (entry) + { + if (entry->table_hton == hton && + entry->state == rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS) + { + entry->state= rpl_slave_state::GTID_POS_AVAILABLE; + break; + } + entry= entry->next; + } + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + +end: + if (engine) + plugin_unlock(NULL, engine); +} + + static bool slave_background_thread_running; static bool slave_background_thread_stop; static bool slave_background_thread_gtid_loaded; -struct slave_background_kill_t { +static struct slave_background_kill_t { slave_background_kill_t *next; THD *to_kill; } *slave_background_kill_list; +static struct slave_background_gtid_pos_create_t { + slave_background_gtid_pos_create_t *next; + void *hton; +} *slave_background_gtid_pos_create_list; + pthread_handler_t handle_slave_background(void *arg __attribute__((unused))) @@ -321,6 +491,7 @@ handle_slave_background(void *arg __attribute__((unused))) do { slave_background_kill_t *kill_list; + slave_background_gtid_pos_create_t *create_list; thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background, &stage_slave_background_wait_request, @@ -329,12 +500,14 @@ handle_slave_background(void *arg __attribute__((unused))) { stop= abort_loop || thd->killed || slave_background_thread_stop; kill_list= slave_background_kill_list; - if (stop || kill_list) + create_list= slave_background_gtid_pos_create_list; + if (stop || kill_list || create_list) break; mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); } slave_background_kill_list= NULL; + slave_background_gtid_pos_create_list= NULL; thd->EXIT_COND(&old_stage); while (kill_list) @@ -351,6 +524,16 @@ handle_slave_background(void *arg __attribute__((unused))) mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready); my_free(p); } + + while (create_list) + { + slave_background_gtid_pos_create_t *next= create_list->next; + void *hton= create_list->hton; + handle_gtid_pos_auto_create_request(thd, hton); + my_free(create_list); + create_list= next; + } + mysql_mutex_lock(&LOCK_slave_background); } while (!stop); @@ -390,6 +573,41 @@ slave_background_kill_request(THD *to_kill) /* + This function must only be called from a slave SQL thread (or worker thread), + to ensure that the table_entry will not go away before we can lock the + LOCK_slave_state. +*/ +void +slave_background_gtid_pos_create_request( + rpl_slave_state::gtid_pos_table *table_entry) +{ + slave_background_gtid_pos_create_t *p; + + if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) + return; + p= (slave_background_gtid_pos_create_t *)my_malloc(sizeof(*p), MYF(MY_WME)); + if (!p) + return; + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE) + { + my_free(p); + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + return; + } + table_entry->state= rpl_slave_state::GTID_POS_CREATE_REQUESTED; + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + + p->hton= table_entry->table_hton; + mysql_mutex_lock(&LOCK_slave_background); + p->next= slave_background_gtid_pos_create_list; + slave_background_gtid_pos_create_list= p; + mysql_cond_signal(&COND_slave_background); + mysql_mutex_unlock(&LOCK_slave_background); +} + + +/* Start the slave background thread. This thread is currently used for two purposes: @@ -419,7 +637,6 @@ start_slave_background_thread() sql_print_error("Failed to create thread while initialising slave"); return 1; } - mysql_mutex_lock(&LOCK_slave_background); while (!slave_background_thread_gtid_loaded) mysql_cond_wait(&COND_slave_background, &LOCK_slave_background); @@ -491,15 +708,6 @@ int init_slave() } /* - If --slave-skip-errors=... was not used, the string value for the - system variable has not been set up yet. Do it now. - */ - if (!use_slave_mask) - { - print_slave_skip_errors(); - } - - /* If master_host is not specified, try to read it from the master_info file. If master_host is specified, create the master_info file if it doesn't exists. @@ -596,12 +804,12 @@ int init_recovery(Master_info* mi, const char** errmsg) DBUG_RETURN(0); } - + /** Convert slave skip errors bitmap into a printable string. */ -static void print_slave_skip_errors(void) +static void make_slave_skip_errors_printable(void) { /* To be safe, we want 10 characters of room in the buffer for a number @@ -610,7 +818,7 @@ static void print_slave_skip_errors(void) plus a NUL terminator. That is a max 6 digit number. */ const size_t MIN_ROOM= 10; - DBUG_ENTER("print_slave_skip_errors"); + DBUG_ENTER("make_slave_skip_errors_printable"); DBUG_ASSERT(sizeof(slave_skip_error_names) > MIN_ROOM); DBUG_ASSERT(MAX_SLAVE_ERROR <= 999999); // 6 digits @@ -632,14 +840,14 @@ static void print_slave_skip_errors(void) else { char *buff= slave_skip_error_names; - char *bend= buff + sizeof(slave_skip_error_names); + char *bend= buff + sizeof(slave_skip_error_names) - MIN_ROOM; int errnum; for (errnum= 0; errnum < MAX_SLAVE_ERROR; errnum++) { if (bitmap_is_set(&slave_error_mask, errnum)) { - if (buff + MIN_ROOM >= bend) + if (buff >= bend) break; /* purecov: tested */ buff= int10_to_str(errnum, buff, 10); *buff++= ','; @@ -669,24 +877,24 @@ static void print_slave_skip_errors(void) Called from get_options() in mysqld.cc on start-up */ -void init_slave_skip_errors(const char* arg) +bool init_slave_skip_errors(const char* arg) { const char *p; DBUG_ENTER("init_slave_skip_errors"); + if (!arg || !*arg) // No errors defined + goto end; + if (my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0)) - { - fprintf(stderr, "Badly out of memory, please check your system status\n"); - exit(1); - } - use_slave_mask = 1; + DBUG_RETURN(1); + + use_slave_mask= 1; for (;my_isspace(system_charset_info,*arg);++arg) /* empty */; if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4)) { bitmap_set_all(&slave_error_mask); - print_slave_skip_errors(); - DBUG_VOID_RETURN; + goto end; } for (p= arg ; *p; ) { @@ -698,11 +906,109 @@ void init_slave_skip_errors(const char* arg) while (!my_isdigit(system_charset_info,*p) && *p) p++; } - /* Convert slave skip errors bitmap into a printable string. */ - print_slave_skip_errors(); + +end: + make_slave_skip_errors_printable(); + DBUG_RETURN(0); +} + +/** + Make printable version if slave_transaction_retry_errors + This is never empty as at least ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT + will be there +*/ + +static void make_slave_transaction_retry_errors_printable(void) +{ + /* + To be safe, we want 10 characters of room in the buffer for a number + plus terminators. Also, we need some space for constant strings. + 10 characters must be sufficient for a number plus {',' | '...'} + plus a NUL terminator. That is a max 6 digit number. + */ + const size_t MIN_ROOM= 10; + char *buff= slave_transaction_retry_error_names; + char *bend= buff + sizeof(slave_transaction_retry_error_names) - MIN_ROOM; + uint i; + DBUG_ENTER("make_slave_transaction_retry_errors_printable"); + DBUG_ASSERT(sizeof(slave_transaction_retry_error_names) > MIN_ROOM); + + /* Make @@slave_transaction_retry_errors show a human-readable value */ + opt_slave_transaction_retry_errors= slave_transaction_retry_error_names; + + for (i= 0; i < slave_transaction_retry_error_length && buff < bend; i++) + { + buff= int10_to_str(slave_transaction_retry_errors[i], buff, 10); + *buff++= ','; + } + if (buff != slave_transaction_retry_error_names) + buff--; // Remove last ',' + if (i < slave_transaction_retry_error_length) + { + /* Couldn't show all errors */ + buff= strmov(buff, "..."); /* purecov: tested */ + } + *buff=0; + DBUG_PRINT("exit", ("error_names: '%s'", + slave_transaction_retry_error_names)); DBUG_VOID_RETURN; } + +bool init_slave_transaction_retry_errors(const char* arg) +{ + const char *p; + long err_code; + uint i; + DBUG_ENTER("init_slave_transaction_retry_errors"); + + /* Handle empty strings */ + if (!arg) + arg= ""; + + slave_transaction_retry_error_length= 2; + for (;my_isspace(system_charset_info,*arg);++arg) + /* empty */; + for (p= arg; *p; ) + { + if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code))) + break; + slave_transaction_retry_error_length++; + while (!my_isdigit(system_charset_info,*p) && *p) + p++; + } + + if (!(slave_transaction_retry_errors= + (uint *) my_once_alloc(sizeof(int) * + slave_transaction_retry_error_length, + MYF(MY_WME)))) + DBUG_RETURN(1); + + /* + Temporary error codes: + currently, InnoDB deadlock detected by InnoDB or lock + wait timeout (innodb_lock_wait_timeout exceeded + */ + slave_transaction_retry_errors[0]= ER_LOCK_DEADLOCK; + slave_transaction_retry_errors[1]= ER_LOCK_WAIT_TIMEOUT; + + /* Add user codes after this */ + for (p= arg, i= 2; *p; ) + { + if (!(p= str2int(p, 10, 0, LONG_MAX, &err_code))) + break; + if (err_code > 0 && err_code < ER_ERROR_LAST) + slave_transaction_retry_errors[i++]= (uint) err_code; + while (!my_isdigit(system_charset_info,*p) && *p) + p++; + } + slave_transaction_retry_error_length= i; + + make_slave_transaction_retry_errors_printable(); + DBUG_RETURN(0); +} + + int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) { DBUG_ENTER("terminate_slave_threads"); @@ -771,7 +1077,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) mysql_mutex_unlock(log_lock); } - DBUG_RETURN(retval); + DBUG_RETURN(retval); } @@ -1284,7 +1590,7 @@ const char *print_slave_db_safe(const char* db) int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val) { - uint length; + size_t length; DBUG_ENTER("init_strvar_from_file"); if ((length=my_b_gets(f,var, max_size))) @@ -1894,7 +2200,7 @@ when it try to get the value of TIME_ZONE global variable from master."; if (++dbug_count < 3) goto heartbeat_network_error; }); - if (mysql_real_query(mysql, query, strlen(query))) + if (mysql_real_query(mysql, query, (ulong)strlen(query))) { if (check_io_slave_killed(mi, NULL)) goto slave_killed_err; @@ -1942,7 +2248,7 @@ when it try to get the value of TIME_ZONE global variable from master."; Once the first FD will be received its alg descriptor will replace the being queried one. */ - rc= mysql_real_query(mysql, query, strlen(query)); + rc= mysql_real_query(mysql, query,(ulong)strlen(query)); if (rc != 0) { if (check_io_slave_killed(mi, NULL)) @@ -2520,7 +2826,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi, bool *suppress_warnings) { uchar buf[1024], *pos= buf; - uint report_host_len=0, report_user_len=0, report_password_len=0; + size_t report_host_len=0, report_user_len=0, report_password_len=0; DBUG_ENTER("register_slave_on_master"); *suppress_warnings= FALSE; @@ -2528,7 +2834,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi, report_host_len= strlen(report_host); if (report_host_len > HOSTNAME_LENGTH) { - sql_print_warning("The length of report_host is %d. " + sql_print_warning("The length of report_host is %zu. " "It is larger than the max length(%d), so this " "slave cannot be registered to the master.", report_host_len, HOSTNAME_LENGTH); @@ -2539,7 +2845,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi, report_user_len= strlen(report_user); if (report_user_len > USERNAME_LENGTH) { - sql_print_warning("The length of report_user is %d. " + sql_print_warning("The length of report_user is %zu. " "It is larger than the max length(%d), so this " "slave cannot be registered to the master.", report_user_len, USERNAME_LENGTH); @@ -2550,7 +2856,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi, report_password_len= strlen(report_password); if (report_password_len > MAX_PASSWORD_LENGTH) { - sql_print_warning("The length of report_password is %d. " + sql_print_warning("The length of report_password is %zu. " "It is larger than the max length(%d), so this " "slave cannot be registered to the master.", report_password_len, MAX_PASSWORD_LENGTH); @@ -2571,7 +2877,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi, /* The master will fill in master_id */ int4store(pos, 0); pos+= 4; - if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (size_t) (pos- buf), 0)) + if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (ulong) (pos- buf), 0)) { if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) { @@ -2838,7 +3144,7 @@ void show_master_info_get_fields(THD *thd, List<Item> *field_list, mem_root); field_list->push_back(new (mem_root) Item_empty_string(thd, "Gtid_Slave_Pos", - gtid_pos_length), + (uint)gtid_pos_length), mem_root); } DBUG_VOID_RETURN; @@ -3162,6 +3468,13 @@ void set_slave_thread_options(THD* thd) options&= ~OPTION_BIN_LOG; thd->variables.option_bits= options; thd->variables.completion_type= 0; + + /* For easier test in LOGGER::log_command */ + if (thd->variables.log_disabled_statements & LOG_DISABLE_SLAVE) + thd->variables.option_bits|= OPTION_LOG_OFF; + + thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_SLAVE); DBUG_VOID_RETURN; } @@ -3208,8 +3521,7 @@ static int init_slave_thread(THD* thd, Master_info *mi, thd->security_ctx->skip_grants(); thd->slave_thread= 1; thd->connection_name= mi->connection_name; - thd->variables.sql_log_slow= opt_log_slow_slave_statements; - thd->variables.log_slow_filter= global_system_variables.log_slow_filter; + thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE); set_slave_thread_options(thd); if (thd_type == SLAVE_THD_SQL) @@ -3274,11 +3586,9 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi, if (opt_log_slave_updates && opt_replicate_annotate_row_events) binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT; - if (RUN_HOOK(binlog_relay_io, - before_request_transmit, - (thd, mi, binlog_flags))) + if (repl_semisync_slave.request_transmit(mi)) DBUG_RETURN(1); - + // TODO if big log files: Change next to int8store() int4store(buf, (ulong) mi->master_log_pos); int2store(buf + 4, binlog_flags); @@ -3377,14 +3687,20 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings, DBUG_RETURN(len - 1); } -/* + +/** Check if the current error is of temporary nature of not. Some errors are temporary in nature, such as ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT. + + @retval 0 if fatal error + @retval 1 temporary error, do retry */ + int has_temporary_error(THD *thd) { + uint current_errno; DBUG_ENTER("has_temporary_error"); DBUG_EXECUTE_IF("all_errors_are_temporary_errors", @@ -3402,14 +3718,12 @@ has_temporary_error(THD *thd) if (!thd->is_error()) DBUG_RETURN(0); - /* - Temporary error codes: - currently, InnoDB deadlock detected by InnoDB or lock - wait timeout (innodb_lock_wait_timeout exceeded - */ - if (thd->get_stmt_da()->sql_errno() == ER_LOCK_DEADLOCK || - thd->get_stmt_da()->sql_errno() == ER_LOCK_WAIT_TIMEOUT) - DBUG_RETURN(1); + current_errno= thd->get_stmt_da()->sql_errno(); + for (uint i= 0; i < slave_transaction_retry_error_length; i++) + { + if (current_errno == slave_transaction_retry_errors[i]) + DBUG_RETURN(1); + } DBUG_RETURN(0); } @@ -4060,8 +4374,9 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, exec_res= 0; serial_rgi->cleanup_context(thd, 1); /* chance for concurrent connection to get more locks */ - slave_sleep(thd, MY_MIN(serial_rgi->trans_retries, + slave_sleep(thd, MY_MAX(MY_MIN(serial_rgi->trans_retries, MAX_SLAVE_RETRY_PAUSE), + slave_trans_retry_interval), sql_slave_killed, serial_rgi); serial_rgi->trans_retries++; mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS @@ -4146,7 +4461,7 @@ static bool check_io_slave_killed(Master_info *mi, const char *info) @param[in] mysql MySQL connection. @param[in] mi Master connection information. @param[in,out] retry_count Number of attempts to reconnect. - @param[in] suppress_warnings TRUE when a normal net read timeout + @param[in] suppress_warnings TRUE when a normal net read timeout has caused to reconnecting. @param[in] messages Messages to print/log, see reconnect_messages[] array. @@ -4300,7 +4615,8 @@ pthread_handler_t handle_slave_io(void *arg) } - if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi))) + if (DBUG_EVALUATE_IF("failed_slave_start", 1, 0) + || repl_semisync_slave.slave_start(mi)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4494,9 +4810,10 @@ Stopping slave I/O thread due to out-of-memory error from master"); retry_count=0; // ok event, reset retry counter THD_STAGE_INFO(thd, stage_queueing_master_event_to_the_relay_log); event_buf= (const char*)mysql->net.read_pos + 1; - if (RUN_HOOK(binlog_relay_io, after_read_event, - (thd, mi,(const char*)mysql->net.read_pos + 1, - event_len, &event_buf, &event_len))) + mi->semi_ack= 0; + if (repl_semisync_slave. + slave_read_sync_header((const char*)mysql->net.read_pos + 1, event_len, + &(mi->semi_ack), &event_buf, &event_len)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4545,9 +4862,6 @@ Stopping slave I/O thread due to out-of-memory error from master"); tokenamount -= network_read_len; } - /* XXX: 'synced' should be updated by queue_event to indicate - whether event has been synced to disk */ - bool synced= 0; if (queue_event(mi, event_buf, event_len)) { mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL, @@ -4556,8 +4870,8 @@ Stopping slave I/O thread due to out-of-memory error from master"); goto err; } - if (RUN_HOOK(binlog_relay_io, after_queue_event, - (thd, mi, event_buf, event_len, synced))) + if (rpl_semi_sync_slave_status && (mi->semi_ack & SEMI_SYNC_NEED_ACK) && + repl_semisync_slave.slave_reply(mi)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4566,7 +4880,16 @@ Stopping slave I/O thread due to out-of-memory error from master"); } if (mi->using_gtid == Master_info::USE_GTID_NO && - flush_master_info(mi, TRUE, TRUE)) + /* + If rpl_semi_sync_slave_delay_master is enabled, we will flush + master info only when ack is needed. This may lead to at least one + group transaction delay but affords better performance improvement. + */ + (!repl_semisync_slave.get_slave_enabled() || + (!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) || + (mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) && + (DBUG_EVALUATE_IF("failed_flush_master_info", 1, 0) || + flush_master_info(mi, TRUE, TRUE))) { sql_print_error("Failed to flush master info file"); goto err; @@ -4620,9 +4943,9 @@ err: IO_RPL_LOG_NAME, mi->master_log_pos, tmp.c_ptr_safe()); } - RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi)); + repl_semisync_slave.slave_stop(mi); thd->reset_query(); - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); if (mysql) { /* @@ -4654,9 +4977,7 @@ err_during_init: // TODO: make rpl_status part of Master_info change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); + thd->assert_not_linked(); delete thd; thread_safe_decrement32(&service_thread_count); signal_thd_deleted(); @@ -5069,6 +5390,14 @@ pthread_handler_t handle_slave_sql(void *arg) if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode) goto err; } + /* Re-load the set of mysql.gtid_slave_posXXX tables available. */ + if (find_gtid_slave_pos_tables(thd)) + { + rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL, + "Error processing replication GTID position tables: %s", + thd->get_stmt_da()->message()); + goto err; + } /* execute init_slave variable */ if (opt_init_slave.length) @@ -5202,7 +5531,7 @@ pthread_handler_t handle_slave_sql(void *arg) */ thd->catalog= 0; thd->reset_query(); - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); if (rli->mi->using_gtid != Master_info::USE_GTID_NO) { ulong domain_count; @@ -5327,11 +5656,7 @@ err_during_init: rpl_parallel_resize_pool_if_no_slaves(); - /* TODO: Check if this lock is needed */ - mysql_mutex_lock(&LOCK_thread_count); delete serial_rgi; - mysql_mutex_unlock(&LOCK_thread_count); - delete thd; thread_safe_decrement32(&service_thread_count); signal_thd_deleted(); @@ -5940,7 +6265,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) mysql_mutex_unlock(log_lock); goto err; } - rli->relay_log.signal_update(); + rli->relay_log.signal_relay_log_update(); mysql_mutex_unlock(log_lock); mi->gtid_reconnect_event_skip_count= 0; @@ -6558,7 +6883,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) if (got_gtid_event) rli->ign_gtids.update(&event_gtid); } - rli->relay_log.signal_update(); // the slave SQL thread needs to re-check + // the slave SQL thread needs to re-check + rli->relay_log.signal_relay_log_update(); DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored", (ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET))); } @@ -7083,7 +7409,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) MYSQL_BIN_LOG::open() will write the buffered description event. */ old_pos= rli->event_relay_log_pos; - if ((ev= Log_event::read_log_event(cur_log,0, + if ((ev= Log_event::read_log_event(cur_log, rli->relay_log.description_event_for_exec, opt_slave_sql_verify_checksum))) diff --git a/sql/slave.h b/sql/slave.h index aec5f0a1d02..649d55b45b9 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -48,6 +48,7 @@ #include "my_list.h" #include "rpl_filter.h" #include "rpl_tblmap.h" +#include "rpl_gtid.h" #define SLAVE_NET_TIMEOUT 60 @@ -131,6 +132,9 @@ extern ulong master_retry_count; extern MY_BITMAP slave_error_mask; extern char slave_skip_error_names[]; extern bool use_slave_mask; +extern char slave_transaction_retry_error_names[]; +extern uint *slave_transaction_retry_errors; +extern uint slave_transaction_retry_error_length; extern char *slave_load_tmpdir; extern char *master_info_file; extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file; @@ -138,6 +142,7 @@ extern char *opt_relay_logname, *opt_relaylog_index_name; extern my_bool opt_skip_slave_start, opt_reckless_slave; extern my_bool opt_log_slave_updates; extern char *opt_slave_skip_errors; +extern char *opt_slave_transaction_retry_errors; extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; extern ulonglong opt_read_binlog_speed_limit; @@ -183,7 +188,8 @@ extern const char *relay_log_basename; int init_slave(); int init_recovery(Master_info* mi, const char** errmsg); -void init_slave_skip_errors(const char* arg); +bool init_slave_skip_errors(const char* arg); +bool init_slave_transaction_retry_errors(const char* arg); int register_slave_on_master(MYSQL* mysql); int terminate_slave_threads(Master_info* mi, int thread_mask, bool skip_lock = 0); @@ -268,6 +274,8 @@ void slave_output_error_info(rpl_group_info *rgi, THD *thd); pthread_handler_t handle_slave_sql(void *arg); bool net_request_file(NET* net, const char* fname); void slave_background_kill_request(THD *to_kill); +void slave_background_gtid_pos_create_request + (rpl_slave_state::gtid_pos_table *table_entry); extern bool volatile abort_loop; extern Master_info *active_mi; /* active_mi for multi-master */ diff --git a/sql/sp.cc b/sql/sp.cc index daf0744c904..773a5479199 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sp.h" @@ -35,7 +35,6 @@ #include <my_user.h> - sp_cache **Sp_handler_procedure::get_cache(THD *thd) const { return &thd->sp_proc_cache; @@ -93,79 +92,79 @@ static const TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = { { - { C_STRING_WITH_LEN("db") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("name") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("name") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("type") }, - { C_STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") }, + { STRING_WITH_LEN("type") }, + { STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("specific_name") }, - { C_STRING_WITH_LEN("char(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("specific_name") }, + { STRING_WITH_LEN("char(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("language") }, - { C_STRING_WITH_LEN("enum('SQL')") }, + { STRING_WITH_LEN("language") }, + { STRING_WITH_LEN("enum('SQL')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("sql_data_access") }, - { C_STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") }, + { STRING_WITH_LEN("sql_data_access") }, + { STRING_WITH_LEN("enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("is_deterministic") }, - { C_STRING_WITH_LEN("enum('YES','NO')") }, + { STRING_WITH_LEN("is_deterministic") }, + { STRING_WITH_LEN("enum('YES','NO')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("security_type") }, - { C_STRING_WITH_LEN("enum('INVOKER','DEFINER')") }, + { STRING_WITH_LEN("security_type") }, + { STRING_WITH_LEN("enum('INVOKER','DEFINER')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("param_list") }, - { C_STRING_WITH_LEN("blob") }, + { STRING_WITH_LEN("param_list") }, + { STRING_WITH_LEN("blob") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("returns") }, - { C_STRING_WITH_LEN("longblob") }, + { STRING_WITH_LEN("returns") }, + { STRING_WITH_LEN("longblob") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("body") }, - { C_STRING_WITH_LEN("longblob") }, + { STRING_WITH_LEN("body") }, + { STRING_WITH_LEN("longblob") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("definer") }, - { C_STRING_WITH_LEN("char(") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("definer") }, + { STRING_WITH_LEN("char(") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("created") }, - { C_STRING_WITH_LEN("timestamp") }, + { STRING_WITH_LEN("created") }, + { STRING_WITH_LEN("timestamp") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("modified") }, - { C_STRING_WITH_LEN("timestamp") }, + { STRING_WITH_LEN("modified") }, + { STRING_WITH_LEN("timestamp") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("sql_mode") }, - { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," + { STRING_WITH_LEN("sql_mode") }, + { STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY'," "'NO_UNSIGNED_SUBTRACTION'," "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," @@ -174,32 +173,37 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH'," - "'EMPTY_STRING_IS_NULL')") }, + "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("comment") }, - { C_STRING_WITH_LEN("text") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("comment") }, + { STRING_WITH_LEN("text") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("character_set_client") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("character_set_client") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("collation_connection") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("collation_connection") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("db_collation") }, - { C_STRING_WITH_LEN("char(32)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db_collation") }, + { STRING_WITH_LEN("char(32)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("body_utf8") }, - { C_STRING_WITH_LEN("longblob") }, + { STRING_WITH_LEN("body_utf8") }, + { STRING_WITH_LEN("longblob") }, + { NULL, 0 } + }, + { + { STRING_WITH_LEN("aggregate") }, + { STRING_WITH_LEN("enum('NONE','GROUP')") }, { NULL, 0 } } }; @@ -450,7 +454,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup) DBUG_ENTER("open_proc_table_for_read"); - table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ); + table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_READ); if (open_system_tables_for_read(thd, &table, backup)) DBUG_RETURN(NULL); @@ -485,7 +489,7 @@ static TABLE *open_proc_table_for_update(THD *thd) MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_proc_table_for_update"); - table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE); + table_list.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_WRITE); if (!(table= open_system_table_for_update(thd, &table_list))) DBUG_RETURN(NULL); @@ -583,6 +587,22 @@ bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table) return true; suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID; + if (table->field[MYSQL_PROC_FIELD_AGGREGATE]->val_str_nopad(thd->mem_root, + &str)) + return true; + + switch (str.str[0]) { + case 'N': + agg_type= NOT_AGGREGATE; + break; + case 'G': + agg_type= GROUP_AGGREGATE; + break; + default: + agg_type= DEFAULT_AGGREGATE; + } + + if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root, &comment)) return true; @@ -1182,6 +1202,13 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name, system_charset_info); + if (sp->agg_type() != DEFAULT_AGGREGATE) + { + store_failed= store_failed || + table->field[MYSQL_PROC_FIELD_AGGREGATE]-> + store((longlong)sp->agg_type(),TRUE); + } + store_failed= store_failed || table->field[MYSQL_PROC_MYSQL_TYPE]-> store((longlong) type(), true); @@ -1493,6 +1520,9 @@ Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name, if (chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment, system_charset_info); + if (chistics->agg_type != DEFAULT_AGGREGATE) + table->field[MYSQL_PROC_FIELD_AGGREGATE]-> + store((longlong)chistics->agg_type, TRUE); if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) && ret != HA_ERR_RECORD_IS_THE_SAME) ret= SP_WRITE_ROW_FAILED; @@ -1558,7 +1588,7 @@ bool lock_db_routines(THD *thd, const char *db) uchar keybuf[MAX_KEY_LENGTH]; DBUG_ENTER("lock_db_routines"); - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); /* mysql.proc will be re-opened during deletion, so we can ignore @@ -1887,16 +1917,16 @@ Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const sp_name *name; LEX_CSTRING lex_db; LEX_CSTRING lex_name; - thd->make_lex_string(&lex_db, routine->db, strlen(routine->db)); - thd->make_lex_string(&lex_name, routine->table_name, - strlen(routine->table_name)); + thd->make_lex_string(&lex_db, routine->db.str, routine->db.length); + thd->make_lex_string(&lex_name, routine->table_name.str, + routine->table_name.length); name= new sp_name(&lex_db, &lex_name, true); sp_object_found= sp_find_routine(thd, name, false) != NULL; thd->get_stmt_da()->clear_warning_info(thd->query_id); if (! sp_object_found) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE", - routine->table_name); + routine->table_name.str); DBUG_RETURN(TRUE); } } @@ -2237,11 +2267,14 @@ Sp_handler::show_create_sp(THD *thd, String *buf, sql_mode_t sql_mode) const { sql_mode_t old_sql_mode= thd->variables.sql_mode; + size_t agglen= (chistics.agg_type == GROUP_AGGREGATE)? 10 : 0; + LEX_CSTRING tmp; + /* Make some room to begin with */ if (buf->alloc(100 + db.length + 1 + name.length + params.length + returns.length + - chistics.comment.length + 10 /* length of " DEFINER= "*/ + - USER_HOST_BUFF_SIZE)) + chistics.comment.length + 10 /* length of " DEFINER= "*/ + + agglen + USER_HOST_BUFF_SIZE)) return true; thd->variables.sql_mode= sql_mode; @@ -2249,19 +2282,22 @@ Sp_handler::show_create_sp(THD *thd, String *buf, if (ddl_options.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); append_definer(thd, buf, &definer.user, &definer.host); - buf->append(type_lex_cstring()); + if (chistics.agg_type == GROUP_AGGREGATE) + buf->append(STRING_WITH_LEN("AGGREGATE ")); + tmp= type_lex_cstring(); + buf->append(&tmp); buf->append(STRING_WITH_LEN(" ")); if (ddl_options.if_not_exists()) buf->append(STRING_WITH_LEN("IF NOT EXISTS ")); if (db.length > 0) { - append_identifier(thd, buf, db.str, db.length); + append_identifier(thd, buf, &db); buf->append('.'); } - append_identifier(thd, buf, name.str, name.length); + append_identifier(thd, buf, &name); buf->append('('); - buf->append(params); + buf->append(¶ms); buf->append(')'); if (type() == TYPE_ENUM_FUNCTION) { @@ -2269,7 +2305,7 @@ Sp_handler::show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN(" RETURN ")); else buf->append(STRING_WITH_LEN(" RETURNS ")); - buf->append(returns); + buf->append(&returns); } buf->append('\n'); switch (chistics.daccess) { @@ -2291,7 +2327,7 @@ Sp_handler::show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN(" DETERMINISTIC\n")); append_suid(buf, chistics.suid); append_comment(buf, chistics.comment); - buf->append(body); + buf->append(body.str, body.length); // Not \0 terminated thd->variables.sql_mode= old_sql_mode; return false; } @@ -100,7 +100,7 @@ public: virtual LEX_CSTRING type_lex_cstring() const= 0; virtual LEX_CSTRING empty_body_lex_cstring() const { - static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("???")}; + static LEX_CSTRING m_empty_body= {STRING_WITH_LEN("???")}; DBUG_ASSERT(0); return m_empty_body; } @@ -182,12 +182,12 @@ public: stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; } LEX_CSTRING type_lex_cstring() const { - static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PROCEDURE")}; + static LEX_CSTRING m_type_str= { STRING_WITH_LEN("PROCEDURE")}; return m_type_str; } LEX_CSTRING empty_body_lex_cstring() const { - static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")}; + static LEX_CSTRING m_empty_body= { STRING_WITH_LEN("BEGIN END")}; return m_empty_body; } const char *show_create_routine_col1_caption() const @@ -218,12 +218,12 @@ public: stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; } LEX_CSTRING type_lex_cstring() const { - static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("FUNCTION")}; + static LEX_CSTRING m_type_str= { STRING_WITH_LEN("FUNCTION")}; return m_type_str; } LEX_CSTRING empty_body_lex_cstring() const { - static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("RETURN NULL")}; + static LEX_CSTRING m_empty_body= { STRING_WITH_LEN("RETURN NULL")}; return m_empty_body; } const char *show_create_routine_col1_caption() const @@ -253,7 +253,7 @@ public: stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; } LEX_CSTRING type_lex_cstring() const { - static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("TRIGGER")}; + static LEX_CSTRING m_type_str= { STRING_WITH_LEN("TRIGGER")}; return m_type_str; } MDL_key::enum_mdl_namespace get_mdl_type() const @@ -370,6 +370,7 @@ enum MYSQL_PROC_FIELD_COLLATION_CONNECTION, MYSQL_PROC_FIELD_DB_COLLATION, MYSQL_PROC_FIELD_BODY_UTF8, + MYSQL_PROC_FIELD_AGGREGATE, MYSQL_PROC_FIELD_COUNT }; diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 70ba5084914..4f7cf1557e4 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION @@ -23,7 +23,7 @@ #include "sp_head.h" static mysql_mutex_t Cversion_lock; -static ulong volatile Cversion= 0; +static ulong volatile Cversion= 1; /* @@ -48,7 +48,7 @@ public: return my_hash_insert(&m_hashtable, (const uchar *)sp); } - inline sp_head *lookup(char *name, uint namelen) + inline sp_head *lookup(char *name, size_t namelen) { return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name, namelen); @@ -238,6 +238,10 @@ void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp) } } +void sp_cache_flush(sp_cache *cp, sp_head *sp) +{ + cp->remove(sp); +} /** Return the current global version of the cache. diff --git a/sql/sp_cache.h b/sql/sp_cache.h index 51886a61ee9..59e0fc186dd 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -21,8 +21,6 @@ #pragma interface /* gcc class implementation */ #endif -#include "my_global.h" /* ulong */ - /* Stored procedures/functions cache. This is used as follows: * Each thread has its own cache. @@ -62,6 +60,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp); sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name); void sp_cache_invalidate(); void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp); +void sp_cache_flush(sp_cache *cp, sp_head *sp); ulong sp_cache_version(); void sp_cache_enforce_limit(sp_cache *cp, ulong upper_limit_for_elements); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index bb56c69aa41..cdb5e256e46 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_prepare.h" @@ -604,7 +604,6 @@ sp_head::set_stmt_end(THD *thd) { Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */ const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */ - uint not_used; /* Make the string of parameters. */ @@ -622,7 +621,7 @@ sp_head::set_stmt_end(THD *thd) m_body.length= end_ptr - m_body_begin; m_body.str= thd->strmake(m_body_begin, m_body.length); - trim_whitespace(thd->charset(), &m_body, ¬_used); + trim_whitespace(thd->charset(), &m_body); /* Make the string of UTF-body. */ @@ -630,7 +629,7 @@ sp_head::set_stmt_end(THD *thd) m_body_utf8.length= lip->get_body_utf8_length(); m_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), m_body_utf8.length); - trim_whitespace(thd->charset(), &m_body_utf8, ¬_used); + trim_whitespace(thd->charset(), &m_body_utf8); /* Make the string of whole stored-program-definition query (in the @@ -639,7 +638,7 @@ sp_head::set_stmt_end(THD *thd) m_defstr.length= end_ptr - lip->get_cpp_buf(); m_defstr.str= thd->strmake(lip->get_cpp_buf(), m_defstr.length); - trim_whitespace(thd->charset(), &m_defstr, ¬_used); + trim_whitespace(thd->charset(), &m_defstr); } @@ -875,7 +874,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) rewritables.sort(cmp_rqp_locations); - thd->query_name_consts= rewritables.elements(); + thd->query_name_consts= (uint)rewritables.elements(); for (Rewritable_query_parameter **rqp= rewritables.front(); rqp <= rewritables.back(); rqp++) @@ -898,14 +897,14 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) <db_name> Name of current database <flags> Flags struct */ - int buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE + - thd->db_length + QUERY_CACHE_FLAGS_SIZE + 1); + size_t buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE + + thd->db.length + QUERY_CACHE_FLAGS_SIZE + 1); if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len))) { char *ptr= pbuf + qbuf.length(); memcpy(pbuf, qbuf.ptr(), qbuf.length()); *ptr= 0; - int2store(ptr+1, thd->db_length); + int2store(ptr+1, thd->db.length); } else DBUG_RETURN(TRUE); @@ -957,6 +956,12 @@ sp_head::execute(THD *thd, bool merge_da_on_success) bool err_status= FALSE; uint ip= 0; sql_mode_t save_sql_mode; + + // TODO(cvicentiu) See if you can drop this bit. This is used to resume + // execution from where we left off. + if (m_chistics.agg_type == GROUP_AGGREGATE) + ip= thd->spcont->instr_ptr; + bool save_abort_on_warning; Query_arena *old_arena; /* per-instruction arena */ @@ -1124,6 +1129,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) #if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); #endif + thd->spcont->quit_func= TRUE; break; } @@ -1195,7 +1201,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success) /* Reset sp_rcontext::end_partial_result_set flag. */ ctx->end_partial_result_set= FALSE; - } while (!err_status && !thd->killed && !thd->is_fatal_error); + } while (!err_status && !thd->killed && !thd->is_fatal_error && + !thd->spcont->pause_state); #if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); @@ -1211,9 +1218,14 @@ sp_head::execute(THD *thd, bool merge_da_on_success) thd->restore_active_arena(&execute_arena, &backup_arena); - thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error + /* Only pop cursors when we're done with group aggregate running. */ + if (m_chistics.agg_type != GROUP_AGGREGATE || + (m_chistics.agg_type == GROUP_AGGREGATE && thd->spcont->quit_func)) + thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error /* Restore all saved */ + if (m_chistics.agg_type == GROUP_AGGREGATE) + thd->spcont->instr_ptr= ip; thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status; old_packet.swap(thd->packet); DBUG_ASSERT(thd->Item_change_list::is_empty()); @@ -1374,7 +1386,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx) */ if (*save_ctx && check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, sp->m_handler, false)) + &sp->m_db, &sp->m_name, sp->m_handler, false)) { sp->m_security_ctx.restore_security_context(thd, *save_ctx); *save_ctx= 0; @@ -1389,7 +1401,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx) bool sp_head::check_execute_access(THD *thd) const { return check_routine_access(thd, EXECUTE_ACL, - m_db.str, m_name.str, + &m_db, &m_name, m_handler, false); } @@ -1616,18 +1628,16 @@ err_with_cleanup: bool sp_head::execute_function(THD *thd, Item **argp, uint argcount, - Field *return_value_fld) + Field *return_value_fld, sp_rcontext **func_ctx, + Query_arena *call_arena) { ulonglong UNINIT_VAR(binlog_save_options); bool need_binlog_call= FALSE; uint arg_no; sp_rcontext *octx = thd->spcont; - sp_rcontext *nctx = NULL; char buf[STRING_BUFFER_USUAL_SIZE]; String binlog_buf(buf, sizeof(buf), &my_charset_bin); bool err_status= FALSE; - MEM_ROOT call_mem_root; - Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP); Query_arena backup_arena; DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); @@ -1660,24 +1670,25 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, TODO: we should create sp_rcontext once per command and reuse it on subsequent executions of a function/trigger. */ - init_sql_alloc(&call_mem_root, "execute_function", MEM_ROOT_BLOCK_SIZE, 0, - MYF(0)); - thd->set_n_backup_active_arena(&call_arena, &backup_arena); - - if (!(nctx= rcontext_create(thd, return_value_fld, argp, argcount))) + if (!(*func_ctx)) { - thd->restore_active_arena(&call_arena, &backup_arena); - err_status= TRUE; - goto err_with_cleanup; - } + thd->set_n_backup_active_arena(call_arena, &backup_arena); - /* - We have to switch temporarily back to callers arena/memroot. - Function arguments belong to the caller and so the may reference - memory which they will allocate during calculation long after - this function call will be finished (e.g. in Item::cleanup()). - */ - thd->restore_active_arena(&call_arena, &backup_arena); + if (!(*func_ctx= rcontext_create(thd, return_value_fld, argp, argcount))) + { + thd->restore_active_arena(call_arena, &backup_arena); + err_status= TRUE; + goto err_with_cleanup; + } + + /* + We have to switch temporarily back to callers arena/memroot. + Function arguments belong to the caller and so the may reference + memory which they will allocate during calculation long after + this function call will be finished (e.g. in Item::cleanup()). + */ + thd->restore_active_arena(call_arena, &backup_arena); + } /* Pass arguments. */ for (arg_no= 0; arg_no < argcount; arg_no++) @@ -1685,7 +1696,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, /* Arguments must be fixed in Item_func_sp::fix_fields */ DBUG_ASSERT(argp[arg_no]->fixed); - if ((err_status= nctx->set_parameter(thd, arg_no, &(argp[arg_no])))) + if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no])))) goto err_with_cleanup; } @@ -1705,9 +1716,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, { binlog_buf.length(0); binlog_buf.append(STRING_WITH_LEN("SELECT ")); - append_identifier(thd, &binlog_buf, m_db.str, m_db.length); + append_identifier(thd, &binlog_buf, &m_db); binlog_buf.append('.'); - append_identifier(thd, &binlog_buf, m_name.str, m_name.length); + append_identifier(thd, &binlog_buf, &m_name); binlog_buf.append('('); for (arg_no= 0; arg_no < argcount; arg_no++) { @@ -1717,7 +1728,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (arg_no) binlog_buf.append(','); - Item_field *item= nctx->get_parameter(arg_no); + Item_field *item= (*func_ctx)->get_parameter(arg_no); str_value= item->type_handler()->print_item_value(thd, item, &str_value_holder); if (str_value) @@ -1727,7 +1738,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } binlog_buf.append(')'); } - thd->spcont= nctx; + thd->spcont= *func_ctx; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; @@ -1768,11 +1779,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, sp_rcontext and allocate all these objects (and sp_rcontext itself) on it directly rather than juggle with arenas. */ - thd->set_n_backup_active_arena(&call_arena, &backup_arena); + thd->set_n_backup_active_arena(call_arena, &backup_arena); err_status= execute(thd, TRUE); - thd->restore_active_arena(&call_arena, &backup_arena); + thd->restore_active_arena(call_arena, &backup_arena); if (need_binlog_call) { @@ -1798,11 +1809,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } - if (!err_status) + if (!err_status && thd->spcont->quit_func) { /* We need result only in function but not in trigger */ - if (!nctx->is_return_value_set()) + if (!(*func_ctx)->is_return_value_set()) { my_error(ER_SP_NORETURNEND, MYF(0), m_name.str); err_status= TRUE; @@ -1814,9 +1825,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, #endif err_with_cleanup: - delete nctx; - call_arena.free_items(); - free_root(&call_mem_root, MYF(0)); thd->spcont= octx; /* @@ -1988,13 +1996,32 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length, m_name.str)); } + save_enable_slow_log= thd->enable_slow_log; - if (!(m_flags & LOG_SLOW_STATEMENTS) && save_enable_slow_log) + + /* + Disable slow log if: + - Slow logging is enabled (no change needed) + - This is a normal SP (not event log) + - If we have not explicitely disabled logging of SP + */ + if (save_enable_slow_log && + ((!(m_flags & LOG_SLOW_STATEMENTS) && + (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SP)))) { DBUG_PRINT("info", ("Disabling slow log for the execution")); thd->enable_slow_log= FALSE; } - if (!(m_flags & LOG_GENERAL_LOG) && !(thd->variables.option_bits & OPTION_LOG_OFF)) + + /* + Disable general log if: + - If general log is enabled (no change needed) + - This is a normal SP (not event log) + - If we have not explicitely disabled logging of SP + */ + if (!(thd->variables.option_bits & OPTION_LOG_OFF) && + (!(m_flags & LOG_GENERAL_LOG) && + (thd->variables.log_disabled_statements & LOG_DISABLE_SP))) { DBUG_PRINT("info", ("Disabling general log for the execution")); save_log_general= true; @@ -2017,6 +2044,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (save_log_general) thd->variables.option_bits &= ~OPTION_LOG_OFF; thd->enable_slow_log= save_enable_slow_log; + /* In the case when we weren't able to employ reuse mechanism for OUT/INOUT paranmeters, we should reallocate memory. This @@ -2271,9 +2299,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) */ continue; } - if (my_strcasecmp(system_charset_info, - bp->lab->name.str, - lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, &bp->lab->name, &lab->name) == 0) { if (bp->instr_type == GOTO) { @@ -2287,7 +2313,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) } if (bp->instr_type == CPOP) { - int n= lab->ctx->diff_cursors(lab_begin_block->ctx, true); + uint n= lab->ctx->diff_cursors(lab_begin_block->ctx, true); if (n == 0) { // Remove cpop instr @@ -2304,7 +2330,7 @@ sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) } if (bp->instr_type == HPOP) { - int n= lab->ctx->diff_handlers(lab_begin_block->ctx, true); + uint n= lab->ctx->diff_handlers(lab_begin_block->ctx, true); if (n == 0) { // Remove hpop instr @@ -2408,7 +2434,6 @@ sp_head::set_chistics(const st_sp_chistics &chistics) m_chistics.comment.length); } - void sp_head::set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode) @@ -2456,6 +2481,7 @@ sp_head::restore_thd_mem_root(THD *thd) Item *flist= free_list; // The old list set_query_arena(thd); // Get new free_list and mem_root state= STMT_INITIALIZED_FOR_SP; + is_stored_procedure= true; DBUG_PRINT("info", ("mem_root %p returned from thd mem root %p", &mem_root, &thd->mem_root)); @@ -2483,8 +2509,10 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) { TABLE_LIST tables; bzero((char*) &tables,sizeof(tables)); - tables.db= (char*) "mysql"; - tables.table_name= tables.alias= (char*) "proc"; + tables.db= MYSQL_SCHEMA_NAME; + tables.table_name= MYSQL_PROC_NAME; + tables.alias= MYSQL_PROC_NAME; + *full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE, 1, TRUE) && (tables.grant.privilege & SELECT_ACL) != 0) || @@ -2608,7 +2636,7 @@ sp_head::show_create_routine(THD *thd, const Sp_handler *sph) Item_empty_string(thd, col1_caption, NAME_CHAR_LEN), thd->mem_root); fields.push_back(new (mem_root) - Item_empty_string(thd, "sql_mode", sql_mode.length), + Item_empty_string(thd, "sql_mode", (uint)sql_mode.length), thd->mem_root); { @@ -2619,7 +2647,7 @@ sp_head::show_create_routine(THD *thd, const Sp_handler *sph) Item_empty_string *stmt_fld= new (mem_root) Item_empty_string(thd, col3_caption, - MY_MAX(m_defstr.length, 1024)); + (uint)MY_MAX(m_defstr.length, 1024)); stmt_fld->maybe_null= TRUE; @@ -3184,14 +3212,29 @@ int sp_instr_stmt::execute(THD *thd, uint *nextp) { int res; + bool save_enable_slow_log; + const CSET_STRING query_backup= thd->query_string; + QUERY_START_TIME_INFO time_info; + Sub_statement_state backup_state; DBUG_ENTER("sp_instr_stmt::execute"); DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command())); - const CSET_STRING query_backup= thd->query_string; #if defined(ENABLED_PROFILING) /* This s-p instr is profilable and will be captured. */ thd->profiling.set_query_source(m_query.str, m_query.length); #endif + + if ((save_enable_slow_log= thd->enable_slow_log)) + { + /* + Save start time info for the CALL statement and overwrite it with the + current time for log_slow_statement() to log the individual query timing. + */ + thd->backup_query_start_time(&time_info); + thd->set_time(); + } + thd->store_slow_query_state(&backup_state); + if (!(res= alloc_query(thd, m_query.str, m_query.length)) && !(res=subst_spvars(thd, this, &m_query))) { @@ -3204,6 +3247,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) if (query_cache_send_result_to_client(thd, thd->query(), thd->query_length()) <= 0) { + thd->reset_slow_query_state(); res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); bool log_slow= !res && thd->enable_slow_log; @@ -3223,6 +3267,15 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) if (log_slow) log_slow_statement(thd); + + /* + Restore enable_slow_log, that can be changed by a admin or call + command + */ + thd->enable_slow_log= save_enable_slow_log; + + /* Add the number of rows to thd for the 'call' statistics */ + thd->add_slow_query_state(&backup_state); } else { @@ -3243,6 +3296,10 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) thd->get_stmt_da()->reset_diagnostics_area(); } } + /* Restore the original query start time */ + if (thd->enable_slow_log) + thd->restore_query_start_time(&time_info); + DBUG_RETURN(res || thd->is_error()); } @@ -3250,7 +3307,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) void sp_instr_stmt::print(String *str) { - uint i, len; + size_t i, len; /* stmt CMD "..." */ if (str->reserve(SP_STMT_PRINT_MAXLEN+SP_INSTR_UINT_MAXLEN+8)) @@ -3284,7 +3341,7 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp) { MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *)thd->security_ctx->host_or_ip, 3); @@ -3322,7 +3379,7 @@ void sp_instr_set::print(String *str) { /* set name@offset ... */ - int rsrv = SP_INSTR_UINT_MAXLEN+6; + size_t rsrv = SP_INSTR_UINT_MAXLEN+6; sp_variable *var = m_ctx->find_variable(m_offset); /* 'var' should always be non-null, but just in case... */ @@ -3333,7 +3390,7 @@ sp_instr_set::print(String *str) str->qs_append(STRING_WITH_LEN("set ")); if (var) { - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('@'); } str->qs_append(m_offset); @@ -3362,7 +3419,7 @@ void sp_instr_set_row_field::print(String *str) { /* set name@offset[field_offset] ... */ - int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3; + size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3; sp_variable *var= m_ctx->find_variable(m_offset); DBUG_ASSERT(var); DBUG_ASSERT(var->field_def.is_row()); @@ -3374,9 +3431,9 @@ sp_instr_set_row_field::print(String *str) if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("set ")); - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('.'); - str->qs_append(def->field_name.str, def->field_name.length); + str->qs_append(&def->field_name); str->qs_append('@'); str->qs_append(m_offset); str->qs_append('['); @@ -3408,7 +3465,7 @@ void sp_instr_set_row_field_by_name::print(String *str) { /* set name.field@offset["field"] ... */ - int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2; + size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2; sp_variable *var= m_ctx->find_variable(m_offset); DBUG_ASSERT(var); DBUG_ASSERT(var->field_def.is_table_rowtype_ref() || @@ -3418,13 +3475,13 @@ sp_instr_set_row_field_by_name::print(String *str) if (str->reserve(rsrv)) return; str->qs_append(STRING_WITH_LEN("set ")); - str->qs_append(var->name.str, var->name.length); + str->qs_append(&var->name); str->qs_append('.'); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append('@'); str->qs_append(m_offset); str->qs_append("[\"",2); - str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append(&m_field_name); str->qs_append("\"]",2); str->qs_append(' '); m_value->print(str, enum_query_type(QT_ORDINARY | @@ -3886,7 +3943,7 @@ sp_instr_cpush::print(String *str) const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cpush name@offset */ - uint rsrv= SP_INSTR_UINT_MAXLEN+7; + size_t rsrv= SP_INSTR_UINT_MAXLEN+7; if (cursor_name) rsrv+= cursor_name->length; @@ -3974,7 +4031,7 @@ sp_instr_copen::print(String *str) const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* copen name@offset */ - uint rsrv= SP_INSTR_UINT_MAXLEN+7; + size_t rsrv= SP_INSTR_UINT_MAXLEN+7; if (cursor_name) rsrv+= cursor_name->length; @@ -4016,7 +4073,7 @@ sp_instr_cclose::print(String *str) const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cclose name@offset */ - uint rsrv= SP_INSTR_UINT_MAXLEN+8; + size_t rsrv= SP_INSTR_UINT_MAXLEN+8; if (cursor_name) rsrv+= cursor_name->length; @@ -4059,7 +4116,7 @@ sp_instr_cfetch::print(String *str) const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cfetch name@offset vars... */ - uint rsrv= SP_INSTR_UINT_MAXLEN+8; + size_t rsrv= SP_INSTR_UINT_MAXLEN+8; if (cursor_name) rsrv+= cursor_name->length; @@ -4077,12 +4134,41 @@ sp_instr_cfetch::print(String *str) if (str->reserve(pv->name.length+SP_INSTR_UINT_MAXLEN+2)) return; str->qs_append(' '); - str->qs_append(pv->name.str, pv->name.length); + str->qs_append(&pv->name); str->qs_append('@'); str->qs_append(pv->offset); } } +int +sp_instr_agg_cfetch::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cfetch::execute"); + int res= 0; + if (!thd->spcont->instr_ptr) + { + *nextp= m_ip+1; + thd->spcont->instr_ptr= m_ip + 1; + } + else if (!thd->spcont->pause_state) + thd->spcont->pause_state= TRUE; + else + { + thd->spcont->pause_state= FALSE; + if (thd->server_status == SERVER_STATUS_LAST_ROW_SENT) + { + my_message(ER_SP_FETCH_NO_DATA, + ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0)); + res= -1; + thd->spcont->quit_func= TRUE; + } + else + *nextp= m_ip + 1; + } + DBUG_RETURN(res); +} + + /* sp_instr_cursor_copy_struct class functions @@ -4285,7 +4371,7 @@ typedef struct st_sp_table db_name\0table_name\0 - for temporary tables */ LEX_STRING qname; - uint db_length, table_name_length; + size_t db_length, table_name_length; bool temp; /* true if corresponds to a temporary table */ thr_lock_type lock_type; /* lock type used for prelocking */ uint lock_count; @@ -4353,12 +4439,12 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) uint temp_table_key_length; tname.length(0); - tname.append(table->db, table->db_length); + tname.append(&table->db); tname.append('\0'); - tname.append(table->table_name, table->table_name_length); + tname.append(&table->table_name); tname.append('\0'); temp_table_key_length= tname.length(); - tname.append(table->alias); + tname.append(&table->alias); tname.append('\0'); /* @@ -4404,8 +4490,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length); if (!tab->qname.str) return FALSE; - tab->table_name_length= table->table_name_length; - tab->db_length= table->db_length; + tab->table_name_length= table->table_name.length; + tab->db_length= table->db.length; tab->lock_type= table->lock_type; tab->lock_count= tab->query_lock_count= 1; tab->trg_event_map= table->trg_event_map; @@ -4462,8 +4548,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, for (i=0 ; i < m_sptabs.records ; i++) { char *tab_buff, *key_buff; - TABLE_LIST *table; SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i); + LEX_CSTRING db_name; if (stab->temp) continue; @@ -4473,15 +4559,26 @@ sp_head::add_used_tables_to_table_list(THD *thd, stab->qname.length))) DBUG_RETURN(FALSE); + db_name.str= key_buff; + db_name.length= stab->db_length; + + for (uint j= 0; j < stab->lock_count; j++) { - table= (TABLE_LIST *)tab_buff; - table->init_one_table_for_prelocking(key_buff, stab->db_length, - key_buff + stab->db_length + 1, stab->table_name_length, - key_buff + stab->db_length + stab->table_name_length + 2, - stab->lock_type, true, belong_to_view, stab->trg_event_map, - query_tables_last_ptr); - + TABLE_LIST *table= (TABLE_LIST *)tab_buff; + LEX_CSTRING table_name= { key_buff + stab->db_length + 1, + stab->table_name_length }; + LEX_CSTRING alias= { table_name.str + table_name.length + 1, + strlen(table_name.str + table_name.length + 1) }; + + table->init_one_table_for_prelocking(&db_name, + &table_name, + &alias, + stab->lock_type, + TABLE_LIST::PRELOCK_ROUTINE, + belong_to_view, + stab->trg_event_map, + query_tables_last_ptr); tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST)); result= TRUE; } @@ -4501,7 +4598,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, TABLE_LIST * sp_add_to_query_tables(THD *thd, LEX *lex, - const char *db, const char *name, + const LEX_CSTRING *db, const LEX_CSTRING *name, thr_lock_type locktype, enum_mdl_type mdl_type) { @@ -4509,15 +4606,15 @@ sp_add_to_query_tables(THD *thd, LEX *lex, if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) return NULL; - table->db_length= strlen(db); - table->db= thd->strmake(db, table->db_length); - table->table_name_length= strlen(name); - table->table_name= thd->strmake(name, table->table_name_length); - table->alias= thd->strdup(name); + if (!thd->make_lex_string(&table->db, db->str, db->length) || + !thd->make_lex_string(&table->table_name, name->str, name->length) || + !thd->make_lex_string(&table->alias, name->str, name->length)) + return NULL; + table->lock_type= locktype; table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str, mdl_type, MDL_TRANSACTION); lex->add_to_query_tables(table); diff --git a/sql/sp_head.h b/sql/sp_head.h index af1381647f0..7f041058829 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -27,7 +27,6 @@ are dependencies on include order for set_var.h and item.h. This will be resolved later. */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_class.h" // THD, set_var.h: THD #include "set_var.h" // Item #include "sp_pcontext.h" // sp_pcontext @@ -165,7 +164,10 @@ public: /* Marks routines that have column type references: DECLARE a t1.a%TYPE; */ - HAS_COLUMN_TYPE_REFS= 8192 + HAS_COLUMN_TYPE_REFS= 8192, + /* Set if has FETCH GROUP NEXT ROW instr. Used to ensure that only + functions with AGGREGATE keyword use the instr. */ + HAS_AGGREGATE_INSTR= 16384 }; const Sp_handler *m_handler; @@ -198,6 +200,7 @@ public: enum_sp_suid_behaviour suid() const { return m_chistics.suid; } bool detistic() const { return m_chistics.detistic; } enum_sp_data_access daccess() const { return m_chistics.daccess; } + enum_sp_aggregate_type agg_type() const { return m_chistics.agg_type; } /** Is this routine being executed? */ @@ -210,7 +213,7 @@ public: ulong sp_cache_version() const { return m_sp_cache_version; } /** Set the value of the SP cache version. */ - void set_sp_cache_version(ulong version_arg) + void set_sp_cache_version(ulong version_arg) const { m_sp_cache_version= version_arg; } @@ -232,7 +235,7 @@ private: is obsolete and should not be used -- sp_cache_flush_obsolete() will purge it. */ - ulong m_sp_cache_version; + mutable ulong m_sp_cache_version; Stored_program_creation_ctx *m_creation_ctx; /** Boolean combination of (1<<flag), where flag is a member of @@ -343,7 +346,8 @@ public: GRANT_INFO *grant_info); bool - execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); + execute_function(THD *thd, Item **args, uint argcount, Field *return_fld, + sp_rcontext **nctx, Query_arena *call_arena); bool execute_procedure(THD *thd, List<Item> *args); @@ -719,10 +723,14 @@ public: const LEX_CSTRING &table); void set_chistics(const st_sp_chistics &chistics); + inline void set_chistics_agg_type(enum enum_sp_aggregate_type type) + { + m_chistics.agg_type= type; + } void set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode); - void set_definer(const char *definer, uint definerlen) + void set_definer(const char *definer, size_t definerlen) { AUTHID tmp; tmp.parse(definer, definerlen); @@ -1819,6 +1827,32 @@ private: }; // class sp_instr_cfetch : public sp_instr +/* +This class is created for the special fetch instruction +FETCH GROUP NEXT ROW, used in the user-defined aggregate +functions +*/ + +class sp_instr_agg_cfetch : public sp_instr +{ + sp_instr_agg_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */ + void operator=(sp_instr_cfetch &); + +public: + + sp_instr_agg_cfetch(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx){} + + virtual ~sp_instr_agg_cfetch() + {} + + virtual int execute(THD *thd, uint *nextp); + + virtual void print(String *str){}; +}; // class sp_instr_agg_cfetch : public sp_instr + + + class sp_instr_error : public sp_instr { @@ -1903,7 +1937,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx); TABLE_LIST * sp_add_to_query_tables(THD *thd, LEX *lex, - const char *db, const char *name, + const LEX_CSTRING *db, const LEX_CSTRING *name, thr_lock_type locktype, enum_mdl_type mdl_type); diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index d98f8005945..83d674e7500 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION @@ -132,8 +132,8 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope) bool cmp_labels(sp_label *a, sp_label *b) { - return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0 - && a->type == b->type); + return (lex_string_cmp(system_charset_info, &a->name, &b->name) == 0 && + a->type == b->type); } sp_pcontext *sp_pcontext::pop_context() @@ -171,12 +171,12 @@ uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const while (pctx && pctx != ctx) { - n+= pctx->m_handlers.elements(); + n+= (uint)pctx->m_handlers.elements(); last_ctx= pctx; pctx= pctx->parent_context(); } if (pctx) - return (exclusive && last_ctx ? n - last_ctx->m_handlers.elements() : n); + return (exclusive && last_ctx ? n -(uint) last_ctx->m_handlers.elements() : n); return 0; // Didn't find ctx } @@ -189,12 +189,12 @@ uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const while (pctx && pctx != ctx) { - n+= pctx->m_cursors.elements(); + n+= (uint)pctx->m_cursors.elements(); last_ctx= pctx; pctx= pctx->parent_context(); } if (pctx) - return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements() : n); + return (exclusive && last_ctx ? (uint)(n - last_ctx->m_cursors.elements()) : n); return 0; // Didn't find ctx } @@ -202,7 +202,7 @@ uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name, bool current_scope_only) const { - uint i= m_vars.elements() - m_pboundary; + size_t i= m_vars.elements() - m_pboundary; while (i--) { @@ -304,7 +304,7 @@ sp_label *sp_pcontext::find_goto_label(const LEX_CSTRING *name, bool recusive) while ((lab= li++)) { - if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, name, &lab->name) == 0) return lab; } @@ -341,7 +341,7 @@ sp_label *sp_pcontext::find_label(const LEX_CSTRING *name) while ((lab= li++)) { - if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) + if (lex_string_cmp(system_charset_info, name, &lab->name) == 0) return lab; } @@ -392,7 +392,7 @@ bool sp_pcontext::add_condition(THD *thd, sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name, bool current_scope_only) const { - uint i= m_conditions.elements(); + size_t i= m_conditions.elements(); while (i--) { @@ -416,17 +416,19 @@ static sp_condition_value // Errors cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"), cond_dup_val_on_index(ER_DUP_ENTRY, "23000"), + cond_dup_val_on_index2(ER_DUP_ENTRY_WITH_KEY_NAME, "23000"), cond_too_many_rows(ER_TOO_MANY_ROWS, "42000"); static sp_condition sp_predefined_conditions[]= { // Warnings - sp_condition(C_STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found), + sp_condition(STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found), // Errors - sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor), - sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index), - sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) + sp_condition(STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor), + sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index), + sp_condition(STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index2), + sp_condition(STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) }; @@ -603,7 +605,7 @@ const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name, uint *poff, bool current_scope_only) const { - uint i= m_cursors.elements(); + uint i= (uint)m_cursors.elements(); while (i--) { diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 990ad35cb57..6a6920d89e1 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -673,7 +673,7 @@ public: { return m_cursor_offset; } uint frame_cursor_count() const - { return m_cursors.elements(); } + { return (uint)m_cursors.elements(); } uint max_cursor_index() const { return m_max_cursor_index + (uint)m_cursors.elements(); } diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 159a25e7594..4009f8dce30 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #ifdef USE_PRAGMA_IMPLEMENTATION @@ -40,9 +40,8 @@ sp_rcontext::sp_rcontext(const sp_head *owner, Field *return_value_fld, bool in_sub_stmt) :end_partial_result_set(false), -#ifndef DBUG_OFF + pause_state(false), quit_func(false), instr_ptr(0), m_sp(owner), -#endif m_root_parsing_ctx(root_parsing_ctx), m_var_table(NULL), m_return_value_fld(return_value_fld), @@ -414,7 +413,7 @@ bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper) } -void sp_rcontext::pop_cursors(uint count) +void sp_rcontext::pop_cursors(size_t count) { DBUG_ASSERT(m_ccount >= count); diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 00816b5ea8a..c4f4cf182da 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -178,12 +178,14 @@ public: /// (if one is found). Otherwise the client will hang due to a violation /// of the client/server protocol. bool end_partial_result_set; + bool pause_state; + bool quit_func; + uint instr_ptr; -#ifndef DBUG_OFF /// The stored program for which this runtime context is created. Used for /// checking if correct runtime context is used for variable handling. + /// Also used by slow log. const sp_head *m_sp; -#endif ///////////////////////////////////////////////////////////////////////// // SP-variables. @@ -303,7 +305,7 @@ public: /// Pop and delete given number of sp_cursor instance from the cursor stack. /// /// @param count Number of cursors to pop & delete. - void pop_cursors(uint count); + void pop_cursors(size_t count); void pop_all_cursors() { pop_cursors(m_ccount); } diff --git a/sql/spatial.cc b/sql/spatial.cc index 2aca528dd15..255ba3f0647 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "spatial.h" #include "gstream.h" // Gis_read_stream @@ -142,7 +142,7 @@ static void get_point(double *x, double *y, const char *data) /***************************** Geometry *******************************/ -Geometry::Class_info *Geometry::find_class(const char *name, uint32 len) +Geometry::Class_info *Geometry::find_class(const char *name, size_t len) { for (Class_info **cur_rt= ci_collection; cur_rt < ci_collection_end; cur_rt++) diff --git a/sql/spatial.h b/sql/spatial.h index 901544b6916..5818607de26 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -347,7 +347,7 @@ protected: return ((type_id < wkb_point) || (type_id > wkb_last)) ? NULL : ci_collection[type_id]; } - static Class_info *find_class(const char *name, uint32 len); + static Class_info *find_class(const char *name, size_t len); const char *append_points(String *txt, uint32 n_points, const char *data, uint32 offset) const; bool create_point(String *result, const char *data) const; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f2124818687..b1faed6aa36 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -25,7 +25,7 @@ in the relevant fields. Empty strings comes last. */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS #include "sql_base.h" // close_mysql_tables @@ -59,123 +59,6 @@ bool mysql_user_table_is_in_short_password_format= false; -static const -TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { - { - { C_STRING_WITH_LEN("Host") }, - { C_STRING_WITH_LEN("char(60)") }, - {NULL, 0} - }, - { - { C_STRING_WITH_LEN("Db") }, - { C_STRING_WITH_LEN("char(64)") }, - {NULL, 0} - }, - { - { C_STRING_WITH_LEN("User") }, - { C_STRING_WITH_LEN("char(") }, - {NULL, 0} - }, - { - { C_STRING_WITH_LEN("Select_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Insert_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Update_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Delete_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Create_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Drop_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Grant_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("References_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Index_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Alter_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Create_tmp_table_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Lock_tables_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Create_view_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Show_view_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Create_routine_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Alter_routine_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Execute_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Event_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - }, - { - { C_STRING_WITH_LEN("Trigger_priv") }, - { C_STRING_WITH_LEN("enum('N','Y')") }, - { C_STRING_WITH_LEN("utf8") } - } -}; - -const TABLE_FIELD_DEF -mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 }; - static LEX_CSTRING native_password_plugin_name= { STRING_WITH_LEN("mysql_native_password") }; @@ -242,7 +125,7 @@ class ACL_USER_BASE :public ACL_ACCESS 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) */ @@ -253,7 +136,7 @@ class ACL_USER :public ACL_USER_BASE { public: acl_host_and_ip host; - uint hostname_length; + 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 @@ -695,9 +578,9 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username, #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #define NORMAL_HANDSHAKE_SIZE 6 -#define ROLE_ASSIGN_COLUMN_IDX 43 -#define DEFAULT_ROLE_COLUMN_IDX 44 -#define MAX_STATEMENT_TIME_COLUMN_IDX 45 +#define ROLE_ASSIGN_COLUMN_IDX 44 +#define DEFAULT_ROLE_COLUMN_IDX 45 +#define MAX_STATEMENT_TIME_COLUMN_IDX 46 /* various flags valid for ACL_USER */ #define IS_ROLE (1L << 0) @@ -739,7 +622,7 @@ 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 *, uint); + char *, size_t new_password_len); 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); @@ -854,7 +737,7 @@ class Grant_table_base if (table_exists()) return 0; - my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db, tl.alias); + my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str); return 1; } @@ -1019,9 +902,7 @@ class User_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("user"), - NULL, lock_type); + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type); Grant_table_base::init(lock_type, false); } @@ -1065,9 +946,7 @@ class Db_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("db"), - NULL, lock_type); + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type); Grant_table_base::init(lock_type, false); } }; @@ -1092,9 +971,8 @@ class Tables_priv_table: public Grant_table_base void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("tables_priv"), - NULL, lock_type); + LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type); Grant_table_base::init(lock_type, false); } }; @@ -1118,9 +996,8 @@ class Columns_priv_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("columns_priv"), - NULL, lock_type); + LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type); Grant_table_base::init(lock_type, false); } }; @@ -1139,9 +1016,8 @@ class Host_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("host"), - NULL, lock_type); + LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type); Grant_table_base::init(lock_type, true); } }; @@ -1166,9 +1042,8 @@ class Procs_priv_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("procs_priv"), - NULL, lock_type); + LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type); Grant_table_base::init(lock_type, true); } }; @@ -1192,9 +1067,8 @@ class Proxies_priv_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("proxies_priv"), - NULL, lock_type); + LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type); Grant_table_base::init(lock_type, true); } }; @@ -1215,9 +1089,8 @@ class Roles_mapping_table: public Grant_table_base void init(enum thr_lock_type lock_type) { /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("roles_mapping"), - NULL, lock_type); + LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") }; + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type); Grant_table_base::init(lock_type, true); } }; @@ -1550,7 +1423,7 @@ static bool validate_password(LEX_USER *user, THD *thd) */ static void -set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) +set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len) { if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) { @@ -1594,12 +1467,10 @@ static const char *fix_plugin_ptr(const char *name) */ static bool fix_user_plugin_ptr(ACL_USER *user) { - if (my_strcasecmp(system_charset_info, user->plugin.str, - native_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &native_password_plugin_name) == 0) user->plugin= native_password_plugin_name; else - if (my_strcasecmp(system_charset_info, user->plugin.str, - old_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &old_password_plugin_name) == 0) user->plugin= old_password_plugin_name; else return true; @@ -1639,12 +1510,10 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) DBUG_ASSERT(user->plugin.length || !user->auth.length); DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); - if (my_strcasecmp(system_charset_info, user->plugin.str, - native_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &native_password_plugin_name) == 0) check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; else - if (my_strcasecmp(system_charset_info, user->plugin.str, - old_password_plugin_name.str) == 0) + if (lex_string_eq(&user->plugin, &old_password_plugin_name) == 0) check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; else if (user->plugin.length) @@ -1777,7 +1646,7 @@ 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, int password_len) +static bool set_user_plugin (ACL_USER *user, size_t password_len) { switch (password_len) { @@ -1832,7 +1701,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { if (host_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(true); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_HOST host; update_hostname(&host.host, get_field(&acl_memroot, host_table.host())); @@ -1936,7 +1805,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) } allow_all_hosts=0; - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_USER user; bool is_role= FALSE; @@ -1971,7 +1840,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) char *password= const_cast<char*>(""); if (user_table.password()) password= get_field(&acl_memroot, user_table.password()); - uint password_len= safe_strlen(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); @@ -2017,6 +1886,9 @@ static bool acl_load(THD *thd, const Grant_tables& tables) 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; + 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; @@ -2148,7 +2020,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) const Db_table& db_table= tables.db_table(); if (db_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(TRUE); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_DB db; char *db_name; @@ -2215,7 +2087,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) { if (proxies_priv_table.init_read_record(&read_record_info, thd)) DBUG_RETURN(TRUE); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { ACL_PROXY_USER proxy; proxy.init(proxies_priv_table, &acl_memroot); @@ -2244,7 +2116,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables) MEM_ROOT temp_root; init_alloc_root(&temp_root, "ACL_tmp", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host())); char *username= safe_str(get_field(&temp_root, roles_mapping_table.user())); @@ -2687,8 +2559,8 @@ int acl_setrole(THD *thd, const char *rolename, ulonglong access) /* merge the privileges */ Security_context *sctx= thd->security_ctx; sctx->master_access= static_cast<ulong>(access); - if (thd->db) - sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db, FALSE); + if (thd->db.str) + sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE); if (!strcasecmp(rolename, "NONE")) { @@ -2696,8 +2568,8 @@ int acl_setrole(THD *thd, const char *rolename, ulonglong access) } else { - if (thd->db) - sctx->db_access|= acl_get("", "", rolename, thd->db, FALSE); + if (thd->db.str) + sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE); /* mark the current role */ strmake_buf(thd->security_ctx->priv_role, rolename); } @@ -2721,7 +2593,7 @@ static void acl_update_role(const char *rolename, ulong privileges) static void acl_update_user(const char *user, const char *host, - const char *password, uint password_len, + const char *password, size_t password_len, enum SSL_type ssl_type, const char *ssl_cipher, const char *x509_issuer, @@ -2799,7 +2671,7 @@ static void acl_insert_role(const char *rolename, ulong privileges) static void acl_insert_user(const char *user, const char *host, - const char *password, uint password_len, + const char *password, size_t password_len, enum SSL_type ssl_type, const char *ssl_cipher, const char *x509_issuer, @@ -3181,7 +3053,7 @@ static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr) { DBUG_ASSERT(!found); delete_dynamic_element(array, i); - IF_DBUG(found= true, break); + IF_DBUG_ASSERT(found= true, break); } } DBUG_ASSERT(found); @@ -3415,6 +3287,7 @@ bool change_password(THD *thd, LEX_USER *user) 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); + set_user_plugin(acl_user, user->pwhash.length); } else @@ -3875,8 +3748,7 @@ bool hostname_requires_resolving(const char *hostname) void set_authentication_plugin_from_password(const User_table& user_table, - const char* password, - uint password_length) + const char* password, size_t password_length) { if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH) { @@ -3910,7 +3782,7 @@ void set_authentication_plugin_from_password(const User_table& user_table, static bool update_user_table(THD *thd, const User_table& user_table, const char *host, const char *user, - const char *new_password, uint new_password_len) + const char *new_password, size_t new_password_len) { char user_key[MAX_KEY_LENGTH]; int error; @@ -3969,14 +3841,13 @@ static bool test_if_create_new_users(THD *thd) { TABLE_LIST tl; ulong db_access; - tl.init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("user"), "user", TL_WRITE); + tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE); create_new_users= 1; db_access=acl_get(sctx->host, sctx->ip, - sctx->priv_user, tl.db, 0); + sctx->priv_user, tl.db.str, 0); if (sctx->priv_role[0]) - db_access|= acl_get("", "", sctx->priv_role, tl.db, 0); + db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0); if (!(db_access & INSERT_ACL)) { if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) @@ -5023,7 +4894,7 @@ table_hash_search(const char *host, const char *ip, const char *db, static GRANT_COLUMN * -column_hash_search(GRANT_TABLE *t, const char *cname, uint length) +column_hash_search(GRANT_TABLE *t, const char *cname, size_t length) { if (!my_hash_inited(&t->hash_columns)) return (GRANT_COLUMN*) 0; @@ -6478,7 +6349,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (f == (Field*)0) { my_error(ER_BAD_FIELD_ERROR, MYF(0), - column->column.c_ptr(), table_list->alias); + column->column.c_ptr(), table_list->alias.str); DBUG_RETURN(TRUE); } if (f == (Field *)-1) @@ -6491,9 +6362,10 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { if (!(rights & CREATE_ACL)) { - if (!ha_table_exists(thd, table_list->db, table_list->table_name)) + if (!ha_table_exists(thd, &table_list->db, &table_list->table_name)) { - my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str, + table_list->alias.str); DBUG_RETURN(TRUE); } } @@ -6504,7 +6376,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, table_list->grant.want_privilege); my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command, thd->security_ctx->priv_user, - thd->security_ctx->host_or_ip, table_list->alias); + thd->security_ctx->host_or_ip, table_list->alias.str); DBUG_RETURN(-1); } } @@ -6580,7 +6452,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (revoke_grant) { my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), - Str->user.str, Str->host.str, table_list->table_name); + Str->user.str, Str->host.str, table_list->table_name.str); result= TRUE; continue; } @@ -6747,8 +6619,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, continue; } - db_name= table_list->db; - table_name= table_list->table_name; + db_name= table_list->db.str; + table_name= table_list->table_name.str; grant_name= routine_hash_search(Str->host.str, NullS, db_name, Str->user.str, table_name, sph, 1); if (!grant_name || !grant_name->init_privs) @@ -7603,10 +7475,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; ulong orig_want_access= original_want_access; - if (t_ref->sequence) + /* + If sequence is used as part of NEXT VALUE, PREVIUS VALUE or SELECT, + we need to modify the requested access rights depending on how the + sequence is used. + */ + if (t_ref->sequence && + (bool)(orig_want_access & + (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL))) { - /* We want to have either SELECT or INSERT rights to sequences depending - on how they are accessed + /* + We want to have either SELECT or INSERT rights to sequences depending + on how they are accessed */ orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ? INSERT_ACL : SELECT_ACL); @@ -7773,7 +7653,7 @@ err: bool check_grant_column(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, - const char *name, uint length, Security_context *sctx) + const char *name, size_t length, Security_context *sctx) { GRANT_TABLE *grant_table; GRANT_TABLE *grant_table_role; @@ -7868,7 +7748,7 @@ err: */ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, - const char *name, uint length) + const char *name, size_t length) { GRANT_INFO *grant; const char *db_name; @@ -8167,13 +8047,13 @@ bool check_grant_routine(THD *thd, ulong want_access, for (table= procs; table; table= table->next_global) { GRANT_NAME *grant_proc; - if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, - table->table_name, sph, 0))) + if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user, + table->table_name.str, sph, 0))) table->grant.privilege|= grant_proc->privs; if (role[0]) /* current role set check */ { - if ((grant_proc= routine_hash_search("", NULL, table->db, role, - table->table_name, sph, 0))) + if ((grant_proc= routine_hash_search("", NULL, table->db.str, role, + table->table_name.str, sph, 0))) table->grant.privilege|= grant_proc->privs; } @@ -8192,7 +8072,7 @@ err: char buff[1024]; const char *command=""; if (table) - strxmov(buff, table->db, ".", table->table_name, NullS); + strxmov(buff, table->db.str, ".", table->table_name.str, NullS); if (want_access & EXECUTE_ACL) command= "execute"; else if (want_access & ALTER_PROC_ACL) @@ -8255,7 +8135,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) { ulong privilege; Security_context *sctx= thd->security_ctx; - const char *db = table->db ? table->db : thd->db; + const char *db = table->db.str ? table->db.str : thd->db.str; GRANT_TABLE *grant_table; GRANT_TABLE *grant_table_role= NULL; @@ -8265,10 +8145,10 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) grant_table_role= NULL; #else grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, - table->table_name, 0); + table->table_name.str, 0); if (sctx->priv_role[0]) grant_table_role= table_hash_search("", "", db, sctx->priv_role, - table->table_name, 0); + table->table_name.str, 0); #endif table->grant.grant_table_user= grant_table; // Remember for column test table->grant.grant_table_role= grant_table_role; @@ -8405,18 +8285,18 @@ static void add_user_parameters(String *result, ACL_USER* acl_user, { DBUG_ASSERT(acl_user->salt_len); result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); - result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append(&acl_user->auth_string); result->append('\''); } } else { result->append(STRING_WITH_LEN(" IDENTIFIED VIA ")); - result->append(acl_user->plugin.str, acl_user->plugin.length); + result->append(&acl_user->plugin); if (acl_user->auth_string.length) { result->append(STRING_WITH_LEN(" USING '")); - result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append(&acl_user->auth_string); result->append('\''); } } @@ -8485,13 +8365,14 @@ static const char *command_array[]= "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", - "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE" + "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", + "DELETE VERSIONING ROWS" }; static uint command_lengths[]= { 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, - 14, 13, 11, 5, 7, 17 + 14, 13, 11, 5, 7, 17, 22, }; @@ -9181,7 +9062,8 @@ static int show_routine_grants(THD* thd, } } global.append(STRING_WITH_LEN(" ON ")); - global.append(sph->type_lex_cstring()); + LEX_CSTRING tmp= sph->type_lex_cstring(); + global.append(&tmp); global.append(' '); append_identifier(thd, &global, grant_proc->db, strlen(grant_proc->db)); @@ -11013,8 +10895,10 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bzero((char*)tables, sizeof(TABLE_LIST)); user_list.empty(); - tables->db= (char*)sp_db; - tables->table_name= tables->alias= (char*)sp_name; + tables->db.str= sp_db; + tables->db.length= sp_db ? strlen(sp_db) : 0; + tables->table_name.str= tables->alias.str= sp_name; + tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0; thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str)); thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str)); @@ -11234,7 +11118,7 @@ applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr) if (!is_role) { if (data->user->default_rolename.length && - !strcmp(data->user->default_rolename.str, role->user.str)) + !lex_string_eq(&data->user->default_rolename, &role->user)) table->field[3]->store(STRING_WITH_LEN("YES"), cs); else table->field[3]->store(STRING_WITH_LEN("NO"), cs); @@ -11813,7 +11697,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, /* global privileges */ grant->privilege= sctx->master_access; - if (!thd->db || strcmp(db, thd->db)) + if (!thd->db.str || strcmp(db, thd->db.str)) { /* db privileges */ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); @@ -12408,7 +12292,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) } mpvio->auth_info.user_name= sctx->user; - mpvio->auth_info.user_name_length= strlen(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)); @@ -12446,12 +12330,10 @@ read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs) if (length > 65535) return true; -#ifdef HAVE_PSI_THREAD_INTERFACE - if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, (size_t)length, from_cs) && + if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) && current_thd->variables.log_warnings) sql_print_warning("Connection attributes of length %llu were truncated", length); -#endif return false; } @@ -12493,7 +12375,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) *passwd > 127 and become 2**32-127+ after casting to uint. */ uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar) (*passwd++) : strlen(passwd)); + (uchar) (*passwd++) : (uint)strlen(passwd)); db+= passwd_len + 1; /* @@ -12507,7 +12389,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) DBUG_RETURN (1); } - uint db_len= strlen(db); + size_t db_len= strlen(db); char *next_field= db + db_len + 1; @@ -12541,7 +12423,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) connection is closed. We don't want to accidentally free a wrong pointer if connect failed. */ - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); if (!initialized) { @@ -12707,7 +12589,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, char *user= end; char *passwd= strend(user)+1; - uint user_len= (uint)(passwd - user - 1), db_len; + size_t user_len= (size_t)(passwd - user - 1), db_len; char *db= passwd; char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 uint dummy_errors; @@ -12792,7 +12674,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, connection is closed. We don't want to accidentally free a wrong pointer if connect failed. */ - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); if (!initialized) { @@ -12846,11 +12728,11 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, restarted and a server auth plugin will read the data that the client has just send. Cache them to return in the next server_mpvio_read_packet(). */ - if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, - plugin_name(mpvio->plugin)->str) != 0) + if (lex_string_eq(&mpvio->acl_user->plugin, + plugin_name(mpvio->plugin)) != 0) { mpvio->cached_client_reply.pkt= passwd; - mpvio->cached_client_reply.pkt_len= passwd_len; + mpvio->cached_client_reply.pkt_len= (uint)passwd_len; mpvio->cached_client_reply.plugin= client_plugin; mpvio->status= MPVIO_EXT::RESTART; return packet_error; @@ -12879,7 +12761,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, } *buff= (uchar*) passwd; - return passwd_len; + return (ulong)passwd_len; #else return 0; #endif @@ -13277,8 +13159,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { DBUG_ASSERT(mpvio.acl_user); DBUG_ASSERT(command == COM_CHANGE_USER || - my_strcasecmp(system_charset_info, auth_plugin_name->str, - mpvio.acl_user->plugin.str)); + lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin)); auth_plugin_name= &mpvio.acl_user->plugin; res= do_auth_once(thd, auth_plugin_name, &mpvio); } @@ -13525,11 +13406,9 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) else my_ok(thd); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread_user_host) - (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user), - thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip)); -#endif + PSI_CALL_set_thread_user_host + (thd->main_security_ctx.user, (uint)strlen(thd->main_security_ctx.user), + thd->main_security_ctx.host_or_ip, (uint)strlen(thd->main_security_ctx.host_or_ip)); /* Ready to handle queries */ DBUG_RETURN(0); @@ -13659,7 +13538,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, We need to figure out the correct scramble length here. */ if (pkt_len == SCRAMBLE_LENGTH_323 + 1) - pkt_len= strnlen((char*)pkt, pkt_len); + pkt_len= (int)strnlen((char*)pkt, pkt_len); if (pkt_len == 0) /* no password */ return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cc6e16a19e5..a608ef0dd77 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -17,7 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "violite.h" /* SSL_type */ #include "sql_class.h" /* LEX_COLUMN */ @@ -50,6 +49,7 @@ #define EVENT_ACL (1UL << 26) #define TRIGGER_ACL (1UL << 27) #define CREATE_TABLESPACE_ACL (1UL << 28) +#define DELETE_HISTORY_ACL (1UL << 29) /* don't forget to update 1. static struct show_privileges_st sys_privileges[] @@ -63,12 +63,13 @@ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ - CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL) + CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL | \ + DELETE_HISTORY_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \ - SHOW_VIEW_ACL | TRIGGER_ACL) + SHOW_VIEW_ACL | TRIGGER_ACL | DELETE_HISTORY_ACL) #define COL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) @@ -86,7 +87,7 @@ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \ - CREATE_TABLESPACE_ACL) + CREATE_TABLESPACE_ACL | DELETE_HISTORY_ACL) #define DEFAULT_CREATE_PROC_ACLS \ (ALTER_PROC_ACL | EXECUTE_ACL) @@ -118,31 +119,37 @@ CREATE_PROC_ACL | ALTER_PROC_ACL ) #define DB_CHUNK4 (EXECUTE_ACL) #define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL) +#define DB_CHUNK6 (DELETE_HISTORY_ACL) #define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) << 4) & DB_CHUNK1) | \ (((A) << 6) & DB_CHUNK2) | \ (((A) << 9) & DB_CHUNK3) | \ - (((A) << 2) & DB_CHUNK4))| \ - (((A) << 9) & DB_CHUNK5) + (((A) << 2) & DB_CHUNK4) | \ + (((A) << 9) & DB_CHUNK5) | \ + (((A) << 10) & DB_CHUNK6)) #define get_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) & DB_CHUNK1) >> 4) | \ (((A) & DB_CHUNK2) >> 6) | \ (((A) & DB_CHUNK3) >> 9) | \ - (((A) & DB_CHUNK4) >> 2))| \ - (((A) & DB_CHUNK5) >> 9) + (((A) & DB_CHUNK4) >> 2) | \ + (((A) & DB_CHUNK5) >> 9) | \ + (((A) & DB_CHUNK6) >> 10)) #define TBL_CHUNK0 DB_CHUNK0 #define TBL_CHUNK1 DB_CHUNK1 #define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) #define TBL_CHUNK3 TRIGGER_ACL +#define TBL_CHUNK4 (DELETE_HISTORY_ACL) #define fix_rights_for_table(A) (((A) & TBL_CHUNK0) | \ (((A) << 4) & TBL_CHUNK1) | \ (((A) << 11) & TBL_CHUNK2) | \ - (((A) << 15) & TBL_CHUNK3)) + (((A) << 15) & TBL_CHUNK3) | \ + (((A) << 16) & TBL_CHUNK4)) #define get_rights_for_table(A) (((A) & TBL_CHUNK0) | \ (((A) & TBL_CHUNK1) >> 4) | \ (((A) & TBL_CHUNK2) >> 11) | \ - (((A) & TBL_CHUNK3) >> 15)) + (((A) & TBL_CHUNK3) >> 15) | \ + (((A) & TBL_CHUNK4) >> 16)) #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) #define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) #define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \ @@ -176,6 +183,7 @@ enum mysql_db_table_field MYSQL_DB_FIELD_EXECUTE_PRIV, MYSQL_DB_FIELD_EVENT_PRIV, MYSQL_DB_FIELD_TRIGGER_PRIV, + MYSQL_DB_FIELD_DELETE_VERSIONING_ROWS_PRIV, MYSQL_DB_FIELD_COUNT }; @@ -229,9 +237,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, bool any_combination_will_do, uint number, bool no_errors); bool check_grant_column (THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, - const char *name, uint length, Security_context *sctx); + const char *name, size_t length, Security_context *sctx); bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, - const char *name, uint length); + const char *name, size_t length); bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index dd3f2e8abf8..4cff69060d1 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -14,7 +14,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "sql_class.h" // THD and my_global.h +#include "mariadb.h" +#include "sql_class.h" // THD #include "keycaches.h" // get_key_cache #include "sql_base.h" // Open_table_context #include "lock.h" // MYSQL_OPEN_* @@ -76,7 +77,7 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table, { Protocol *protocol= thd->protocol; protocol->prepare_for_resend(); - protocol->store(table->alias, system_charset_info); + protocol->store(table->alias.str, table->alias.length, system_charset_info); protocol->store((char*) operator_name, system_charset_info); protocol->store(STRING_WITH_LEN("error"), system_charset_info); protocol->store(errmsg, system_charset_info); @@ -122,7 +123,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, */ table_list->mdl_request.init(MDL_key::TABLE, - table_list->db, table_list->table_name, + table_list->db.str, table_list->table_name.str, MDL_EXCLUSIVE, MDL_TRANSACTION); if (lock_table_names(thd, table_list, table_list->next_global, @@ -134,7 +135,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (share == NULL) DBUG_RETURN(0); // Can't open frm file - if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE)) + if (open_table_from_share(thd, share, &empty_clex_str, 0, 0, 0, + &tmp_table, FALSE)) { tdc_release_share(share); DBUG_RETURN(0); // Out of memory @@ -217,7 +219,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, "Failed renaming data file"); goto end; } - if (dd_recreate_table(thd, table_list->db, table_list->table_name)) + if (dd_recreate_table(thd, table_list->db.str, table_list->table_name.str)) { error= send_check_errmsg(thd, table_list, "repair", "Failed generating table from .frm file"); @@ -447,10 +449,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, int compl_result_code; bool need_repair_or_alter= 0; wait_for_commit* suspended_wfc; - DBUG_ENTER("mysql_admin_table"); DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options)); + thd->prepare_logs_for_admin_command(); + field_list.push_back(item= new (thd->mem_root) Item_empty_string(thd, "Table", NAME_CHAR_LEN * 2), thd->mem_root); @@ -490,13 +493,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, for (table= tables; table; table= table->next_local) { char table_name[SAFE_NAME_LEN*2+2]; - const char *db= table->db; + const char *db= table->db.str; bool fatal_error=0; bool open_error; bool collect_eis= FALSE; - DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name)); - strxmov(table_name, db, ".", table->table_name, NullS); + DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str)); + strxmov(table_name, db, ".", table->table_name.str, NullS); thd->open_options|= extra_open_options; table->lock_type= lock_type; /* @@ -545,7 +548,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, close_thread_tables(thd); table->table= NULL; thd->mdl_context.release_transactional_locks(); - table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str, MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION); } @@ -807,7 +810,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Here we close and reopen table in read mode because operation of collecting statistics is long and it will be better do not block the table completely. - InnoDB/XtraDB will allow read/write and MyISAM read/insert. + InnoDB will allow read/write and MyISAM read/insert. */ trans_commit_stmt(thd); trans_commit(thd); @@ -815,7 +818,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, close_thread_tables(thd); table->table= NULL; thd->mdl_context.release_transactional_locks(); - table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str, MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION); table->mdl_request.set_type(MDL_SHARED_READ); @@ -1038,7 +1041,7 @@ send_result_message: if (!thd->open_temporary_tables(table) && (table->table= open_ltable(thd, table, lock_type, 0))) { - uint save_flags; + ulonglong save_flags; /* Store the original value of alter_info->flags */ save_flags= alter_info->flags; @@ -1115,11 +1118,11 @@ send_result_message: if (what_to_upgrade) length= my_snprintf(buf, sizeof(buf), ER_THD(thd, ER_TABLE_NEEDS_UPGRADE), - what_to_upgrade, table->table_name); + what_to_upgrade, table->table_name.str); else length= my_snprintf(buf, sizeof(buf), ER_THD(thd, ER_TABLE_NEEDS_REBUILD), - table->table_name); + table->table_name.str); protocol->store(buf, length, system_charset_info); fatal_error=1; break; @@ -1151,7 +1154,7 @@ send_result_message: else if (open_for_modify || fatal_error) { tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table->db, table->table_name, FALSE); + table->db.str, table->table_name.str, FALSE); /* May be something modified. Consequently, we have to invalidate the query cache. @@ -1301,7 +1304,6 @@ bool Sql_cmd_analyze_table::execute(THD *thd) FALSE, UINT_MAX, FALSE)) goto error; WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "analyze", lock_type, 1, 0, 0, 0, &handler::ha_analyze, 0); @@ -1332,7 +1334,6 @@ bool Sql_cmd_check_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL, first_table, TRUE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check", lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0, @@ -1357,7 +1358,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd) FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; + res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table, true) : mysql_admin_table(thd, first_table, &m_lex->check_opt, @@ -1389,7 +1390,8 @@ bool Sql_cmd_repair_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->enable_slow_log&= !MY_TEST(thd->variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_ADMIN); WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, diff --git a/sql/sql_alloc.h b/sql/sql_alloc.h new file mode 100644 index 00000000000..e7cda5b2abf --- /dev/null +++ b/sql/sql_alloc.h @@ -0,0 +1,55 @@ +#ifndef SQL_ALLOC_INCLUDED +#define SQL_ALLOC_INCLUDED +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2017, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include <my_sys.h> /* alloc_root, MEM_ROOT, TRASH */ + +THD *thd_get_current_thd(); + +/* mysql standard class memory allocator */ + +class Sql_alloc +{ +public: + static void *operator new(size_t size) throw () + { + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), uint(size)); + } + static void *operator new[](size_t size) throw () + { + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), uint(size)); + } + static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void *operator new(size_t size, MEM_ROOT *mem_root) throw() + { return alloc_root(mem_root, size); } + static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); } + static void operator delete(void *, MEM_ROOT *){} + static void operator delete[](void *ptr, MEM_ROOT *mem_root) + { /* never called */ } + static void operator delete[](void *ptr, size_t size) { TRASH_FREE(ptr, size); } +#ifdef HAVE_valgrind + bool dummy_for_valgrind; + inline Sql_alloc() :dummy_for_valgrind(0) {} +#else + inline Sql_alloc() {} +#endif + inline ~Sql_alloc() {} +}; +#endif /* SQL_ALLOC_INCLUDED */ diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 0194f26d217..2cc6504d2f3 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" // check_access #include "sql_table.h" // mysql_alter_table, // mysql_exchange_partition @@ -85,11 +86,11 @@ bool Alter_info::set_requested_lock(const LEX_CSTRING *str) Alter_table_ctx::Alter_table_ctx() : datetime_field(NULL), error_if_not_empty(false), tables_opened(0), - db(NULL), table_name(NULL), alias(NULL), - new_db(NULL), new_name(NULL), new_alias(NULL), + db(null_clex_str), table_name(null_clex_str), alias(null_clex_str), + new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL) -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS , tmp_table(false) #endif { @@ -102,14 +103,14 @@ Alter_table_ctx::Alter_table_ctx() Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg, - const char *new_db_arg, - const char *new_name_arg) + const LEX_CSTRING *new_db_arg, + const LEX_CSTRING *new_name_arg) : datetime_field(NULL), error_if_not_empty(false), tables_opened(tables_opened_arg), - new_db(new_db_arg), new_name(new_name_arg), + new_db(*new_db_arg), new_name(*new_name_arg), fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_table(NULL) -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS , tmp_table(false) #endif { @@ -122,29 +123,31 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, table_name= table_list->table_name; alias= (lower_case_table_names == 2) ? table_list->alias : table_name; - if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) + if (!new_db.str || !my_strcasecmp(table_alias_charset, new_db.str, db.str)) new_db= db; - if (new_name) + if (new_name.str) { - DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name)); + DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db.str, new_name.str)); - if (lower_case_table_names == 1) // Convert new_name/new_alias to lower case + if (lower_case_table_names == 1) // Convert new_name/new_alias to lower { - my_casedn_str(files_charset_info, (char*) new_name); + new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str); new_alias= new_name; } else if (lower_case_table_names == 2) // Convert new_name to lower case { - new_alias= new_alias_buff; - strmov(new_alias_buff, new_name); - my_casedn_str(files_charset_info, (char*) new_name); + new_alias.str= new_alias_buff; + new_alias.length= new_name.length; + strmov(new_alias_buff, new_name.str); + new_name.length= my_casedn_str(files_charset_info, (char*) new_name.str); + } else new_alias= new_name; // LCTN=0 => case sensitive + case preserving if (!is_database_changed() && - !my_strcasecmp(table_alias_charset, new_name, table_name)) + !my_strcasecmp(table_alias_charset, new_name.str, table_name.str)) { /* Source and destination table names are equal: @@ -160,22 +163,23 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, new_name= table_name; } - my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%llx", tmp_file_prefix, - current_pid, thd->thread_id); + tmp_name.str= tmp_name_buff; + tmp_name.length= my_snprintf(tmp_name_buff, sizeof(tmp_name_buff), "%s-%lx_%llx", + tmp_file_prefix, current_pid, thd->thread_id); /* Safety fix for InnoDB */ if (lower_case_table_names) - my_casedn_str(files_charset_info, tmp_name); + tmp_name.length= my_casedn_str(files_charset_info, tmp_name_buff); if (table_list->table->s->tmp_table == NO_TMP_TABLE) { - build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, db.str, table_name.str, "", 0); - build_table_filename(new_path, sizeof(new_path) - 1, new_db, new_name, "", 0); + build_table_filename(new_path, sizeof(new_path) - 1, new_db.str, new_name.str, "", 0); build_table_filename(new_filename, sizeof(new_filename) - 1, - new_db, new_name, reg_ext, 0); + new_db.str, new_name.str, reg_ext, 0); - build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db, tmp_name, "", + build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db.str, tmp_name.str, "", FN_IS_TMP); } else @@ -186,7 +190,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, this case. This fact is enforced with assert. */ build_tmptable_filename(thd, tmp_path, sizeof(tmp_path)); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_table= true; #endif } @@ -226,14 +230,14 @@ bool Sql_cmd_alter_table::execute(THD *thd) priv_needed|= DROP_ACL; /* Must be set in the parser */ - DBUG_ASSERT(select_lex->db); + DBUG_ASSERT(select_lex->db.str); DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION)); DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION)); - if (check_access(thd, priv_needed, first_table->db, + if (check_access(thd, priv_needed, first_table->db.str, &first_table->grant.privilege, &first_table->grant.m_internal, 0, 0) || - check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db, + check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db.str, &priv, NULL, /* Don't use first_tab->grant with sel_lex->db */ 0, 0)) @@ -290,7 +294,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) // Rename of table TABLE_LIST tmp_table; memset(&tmp_table, 0, sizeof(tmp_table)); - tmp_table.table_name= lex->name.str; + tmp_table.table_name= lex->name; tmp_table.db= select_lex->db; tmp_table.grant.privilege= priv; if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE, @@ -309,19 +313,19 @@ bool Sql_cmd_alter_table::execute(THD *thd) "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); #ifdef WITH_WSREP if ((!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table))) { - WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL), + WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db.str : NULL), ((lex->name.str) ? lex->name.str : NULL), first_table); } #endif /* WITH_WSREP */ - result= mysql_alter_table(thd, select_lex->db, lex->name.str, + result= mysql_alter_table(thd, &select_lex->db, &lex->name, &create_info, first_table, &alter_info, @@ -345,7 +349,7 @@ bool Sql_cmd_discard_import_tablespace::execute(THD *thd) /* first table of first SELECT_LEX */ TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; - if (check_access(thd, ALTER_ACL, table_list->db, + if (check_access(thd, ALTER_ACL, table_list->db.str, &table_list->grant.privilege, &table_list->grant.m_internal, 0, 0)) @@ -354,7 +358,7 @@ bool Sql_cmd_discard_import_tablespace::execute(THD *thd) if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false)) return true; - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); /* Check if we attempt to alter mysql.slow_log or diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 168b4772dc1..c7cc98aacc4 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -1,5 +1,5 @@ /* Copyright (c) 2010, 2014, Oracle and/or its affiliates. - Copyright (c) 2013, 2014, Monty Program Ab. + Copyright (c) 2013, 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 @@ -38,69 +38,90 @@ public: type of index to be added/dropped. */ - enum operations_used_flags - { - // Set for ADD [COLUMN] - ALTER_ADD_COLUMN = 1L << 0, - // Set for DROP [COLUMN] - ALTER_DROP_COLUMN = 1L << 1, - // Set for CHANGE [COLUMN] | MODIFY [CHANGE] & mysql_recreate_table - ALTER_CHANGE_COLUMN = 1L << 2, - // Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY | - // ADD UNIQUE INDEX | ALTER ADD [COLUMN] - ALTER_ADD_INDEX = 1L << 3, - // Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX - ALTER_DROP_INDEX = 1L << 4, - // Set for RENAME [TO] - ALTER_RENAME = 1L << 5, - // Set for ORDER BY - ALTER_ORDER = 1L << 6, - // Set for table_options - ALTER_OPTIONS = 1L << 7, - // Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT - ALTER_CHANGE_COLUMN_DEFAULT = 1L << 8, - // Set for DISABLE KEYS | ENABLE KEYS - ALTER_KEYS_ONOFF = 1L << 9, - // Set for FORCE, ENGINE(same engine), by mysql_recreate_table() - ALTER_RECREATE = 1L << 10, - // Set for ADD PARTITION - ALTER_ADD_PARTITION = 1L << 11, - // Set for DROP PARTITION - ALTER_DROP_PARTITION = 1L << 12, - // Set for COALESCE PARTITION - ALTER_COALESCE_PARTITION = 1L << 13, - // Set for REORGANIZE PARTITION ... INTO - ALTER_REORGANIZE_PARTITION = 1L << 14, - // Set for partition_options - ALTER_PARTITION = 1L << 15, - // Set for LOAD INDEX INTO CACHE ... PARTITION - // Set for CACHE INDEX ... PARTITION - ALTER_ADMIN_PARTITION = 1L << 16, - // Set for REORGANIZE PARTITION - ALTER_TABLE_REORG = 1L << 17, - // Set for REBUILD PARTITION - ALTER_REBUILD_PARTITION = 1L << 18, - // Set for partitioning operations specifying ALL keyword - ALTER_ALL_PARTITION = 1L << 19, - // Set for REMOVE PARTITIONING - ALTER_REMOVE_PARTITIONING = 1L << 20, - // Set for ADD FOREIGN KEY - ADD_FOREIGN_KEY = 1L << 21, - // Set for DROP FOREIGN KEY - DROP_FOREIGN_KEY = 1L << 22, - // Set for EXCHANGE PARITION - ALTER_EXCHANGE_PARTITION = 1L << 23, - // Set by Sql_cmd_alter_table_truncate_partition::execute() - ALTER_TRUNCATE_PARTITION = 1L << 24, - // Set for ADD [COLUMN] FIRST | AFTER - ALTER_COLUMN_ORDER = 1L << 25, - ALTER_ADD_CHECK_CONSTRAINT = 1L << 27, - ALTER_DROP_CHECK_CONSTRAINT = 1L << 28, - ALTER_RENAME_COLUMN = 1L << 29 - }; + /** Set for ADD [COLUMN] */ + static const ulonglong ALTER_ADD_COLUMN = 1ULL << 0; + /** Set for DROP [COLUMN] */ + static const ulonglong ALTER_DROP_COLUMN = 1ULL << 1; + /** Set for CHANGE [COLUMN] | MODIFY [CHANGE] & mysql_recreate_table */ + static const ulonglong ALTER_CHANGE_COLUMN = 1ULL << 2; + /** Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY | + ADD UNIQUE INDEX | ALTER ADD [COLUMN] */ + static const ulonglong ALTER_ADD_INDEX = 1ULL << 3; + /** Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX */ + static const ulonglong ALTER_DROP_INDEX = 1ULL << 4; + /** Set for RENAME [TO] */ + static const ulonglong ALTER_RENAME = 1ULL << 5; + /** Set for ORDER BY */ + static const ulonglong ALTER_ORDER = 1ULL << 6; + /** Set for table_options */ + static const ulonglong ALTER_OPTIONS = 1ULL << 7; + /** Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT */ + static const ulonglong ALTER_CHANGE_COLUMN_DEFAULT = 1ULL << 8; + /** Set for DISABLE KEYS | ENABLE KEYS */ + static const ulonglong ALTER_KEYS_ONOFF = 1ULL << 9; + /** Set for FORCE, ENGINE(same engine), by mysql_recreate_table() */ + static const ulonglong ALTER_RECREATE = 1ULL << 10; + /** Set for ADD PARTITION */ + static const ulonglong ALTER_ADD_PARTITION = 1ULL << 11; + /** Set for DROP PARTITION */ + static const ulonglong ALTER_DROP_PARTITION = 1ULL << 12; + /** Set for COALESCE PARTITION */ + static const ulonglong ALTER_COALESCE_PARTITION = 1ULL << 13; + /** Set for REORGANIZE PARTITION ... INTO */ + static const ulonglong ALTER_REORGANIZE_PARTITION = 1ULL << 14; + /** Set for partition_options */ + static const ulonglong ALTER_PARTITION = 1ULL << 15; + /** Set for LOAD INDEX INTO CACHE ... PARTITION + and CACHE INDEX ... PARTITION */ + static const ulonglong ALTER_ADMIN_PARTITION = 1ULL << 16; + /** Set for REORGANIZE PARTITION */ + static const ulonglong ALTER_TABLE_REORG = 1ULL << 17; + /** Set for REBUILD PARTITION */ + static const ulonglong ALTER_REBUILD_PARTITION = 1ULL << 18; + /** Set for partitioning operations specifying ALL keyword */ + static const ulonglong ALTER_ALL_PARTITION = 1ULL << 19; + /** Set for REMOVE PARTITIONING */ + static const ulonglong ALTER_REMOVE_PARTITIONING = 1ULL << 20; + /** Set for ADD FOREIGN KEY */ + static const ulonglong ADD_FOREIGN_KEY = 1ULL << 21; + /** Set for DROP FOREIGN KEY */ + static const ulonglong DROP_FOREIGN_KEY = 1ULL << 22; + /** Set for EXCHANGE PARITION */ + static const ulonglong ALTER_EXCHANGE_PARTITION = 1ULL << 23; + /** Set by Sql_cmd_alter_table_truncate_partition::execute() */ + static const ulonglong ALTER_TRUNCATE_PARTITION = 1ULL << 24; + /** Set for ADD [COLUMN] FIRST | AFTER */ + static const ulonglong ALTER_COLUMN_ORDER = 1ULL << 25; + static const ulonglong ALTER_ADD_CHECK_CONSTRAINT = 1ULL << 27; + static const ulonglong ALTER_DROP_CHECK_CONSTRAINT = 1ULL << 28; + static const ulonglong ALTER_RENAME_COLUMN = 1ULL << 29; + static const ulonglong ALTER_COLUMN_UNVERSIONED = 1ULL << 30; + static const ulonglong ALTER_ADD_SYSTEM_VERSIONING = 1ULL << 31; + static const ulonglong ALTER_DROP_SYSTEM_VERSIONING= 1ULL << 32; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; + bool data_modifying() const + { + return flags & ( + ALTER_ADD_COLUMN | + ALTER_DROP_COLUMN | + ALTER_CHANGE_COLUMN | + ALTER_COLUMN_ORDER); + } + + bool partition_modifying() const + { + return flags & ( + ALTER_DROP_PARTITION | + ALTER_COALESCE_PARTITION | + ALTER_REORGANIZE_PARTITION | + ALTER_REMOVE_PARTITIONING | + ALTER_TABLE_REORG | + ALTER_EXCHANGE_PARTITION | + ALTER_TRUNCATE_PARTITION); + } + /** The different values of the ALGORITHM clause. Describes which algorithm to use when altering the table. @@ -153,7 +174,7 @@ public: }; List<Virtual_column_info> check_constraint_list; // Type of ALTER TABLE operation. - uint flags; + ulonglong flags; // Enable or disable keys. enum_enable_or_disable keys_onoff; // List of partitions. @@ -245,19 +266,19 @@ public: Alter_table_ctx(); Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg, - const char *new_db_arg, const char *new_name_arg); + const LEX_CSTRING *new_db_arg, const LEX_CSTRING *new_name_arg); /** @return true if the table is moved to another database, false otherwise. */ bool is_database_changed() const - { return (new_db != db); }; + { return (new_db.str != db.str); }; /** @return true if the table is renamed, false otherwise. */ bool is_table_renamed() const - { return (is_database_changed() || new_name != table_name); }; + { return (is_database_changed() || new_name.str != table_name.str); }; /** @return filename (including .frm) for the new table. @@ -307,13 +328,14 @@ public: Create_field *datetime_field; bool error_if_not_empty; uint tables_opened; - const char *db; - const char *table_name; - const char *alias; - const char *new_db; - const char *new_name; - const char *new_alias; - char tmp_name[80]; + LEX_CSTRING db; + LEX_CSTRING table_name; + LEX_CSTRING alias; + LEX_CSTRING new_db; + LEX_CSTRING new_name; + LEX_CSTRING new_alias; + LEX_CSTRING tmp_name; + char tmp_buff[80]; /** Indicates that if a row is deleted during copying of data from old version of table to the new version ER_FK_CANNOT_DELETE_PARENT error should be @@ -327,12 +349,13 @@ public: private: char new_filename[FN_REFLEN + 1]; - char new_alias_buff[FN_REFLEN + 1]; + char new_alias_buff[NAME_LEN + 1]; + char tmp_name_buff[NAME_LEN + 1]; char path[FN_REFLEN + 1]; char new_path[FN_REFLEN + 1]; char tmp_path[FN_REFLEN + 1]; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS /** Indicates that we are altering temporary table. Used only in asserts. */ bool tmp_table; #endif diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index d6af0410ad2..351ddb452d8 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -29,7 +29,7 @@ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "procedure.h" #include "sql_analyse.h" @@ -298,9 +298,10 @@ bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num) } // get_ev_num_info -void free_string(String *s) +int free_string(String *s) { s->free(); + return 0; } @@ -374,7 +375,7 @@ void field_str::add() if (!tree_insert(&tree, (void*) &s, 0, tree.custom_arg)) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } else { @@ -382,7 +383,7 @@ void field_str::add() if ((treemem += length) > pc->max_treemem) { room_in_tree = 0; // Remove tree, too big tree - delete_tree(&tree); + delete_tree(&tree, 0); } } } @@ -441,7 +442,7 @@ void field_real::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -450,7 +451,7 @@ void field_real::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -507,7 +508,7 @@ void field_decimal::add() if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -516,7 +517,7 @@ void field_decimal::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -574,7 +575,7 @@ void field_longlong::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -583,7 +584,7 @@ void field_longlong::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } @@ -630,7 +631,7 @@ void field_ulonglong::add() if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) { room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); + delete_tree(&tree, 0); } /* if element->count == 1, this element can be found only once from tree @@ -639,7 +640,7 @@ void field_ulonglong::add() else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) { room_in_tree = 0; // Remove tree, too many elements - delete_tree(&tree); + delete_tree(&tree, 0); } } diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 820877f2a69..6704de4ed6d 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -68,7 +68,7 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)), int compare_decimal2(int* len, const char *s, const char *t); Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list); -void free_string(String*); +int free_string(String*); class analyse; class field_info :public Sql_alloc @@ -86,7 +86,7 @@ public: nulls(0), min_length(0), max_length(0), room_in_tree(1), found(0),item(a), pc(b) {}; - virtual ~field_info() { delete_tree(&tree); } + virtual ~field_info() { delete_tree(&tree, 0); } virtual void add() = 0; virtual void get_opt_type(String*, ha_rows) = 0; virtual String *get_min_arg(String *) = 0; diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc index ac3797aae60..2c09772bd66 100644 --- a/sql/sql_analyze_stmt.cc +++ b/sql/sql_analyze_stmt.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "my_json_writer.h" diff --git a/sql/sql_array.h b/sql/sql_array.h index bbaa653b177..cad7b0e1c48 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -191,9 +191,10 @@ public: return *((Elem*)pop_dynamic(&array)); } - void del(uint idx) + void del(size_t idx) { - delete_dynamic_element(&array, idx); + DBUG_ASSERT(idx <= array.max_element); + delete_dynamic_element(&array, (uint)idx); } size_t elements() const @@ -204,7 +205,7 @@ public: void elements(size_t num_elements) { DBUG_ASSERT(num_elements <= array.max_element); - array.elements= num_elements; + array.elements= (uint)num_elements; } void clear() @@ -220,12 +221,12 @@ public: bool resize(size_t new_size, Elem default_val) { size_t old_size= elements(); - if (allocate_dynamic(&array, new_size)) + if (allocate_dynamic(&array, (uint)new_size)) return true; if (new_size > old_size) { - set_dynamic(&array, (uchar*)&default_val, new_size - 1); + set_dynamic(&array, (uchar*)&default_val, (uint)(new_size - 1)); /*for (size_t i= old_size; i != new_size; i++) { at(i)= default_val; @@ -258,4 +259,6 @@ public: } }; +typedef Bounds_checked_array<Item*> Ref_ptr_array; + #endif /* SQL_ARRAY_INCLUDED */ diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index 8134adca13f..793e3a801b3 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -13,8 +13,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" +#include "mysqld.h" #include "sql_audit.h" extern int initialize_audit_plugin(st_plugin_int *plugin); diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 96f9d0caece..5c66138b466 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -18,8 +18,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> - #include <mysql/plugin_audit.h> #include "sql_class.h" @@ -123,15 +121,13 @@ void mysql_audit_general_log(THD *thd, time_t time, event.general_thread_id= (unsigned long)thd->thread_id; event.general_charset= thd->variables.character_set_client; event.database= thd->db; - event.database_length= (unsigned int)thd->db_length; event.query_id= thd->query_id; } else { event.general_thread_id= 0; event.general_charset= global_system_variables.character_set_client; - event.database= ""; - event.database_length= 0; + event.database= null_clex_str; event.query_id= 0; } @@ -155,6 +151,7 @@ static inline void mysql_audit_general(THD *thd, uint event_subtype, int error_code, const char *msg) { + DBUG_ENTER("mysql_audit_general"); if (mysql_audit_general_enabled()) { char user_buff[MAX_USER_HOST_SIZE]; @@ -176,7 +173,6 @@ void mysql_audit_general(THD *thd, uint event_subtype, event.general_charset= thd->query_string.charset(); event.general_rows= thd->get_stmt_da()->current_row_for_warning(); event.database= thd->db; - event.database_length= (uint)thd->db_length; event.query_id= thd->query_id; } else @@ -188,13 +184,13 @@ void mysql_audit_general(THD *thd, uint event_subtype, event.general_query_length= 0; event.general_charset= &my_charset_bin; event.general_rows= 0; - event.database= ""; - event.database_length= 0; + event.database= null_clex_str; event.query_id= 0; } mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, &event); } + DBUG_VOID_RETURN; } static inline @@ -222,7 +218,6 @@ void mysql_audit_notify_connection_connect(THD *thd) event.ip= sctx->ip; event.ip_length= safe_strlen_uint(sctx->ip); event.database= thd->db; - event.database_length= safe_strlen_uint(thd->db); mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event); } @@ -252,7 +247,6 @@ void mysql_audit_notify_connection_disconnect(THD *thd, int errcode) event.ip= sctx->ip; event.ip_length= safe_strlen_uint(sctx->ip) ; event.database= thd->db; - event.database_length= safe_strlen_uint(thd->db); mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event); } @@ -283,7 +277,6 @@ void mysql_audit_notify_connection_change_user(THD *thd) event.ip= sctx->ip; event.ip_length= safe_strlen_uint(sctx->ip); event.database= thd->db; - event.database_length= safe_strlen_uint(thd->db); mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event); } @@ -307,14 +300,10 @@ void mysql_audit_external_lock(THD *thd, TABLE_SHARE *share, int lock) event.proxy_user= sctx->proxy_user; event.host= sctx->host; event.ip= sctx->ip; - event.database= share->db.str; - event.database_length= (unsigned int)share->db.length; - event.table= share->table_name.str; - event.table_length= (unsigned int)share->table_name.length; - event.new_database= 0; - event.new_database_length= 0; - event.new_table= 0; - event.new_table_length= 0; + event.database= share->db; + event.table= share->table_name; + event.new_database= null_clex_str; + event.new_table= null_clex_str; event.query_id= thd->query_id; mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event); @@ -341,15 +330,11 @@ void mysql_audit_create_table(TABLE *table) event.proxy_user= sctx->proxy_user; event.host= sctx->host; event.ip= sctx->ip; - event.database= share->db.str; - event.database_length= (unsigned int)share->db.length; - event.table= share->table_name.str; - event.table_length= (unsigned int)share->table_name.length; - event.new_database= 0; - event.new_database_length= 0; - event.new_table= 0; - event.new_table_length= 0; - event.query_id= thd->query_id; + event.database= share->db; + event.table= share->table_name; + event.new_database= null_clex_str; + event.new_table= null_clex_str; + event.query_id= thd->query_id; mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event); } @@ -373,23 +358,20 @@ void mysql_audit_drop_table(THD *thd, TABLE_LIST *table) event.proxy_user= sctx->proxy_user; event.host= sctx->host; event.ip= sctx->ip; - event.database= table->db; - event.database_length= (unsigned int)table->db_length; - event.table= table->table_name; - event.table_length= (unsigned int)table->table_name_length; - event.new_database= 0; - event.new_database_length= 0; - event.new_table= 0; - event.new_table_length= 0; - event.query_id= thd->query_id; + event.database= table->db; + event.table= table->table_name; + event.new_database= null_clex_str; + event.new_table= null_clex_str; + event.query_id= thd->query_id; mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event); } } static inline -void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb, - const char *new_db, const char *new_tb) +void mysql_audit_rename_table(THD *thd, const LEX_CSTRING *old_db, + const LEX_CSTRING *old_tb, + const LEX_CSTRING *new_db, const LEX_CSTRING *new_tb) { if (mysql_audit_table_enabled()) { @@ -406,14 +388,10 @@ void mysql_audit_rename_table(THD *thd, const char *old_db, const char *old_tb, event.proxy_user= sctx->proxy_user; event.host= sctx->host; event.ip= sctx->ip; - event.database= old_db; - event.database_length= strlen_uint(old_db); - event.table= old_tb; - event.table_length= strlen_uint(old_tb); - event.new_database= new_db; - event.new_database_length= strlen_uint(new_db); - event.new_table= new_tb; - event.new_table_length= strlen_uint(new_tb); + event.database= *old_db; + event.table= *old_tb; + event.new_database= *new_db; + event.new_table= *new_tb; event.query_id= thd->query_id; mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event); @@ -439,13 +417,9 @@ void mysql_audit_alter_table(THD *thd, TABLE_LIST *table) event.host= sctx->host; event.ip= sctx->ip; event.database= table->db; - event.database_length= (unsigned int)table->db_length; event.table= table->table_name; - event.table_length= (unsigned int)table->table_name_length; - event.new_database= 0; - event.new_database_length= 0; - event.new_table= 0; - event.new_table_length= 0; + event.new_database= null_clex_str; + event.new_table= null_clex_str; event.query_id= thd->query_id; mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, &event); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 87a026ee743..3e1d5ffd63a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -17,7 +17,7 @@ /* Basic functions needed by many modules */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" // setup_table_map #include "sql_priv.h" #include "unireg.h" @@ -244,8 +244,9 @@ struct list_open_tables_arg static my_bool list_open_tables_callback(TDC_element *element, list_open_tables_arg *arg) { - char *db= (char*) element->m_key; - char *table_name= (char*) element->m_key + strlen((char*) element->m_key) + 1; + const char *db= (char*) element->m_key; + size_t db_length= strlen(db); + const char *table_name= db + db_length + 1; if (arg->db && my_strcasecmp(system_charset_info, arg->db, db)) return FALSE; @@ -253,8 +254,10 @@ static my_bool list_open_tables_callback(TDC_element *element, return FALSE; /* Check if user has SELECT privilege for any column in the table */ - arg->table_list.db= db; - arg->table_list.table_name= table_name; + arg->table_list.db.str= db; + arg->table_list.db.length= db_length; + arg->table_list.table_name.str= table_name; + arg->table_list.table_name.length= strlen(table_name); arg->table_list.grant.privilege= 0; if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE)) @@ -381,8 +384,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, for (TABLE_LIST *table= tables; table; table= table->next_local) { /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */ - found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db, - table->table_name, TRUE); + found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db.str, + table->table_name.str, TRUE); } if (!found) wait_for_refresh=0; // Nothing to wait for @@ -412,8 +415,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, table_list= table_list->next_global) { /* 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, - table_list->table_name, TRUE); + TABLE *table= find_table_for_mdl_upgrade(thd, table_list->db.str, + table_list->table_name.str, TRUE); /* May return NULL if this table has already been closed via an alias. */ if (! table) @@ -463,7 +466,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, { if (thd->killed) break; - if (tdc_wait_for_old_version(thd, table->db, table->table_name, timeout, + if (tdc_wait_for_old_version(thd, table->db.str, table->table_name.str, timeout, MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL, refresh_version)) { @@ -530,9 +533,9 @@ static my_bool close_cached_connection_tables_callback( /* close_cached_tables() only uses these elements */ if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) || - !(tmp->db= strdup_root(arg->thd->mem_root, element->share->db.str)) || - !(tmp->table_name= strdup_root(arg->thd->mem_root, - element->share->table_name.str))) + !(arg->thd->make_lex_string(&tmp->db, element->share->db.str, element->share->db.length)) || + !(arg->thd->make_lex_string(&tmp->table_name, element->share->table_name.str, + element->share->table_name.length))) { mysql_mutex_unlock(&element->LOCK_table_share); return TRUE; @@ -641,7 +644,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!share->tmp_table); char key[MAX_DBKEY_LENGTH]; - uint key_length= share->table_cache_key.length; + size_t key_length= share->table_cache_key.length; const char *db= key; const char *table_name= db + share->db.length + 1; @@ -878,6 +881,12 @@ void close_thread_table(THD *thd, TABLE **table_ptr) table->file->update_global_index_stats(); } + /* + This look is needed to allow THD::notify_shared_lock() to + traverse the thd->open_tables list without having to worry that + some of the tables are removed from under it + */ + mysql_mutex_lock(&thd->LOCK_thd_data); *table_ptr=table->next; mysql_mutex_unlock(&thd->LOCK_thd_data); @@ -913,8 +922,7 @@ void close_thread_table(THD *thd, TABLE **table_ptr) table_name Table name NOTES: - This is called by find_table_in_local_list() and - find_table_in_global_list(). + This is called by find_table_in_global_list(). RETURN VALUES NULL Table not found @@ -923,13 +931,13 @@ void close_thread_table(THD *thd, TABLE **table_ptr) TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, - const char *db_name, - const char *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { for (; table; table= table->*link ) { - if (strcmp(table->db, db_name) == 0 && - strcmp(table->table_name, table_name) == 0) + if (cmp(&table->db, db_name) == 0 && + cmp(&table->table_name, table_name) == 0) break; } return table; @@ -974,9 +982,9 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias) { TABLE_LIST *res; - const char *d_name, *t_name, *t_alias; + LEX_CSTRING *d_name, *t_name, *t_alias; DBUG_ENTER("find_dup_table"); - DBUG_PRINT("enter", ("table alias: %s", table->alias)); + DBUG_PRINT("enter", ("table alias: %s", table->alias.str)); /* If this function called for query which update table (INSERT/UPDATE/...) @@ -1000,12 +1008,12 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ DBUG_ASSERT(table); } - d_name= table->db; - t_name= table->table_name; - t_alias= table->alias; + d_name= &table->db; + t_name= &table->table_name; + t_alias= &table->alias; retry: - DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name)); + DBUG_PRINT("info", ("real table: %s.%s", d_name->str, t_name->str)); for (TABLE_LIST *tl= table_list;;) { if (tl && @@ -1033,7 +1041,7 @@ retry: /* Skip if table alias does not match. */ if (check_alias) { - if (my_strcasecmp(table_alias_charset, t_alias, res->alias)) + if (my_strcasecmp(table_alias_charset, t_alias->str, res->alias.str)) goto next; } @@ -1096,14 +1104,32 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, table= table->find_table_for_update(); - if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (table->table && + table->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { TABLE_LIST *child; dup= NULL; /* Check duplicates of all merge children. */ - for (child= table->next_global; child && child->parent_l == table; + for (child= table->next_global; child; child= child->next_global) { + if (child->table && + child->table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) + continue; + + /* + Ensure that the child has one parent that is the table that is + updated. + */ + TABLE_LIST *tmp_parent= child; + while ((tmp_parent= tmp_parent->parent_l)) + { + if (tmp_parent == table) + break; + } + if (!tmp_parent) + break; + if ((dup= find_dup_table(thd, child, child->next_global, check_alias))) break; } @@ -1112,6 +1138,8 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, dup= find_dup_table(thd, table, table_list, check_alias); return dup; } + + /* Issue correct error message in case we found 2 duplicate tables which prevent some update operation @@ -1136,10 +1164,10 @@ void update_non_unique_table_error(TABLE_LIST *update, update->view == duplicate->view || update->view_name.length != duplicate->view_name.length || update->view_db.length != duplicate->view_db.length || - my_strcasecmp(table_alias_charset, - update->view_name.str, duplicate->view_name.str) != 0 || - my_strcasecmp(table_alias_charset, - update->view_db.str, duplicate->view_db.str) != 0) + lex_string_cmp(table_alias_charset, + &update->view_name, &duplicate->view_name) != 0 || + lex_string_cmp(table_alias_charset, + &update->view_db, &duplicate->view_db) != 0) { /* it is not the same view repeated (but it can be parts of the same copy @@ -1151,21 +1179,21 @@ void update_non_unique_table_error(TABLE_LIST *update, if (update->view == duplicate->view) my_error(!strncmp(operation, "INSERT", 6) ? ER_NON_INSERTABLE_TABLE : ER_NON_UPDATABLE_TABLE, MYF(0), - update->alias, operation); + update->alias.str, operation); else my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), - (duplicate->view ? duplicate->alias : update->alias), - operation, update->alias); + (duplicate->view ? duplicate->alias.str : update->alias.str), + operation, update->alias.str); return; } if (duplicate->view) { - my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias, operation, - update->alias); + my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias.str, operation, + update->alias.str); return; } } - my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias, operation); + my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias.str, operation); } @@ -1230,8 +1258,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, prelocked mode, e.g. if we do CREATE TABLE .. SELECT f1(); */ -void drop_open_table(THD *thd, TABLE *table, const char *db_name, - const char *table_name) +void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { DBUG_ENTER("drop_open_table"); if (table->s->tmp_table) @@ -1244,7 +1272,7 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, table->file->extra(HA_EXTRA_PREPARE_FOR_DROP); close_thread_table(thd, &thd->open_tables); /* Remove the table share from the table cache. */ - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name, + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name->str, table_name->str, FALSE); /* Remove the table from the storage engine and rm the .frm. */ quick_rm_table(thd, table_type, db_name, table_name, 0); @@ -1481,11 +1509,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) TABLE *table; const char *key; uint key_length; - const char *alias= table_list->alias; + const char *alias= table_list->alias.str; uint flags= ot_ctx->get_flags(); MDL_ticket *mdl_ticket; TABLE_SHARE *share; uint gts_flags; + int part_names_error=0; DBUG_ENTER("open_table"); /* @@ -1583,6 +1612,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) table= best_table; table->query_id= thd->query_id; DBUG_PRINT("info",("Using locked table")); + if (table->part_info) + { + /* Set all [named] partitions as used. */ + part_names_error= + table->file->change_partitions_to_open(table_list->partition_names); + } goto reset; } /* @@ -1595,13 +1630,13 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) to deadlocks) it should be disallowed. */ if (thd->mdl_context.is_lock_owner(MDL_key::TABLE, - table_list->db, - table_list->table_name, + 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, table_list->table_name, reg_ext, 0); + 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 @@ -1636,7 +1671,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) locked tables list was created. */ if (thd->locked_tables_mode == LTM_PRELOCKED) - my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str, table_list->alias.str); else my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); DBUG_RETURN(TRUE); @@ -1722,7 +1757,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS) { - if (!ha_table_exists(thd, table_list->db, table_list->table_name)) + if (!ha_table_exists(thd, &table_list->db, &table_list->table_name)) DBUG_RETURN(FALSE); } else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB) @@ -1785,7 +1820,8 @@ retry_share: } if (table_list->sequence) { - my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, + table_list->alias.str); goto err_lock; } /* @@ -1832,8 +1868,8 @@ retry_share: bool wait_result; thd->push_internal_handler(&mdl_deadlock_handler); - wait_result= tdc_wait_for_old_version(thd, table_list->db, - table_list->table_name, + wait_result= tdc_wait_for_old_version(thd, table_list->db.str, + table_list->table_name.str, ot_ctx->get_timeout(), mdl_ticket->get_deadlock_weight()); thd->pop_internal_handler(); @@ -1866,6 +1902,12 @@ retry_share: { DBUG_ASSERT(table->file != NULL); MYSQL_REBIND_TABLE(table->file); + if (table->part_info) + { + /* Set all [named] partitions as used. */ + part_names_error= + table->file->change_partitions_to_open(table_list->partition_names); + } } else { @@ -1875,10 +1917,11 @@ retry_share: if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME)))) goto err_lock; - error= open_table_from_share(thd, share, alias, + error= open_table_from_share(thd, share, &table_list->alias, HA_OPEN_KEYFILE | HA_TRY_READ_ONLY, EXTRA_RECORD, - thd->open_options, table, FALSE); + thd->open_options, table, FALSE, + table_list->partition_names); if (error) { @@ -1927,9 +1970,12 @@ retry_share: #ifdef WITH_PARTITION_STORAGE_ENGINE if (table->part_info) { - /* Set all [named] partitions as used. */ - if (table->part_info->set_partition_bitmaps(table_list)) + /* Partitions specified were incorrect.*/ + if (part_names_error) + { + table->file->print_error(part_names_error, MYF(0)); DBUG_RETURN(true); + } } else if (table_list->partition_names) { @@ -1940,7 +1986,7 @@ retry_share: #endif if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE) { - my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str); DBUG_RETURN(true); } @@ -2068,28 +2114,30 @@ Locked_tables_list::init_locked_tables(THD *thd) table= table->next, m_locked_tables_count++) { TABLE_LIST *src_table_list= table->pos_in_table_list; - char *db, *table_name, *alias; - size_t db_len= table->s->db.length; - size_t table_name_len= table->s->table_name.length; - size_t alias_len= table->alias.length(); + LEX_CSTRING db, table_name, alias; + + db.length= table->s->db.length; + table_name.length= table->s->table_name.length; + alias.length= table->alias.length(); TABLE_LIST *dst_table_list; if (! multi_alloc_root(&m_locked_tables_root, &dst_table_list, sizeof(*dst_table_list), - &db, db_len + 1, - &table_name, table_name_len + 1, - &alias, alias_len + 1, + &db.str, (size_t) db.length + 1, + &table_name.str, (size_t) table_name.length + 1, + &alias.str, (size_t) alias.length + 1, NullS)) { reset(); return TRUE; } - memcpy(db, table->s->db.str, db_len + 1); - memcpy(table_name, table->s->table_name.str, table_name_len + 1); - strmake(alias, table->alias.ptr(), alias_len); - dst_table_list->init_one_table(db, db_len, table_name, table_name_len, - alias, table->reginfo.lock_type); + memcpy((char*) db.str, table->s->db.str, db.length + 1); + memcpy((char*) table_name.str, table->s->table_name.str, + table_name.length + 1); + memcpy((char*) alias.str, table->alias.c_ptr(), alias.length + 1); + dst_table_list->init_one_table(&db, &table_name, + &alias, table->reginfo.lock_type); dst_table_list->table= table; dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket; @@ -2356,7 +2404,7 @@ bool Locked_tables_list::reopen_tables(THD *thd) { Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); - size_t reopen_count= 0; + uint reopen_count= 0; MYSQL_LOCK *lock; MYSQL_LOCK *merged_lock; DBUG_ENTER("Locked_tables_list::reopen_tables"); @@ -2424,7 +2472,7 @@ bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list, { MYSQL_LOCK *merged_lock; DBUG_ENTER("restore_lock"); - DBUG_ASSERT(!strcmp(dst_table_list->table_name, table->s->table_name.str)); + DBUG_ASSERT(!strcmp(dst_table_list->table_name.str, table->s->table_name.str)); /* Ensure we have the memory to add the table back */ if (!(merged_lock= mysql_lock_merge(thd->lock, lock))) @@ -2653,8 +2701,8 @@ ret: static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry) { - if (Table_triggers_list::check_n_load(thd, share->db.str, - share->table_name.str, entry, 0)) + if (Table_triggers_list::check_n_load(thd, &share->db, + &share->table_name, entry, 0)) return TRUE; /* @@ -2671,10 +2719,9 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry) query.length(0); query.append("DELETE FROM "); - append_identifier(thd, &query, share->db.str, share->db.length); + append_identifier(thd, &query, &share->db); query.append("."); - append_identifier(thd, &query, share->table_name.str, - share->table_name.length); + append_identifier(thd, &query, &share->table_name); /* we bypass thd->binlog_query() here, @@ -2710,7 +2757,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) DBUG_ASSERT(! share->is_view); - if (open_table_from_share(thd, share, table_list->alias, + if (open_table_from_share(thd, share, &table_list->alias, HA_OPEN_KEYFILE | HA_TRY_READ_ONLY, EXTRA_RECORD, ha_open_options | HA_OPEN_FOR_REPAIR, @@ -2735,7 +2782,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) tdc_release_share(share); /* Remove the repaired share from the table cache. */ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, - table_list->db, table_list->table_name, + table_list->db.str, table_list->table_name.str, FALSE); end_free: my_free(entry); @@ -2838,10 +2885,7 @@ request_backoff_action(enum_open_table_action action_arg, m_failed_table= (TABLE_LIST*) m_thd->alloc(sizeof(TABLE_LIST)); if (m_failed_table == NULL) return TRUE; - m_failed_table->init_one_table(table->db, table->db_length, - table->table_name, - table->table_name_length, - table->alias, TL_WRITE); + m_failed_table->init_one_table(&table->db, &table->table_name, &table->alias, TL_WRITE); m_failed_table->open_strategy= table->open_strategy; m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE); } @@ -2910,8 +2954,8 @@ Open_table_context::recover_from_failed_open() get_timeout(), 0))) break; - tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db, - m_failed_table->table_name, FALSE); + tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str, + m_failed_table->table_name.str, FALSE); m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id); m_thd->clear_error(); // Clear error message @@ -2946,8 +2990,8 @@ Open_table_context::recover_from_failed_open() get_timeout(), 0))) break; - tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db, - m_failed_table->table_name, FALSE); + tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str, + m_failed_table->table_name.str, FALSE); result= auto_repair_table(m_thd, m_failed_table); /* @@ -3267,10 +3311,8 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, obtain proper metadata locks and do other necessary steps like stored routine processing. */ - tables->db= tables->view_db.str; - tables->db_length= tables->view_db.length; - tables->table_name= tables->view_name.str; - tables->table_name_length= tables->view_name.length; + tables->db= tables->view_db; + tables->table_name= tables->view_name; } else if (tables->select_lex) { @@ -3327,7 +3369,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, goto end; } DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: %p", - tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here + tables->db.str, tables->table_name.str, tables)); (*counter)++; /* @@ -3343,7 +3385,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, DBUG_ASSERT(is_temporary_table(tables) || tables->table->s->sequence); if (tables->sequence && tables->table->s->table_type != TABLE_TYPE_SEQUENCE) { - my_error(ER_NOT_SEQUENCE, MYF(0), tables->db, tables->alias); + my_error(ER_NOT_SEQUENCE, MYF(0), tables->db.str, tables->alias.str); DBUG_RETURN(true); } } @@ -3446,7 +3488,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, if (! ot_ctx->can_recover_from_failed_open() && safe_to_ignore_table) { DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", - tables->db, tables->alias)); + tables->db.str, tables->alias.str)); error= FALSE; } goto end; @@ -3502,7 +3544,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, */ if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) + (tables->lock_type >= TL_WRITE_ALLOW_WRITE || thd->lex->default_used)) { bool need_prelocking= FALSE; TABLE_LIST **save_query_tables_last= lex->query_tables_last; @@ -3669,7 +3711,7 @@ lock_table_names(THD *thd, const DDL_options_st &options, MDL_request *schema_request= new (thd->mem_root) MDL_request; if (schema_request == NULL) DBUG_RETURN(TRUE); - schema_request->init(MDL_key::SCHEMA, table->db, "", + schema_request->init(MDL_key::SCHEMA, table->db.str, "", MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION); mdl_requests.push_front(schema_request); @@ -3720,17 +3762,17 @@ lock_table_names(THD *thd, const DDL_options_st &options, We come here in the case of lock timeout when executing CREATE TABLE. Verify that table does exist (it usually does, as we got a lock conflict) */ - if (ha_table_exists(thd, tables_start->db, tables_start->table_name)) + if (ha_table_exists(thd, &tables_start->db, &tables_start->table_name)) { if (options.if_not_exists()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER_THD(thd, ER_TABLE_EXISTS_ERROR), - tables_start->table_name); + tables_start->table_name.str); } else - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name.str); DBUG_RETURN(TRUE); } /* @@ -3804,7 +3846,7 @@ 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, table->table_name, false)) + if (!find_table_for_mdl_upgrade(thd, table->db.str, table->table_name.str, false)) return TRUE; } @@ -4089,7 +4131,7 @@ restart: continue; /* Schema tables may not have a TABLE object here. */ - if (tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (tbl->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { /* MERGE tables need to access parent and child TABLE_LISTs. */ DBUG_ASSERT(tbl->pos_in_table_list == tables); @@ -4120,7 +4162,7 @@ restart: (*start)->table && (*start)->table->file->ht == myisam_hton && wsrep_thd_exec_mode(thd) == LOCAL_STATE && - !is_stat_table((*start)->db, (*start)->alias) && + !is_stat_table(&(*start)->db, &(*start)->alias) && thd->get_command() != COM_STMT_PREPARE && ((thd->lex->sql_command == SQLCOM_INSERT || thd->lex->sql_command == SQLCOM_INSERT_SELECT || @@ -4208,15 +4250,62 @@ static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, for (; tl; tl= tl->next_global ) { if (tl->lock_type >= lock_type && - tl->prelocking_placeholder == TABLE_LIST::FK && - strcmp(tl->db, db->str) == 0 && - strcmp(tl->table_name, table->str) == 0) + tl->prelocking_placeholder == TABLE_LIST::PRELOCK_FK && + strcmp(tl->db.str, db->str) == 0 && + strcmp(tl->table_name.str, table->str) == 0) return true; } return false; } +static bool internal_table_exists(TABLE_LIST *global_list, + const char *table_name) +{ + do + { + if (global_list->table_name.str == table_name) + return 1; + } while ((global_list= global_list->next_global)); + return 0; +} + + +static bool +add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *tables) +{ + TABLE_LIST *global_table_list= prelocking_ctx->query_tables; + + do + { + /* + Skip table if already in the list. Can happen with prepared statements + */ + if (tables->next_local && + internal_table_exists(global_table_list, tables->table_name.str)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + if (!tl) + return TRUE; + tl->init_one_table_for_prelocking(&tables->db, + &tables->table_name, + NULL, tables->lock_type, + TABLE_LIST::PRELOCK_NONE, + 0, 0, + &prelocking_ctx->query_tables_last); + /* + Store link to the new table_list that will be used by open so that + Item_func_nextval() can find it + */ + tables->next_local= tl; + } while ((tables= tables->next_global)); + return FALSE; +} + + + /** Defines how prelocking algorithm for DML statements should handle table list elements: @@ -4243,21 +4332,23 @@ bool DML_prelocking_strategy:: handle_table(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking) { + TABLE *table= table_list->table; /* We rely on a caller to check that table is going to be changed. */ - DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE); + DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE || + thd->lex->default_used); if (table_list->trg_event_map) { - if (table_list->table->triggers) + if (table->triggers) { *need_prelocking= TRUE; - if (table_list->table->triggers-> + if (table->triggers-> add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) return TRUE; } - if (table_list->table->file->referenced_by_foreign_key()) + if (table->file->referenced_by_foreign_key()) { List <FOREIGN_KEY_INFO> fk_list; List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); @@ -4266,7 +4357,7 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, arena= thd->activate_stmt_arena_if_needed(&backup); - table_list->table->file->get_parent_foreign_key_list(thd, &fk_list); + table->file->get_parent_foreign_key_list(thd, &fk_list); if (thd->is_error()) { if (arena) @@ -4295,21 +4386,94 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, continue; TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); - tl->init_one_table_for_prelocking(fk->foreign_db->str, fk->foreign_db->length, - fk->foreign_table->str, fk->foreign_table->length, - NULL, lock_type, false, table_list->belong_to_view, - op, &prelocking_ctx->query_tables_last); + tl->init_one_table_for_prelocking(fk->foreign_db, + fk->foreign_table, + NULL, lock_type, + TABLE_LIST::PRELOCK_FK, + table_list->belong_to_view, op, + &prelocking_ctx->query_tables_last); } if (arena) thd->restore_active_arena(arena, &backup); } } + /* Open any tables used by DEFAULT (like sequence tables) */ + if (table->internal_tables && + ((sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA) || + thd->lex->default_used)) + { + Query_arena *arena, backup; + bool error; + arena= thd->activate_stmt_arena_if_needed(&backup); + error= add_internal_tables(thd, prelocking_ctx, + table->internal_tables); + if (arena) + thd->restore_active_arena(arena, &backup); + if (error) + { + *need_prelocking= TRUE; + return TRUE; + } + } return FALSE; } /** + Open all tables used by DEFAULT functions. + + This is different from normal open_and_lock_tables() as we may + already have other tables opened and locked and we have to merge the + new table with the old ones. +*/ + +bool open_and_lock_internal_tables(TABLE *table, bool lock_table) +{ + THD *thd= table->in_use; + TABLE_LIST *tl; + MYSQL_LOCK *save_lock,*new_lock; + DBUG_ENTER("open_internal_tables"); + + /* remove pointer to old select_lex which is already destroyed */ + for (tl= table->internal_tables ; tl ; tl= tl->next_global) + tl->select_lex= 0; + + uint counter; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST *tmp= table->internal_tables; + DML_prelocking_strategy prelocking_strategy; + + if (open_tables(thd, thd->lex->create_info, &tmp, &counter, 0, + &prelocking_strategy)) + goto err; + + if (lock_table) + { + save_lock= thd->lock; + thd->lock= 0; + if (lock_tables(thd, table->internal_tables, counter, + MYSQL_LOCK_USE_MALLOC)) + goto err; + + if (!(new_lock= mysql_lock_merge(save_lock, thd->lock))) + { + thd->lock= save_lock; + mysql_unlock_tables(thd, save_lock, 1); + /* We don't have to close tables as caller will do that */ + goto err; + } + thd->lock= new_lock; + } + DBUG_RETURN(0); + +err: + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(1); +} + + +/** Defines how prelocking algorithm for DML statements should handle view - all view routines should be added to the prelocking set. @@ -4460,7 +4624,7 @@ static bool check_lock_and_start_stmt(THD *thd, Prelocking placeholder is not set for TABLE_LIST that are directly used by TOP level statement. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); + DBUG_ASSERT(table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_NONE); /* TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only @@ -4473,7 +4637,6 @@ static bool check_lock_and_start_stmt(THD *thd, Last argument routine_modifies_data for read_lock_type_for_table() is ignored, as prelocking placeholder will never be set here. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); if (table_list->lock_type == TL_WRITE_DEFAULT) lock_type= thd->update_lock_default; else if (table_list->lock_type == TL_READ_DEFAULT) @@ -4481,8 +4644,8 @@ static bool check_lock_and_start_stmt(THD *thd, else lock_type= table_list->lock_type; - if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE && - (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE) + if ((int) lock_type >= (int) TL_WRITE_ALLOW_WRITE && + (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_WRITE) { my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->table->alias.c_ptr()); @@ -4636,7 +4799,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, */ DBUG_ASSERT(table_list->table); table= table_list->table; - if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM) + if (table->file->ha_table_flags() & HA_CAN_MULTISTEP_MERGE) { /* A MERGE table must not come here. */ /* purecov: begin tested */ @@ -5189,7 +5352,7 @@ Field *view_ref_found= (Field*) 0x2; static void update_field_dependencies(THD *thd, Field *field, TABLE *table) { DBUG_ENTER("update_field_dependencies"); - if (thd->mark_used_columns != MARK_COLUMNS_NONE) + if (should_mark_column(thd->column_usage)) { MY_BITMAP *bitmap; @@ -5203,7 +5366,7 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) if (field->vcol_info) table->mark_virtual_col(field); - if (thd->mark_used_columns == MARK_COLUMNS_READ) + if (thd->column_usage == MARK_COLUMNS_READ) bitmap= table->read_set; else bitmap= table->write_set; @@ -5211,13 +5374,13 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) /* The test-and-set mechanism in the bitmap is not reliable during multi-UPDATE statements under MARK_COLUMNS_READ mode - (thd->mark_used_columns == MARK_COLUMNS_READ), as this bitmap contains + (thd->column_usage == MARK_COLUMNS_READ), as this bitmap contains only those columns that are used in the SET clause. I.e they are being set here. See multi_update::prepare() */ if (bitmap_fast_test_and_set(bitmap, field->field_index)) { - if (thd->mark_used_columns == MARK_COLUMNS_WRITE) + if (thd->column_usage == MARK_COLUMNS_WRITE) { DBUG_PRINT("warning", ("Found duplicated field")); thd->dup_field= field; @@ -5228,11 +5391,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) } DBUG_VOID_RETURN; } - if (table->get_fields_in_item_tree) - field->flags|= GET_FIXED_FIELDS_FLAG; table->used_fields++; } - else if (table->get_fields_in_item_tree) + if (table->get_fields_in_item_tree) field->flags|= GET_FIXED_FIELDS_FLAG; DBUG_VOID_RETURN; } @@ -5261,14 +5422,14 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) static Field * find_field_in_view(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, + const char *name, size_t length, const char *item_name, Item **ref, bool register_tree_change) { DBUG_ENTER("find_field_in_view"); DBUG_PRINT("enter", ("view: '%s', field name: '%s', item name: '%s', ref %p", - table_list->alias, name, item_name, ref)); + table_list->alias.str, name, item_name, ref)); Field_iterator_view field_it; field_it.set(table_list); Query_arena *arena= 0, backup; @@ -5342,8 +5503,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, */ static Field * -find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, - uint length, Item **ref, bool register_tree_change, +find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, size_t length, Item **ref, bool register_tree_change, TABLE_LIST **actual_table) { List_iterator_fast<Natural_join_column> @@ -5463,10 +5623,10 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, */ Field * -find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, +find_field_in_table(THD *thd, TABLE *table, const char *name, size_t length, bool allow_rowid, uint *cached_field_index_ptr) { - Field **field_ptr, *field; + Field *field; uint cached_field_index= *cached_field_index_ptr; DBUG_ENTER("find_field_in_table"); DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias.c_ptr(), @@ -5476,34 +5636,23 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, if (cached_field_index < table->s->fields && !my_strcasecmp(system_charset_info, table->field[cached_field_index]->field_name.str, name)) - field_ptr= table->field + cached_field_index; - else if (table->s->name_hash.records) - { - field_ptr= (Field**) my_hash_search(&table->s->name_hash, (uchar*) name, - length); - if (field_ptr) - { - /* - field_ptr points to field in TABLE_SHARE. Convert it to the matching - field in table - */ - field_ptr= (table->field + (field_ptr - table->s->field)); - } - } + field= table->field[cached_field_index]; else { - if (!(field_ptr= table->field)) - DBUG_RETURN((Field *)0); - for (; *field_ptr; ++field_ptr) - if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name.str, - name)) - break; + LEX_CSTRING fname= {name, length}; + field= table->find_field_by_name(&fname); } - if (field_ptr && *field_ptr) + if (field) { - *cached_field_index_ptr= (uint)(field_ptr - table->field); - field= *field_ptr; + if (field->invisible == INVISIBLE_FULL && + DBUG_EVALUATE_IF("test_completely_invisible", 0, 1)) + DBUG_RETURN((Field*)0); + + if (field->invisible == INVISIBLE_SYSTEM && + thd->column_usage != MARK_COLUMNS_READ && + thd->column_usage != COLUMNS_READ) + DBUG_RETURN((Field*)0); } else { @@ -5513,6 +5662,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, DBUG_RETURN((Field*) 0); field= table->field[table->s->rowid_field_offset-1]; } + *cached_field_index_ptr= field->field_index; update_field_dependencies(thd, field, table); @@ -5532,6 +5682,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, item_name [in] name of item if it will be created (VIEW) db_name [in] optional database name that qualifies the table_name [in] optional table name that qualifies the field + 0 for non-qualified field in natural joins ref [in/out] if 'name' is resolved to a view field, ref is set to point to the found view field check_privileges [in] check privileges @@ -5566,7 +5717,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, Field * find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, + const char *name, size_t length, const char *item_name, const char *db_name, const char *table_name, Item **ref, bool check_privileges, bool allow_rowid, @@ -5575,12 +5726,12 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, { Field *fld; DBUG_ENTER("find_field_in_table_ref"); - DBUG_ASSERT(table_list->alias); + DBUG_ASSERT(table_list->alias.str); DBUG_ASSERT(name); DBUG_ASSERT(item_name); DBUG_PRINT("enter", ("table: '%s' field name: '%s' item name: '%s' ref %p", - table_list->alias, name, item_name, ref)); + table_list->alias.str, name, item_name, ref)); /* Check that the table and database that qualify the current field name @@ -5596,9 +5747,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, inside the view, but we want to search directly in the view columns which are represented as a 'field_translation'. - TODO: Ensure that table_name, db_name and tables->db always points to - something ! + tables->db.str may be 0 if we are preparing a statement + db_name is 0 if item doesn't have a db name + table_name is 0 if item doesn't have a specified table_name */ + if (db_name && !db_name[0]) + db_name= 0; // Simpler test later + if (/* Exclude nested joins. */ (!table_list->nested_join || /* Include merge views and information schema tables. */ @@ -5608,12 +5763,12 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, to search. */ table_name && table_name[0] && - (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || - (db_name && db_name[0] && (!table_list->db || !table_list->db[0])) || - (db_name && db_name[0] && table_list->db && table_list->db[0] && + (my_strcasecmp(table_alias_charset, table_list->alias.str, table_name) || + (db_name && (!table_list->db.str || !table_list->db.str[0])) || + (db_name && table_list->db.str && table_list->db.str[0] && (table_list->schema_table ? - my_strcasecmp(system_charset_info, db_name, table_list->db) : - strcmp(db_name, table_list->db))))) + my_strcasecmp(system_charset_info, db_name, table_list->db.str) : + strcmp(db_name, table_list->db.str))))) DBUG_RETURN(0); /* @@ -5684,7 +5839,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, fld= WRONG_GRANT; else #endif - if (thd->mark_used_columns != MARK_COLUMNS_NONE) + if (should_mark_column(thd->column_usage)) { /* Get rw_set correct for this field so that the handler @@ -5701,7 +5856,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, field_to_set= ((Item_field*)it)->field; else { - if (thd->mark_used_columns == MARK_COLUMNS_READ) + if (thd->column_usage == MARK_COLUMNS_READ) it->walk(&Item::register_field_in_read_map, 0, 0); else it->walk(&Item::register_field_in_write_map, 0, 0); @@ -5712,7 +5867,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, if (field_to_set) { TABLE *table= field_to_set->table; - if (thd->mark_used_columns == MARK_COLUMNS_READ) + DBUG_ASSERT(table); + if (thd->column_usage == MARK_COLUMNS_READ) bitmap_set_bit(table->read_set, field_to_set->field_index); else bitmap_set_bit(table->write_set, field_to_set->field_index); @@ -5814,7 +5970,7 @@ find_field_in_tables(THD *thd, Item_ident *item, const char *db= item->db_name; const char *table_name= item->table_name; const char *name= item->field_name.str; - uint length= item->field_name.length; + size_t length= item->field_name.length; char name_buff[SAFE_NAME_LEN+1]; TABLE_LIST *cur_table= first_table; TABLE_LIST *actual_table; @@ -6130,8 +6286,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, item is not fix_field()'ed yet. */ if (item_field->field_name.str && item_field->table_name && - !my_strcasecmp(system_charset_info, item_field->field_name.str, - field_name->str) && + !lex_string_cmp(system_charset_info, &item_field->field_name, + field_name) && !my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && (!db_name || (item_field->db_name && @@ -6160,11 +6316,11 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } else { - int fname_cmp= my_strcasecmp(system_charset_info, - item_field->field_name.str, - field_name->str); - if (!my_strcasecmp(system_charset_info, - item_field->name.str,field_name->str)) + bool fname_cmp= lex_string_cmp(system_charset_info, + &item_field->field_name, + field_name); + if (!lex_string_cmp(system_charset_info, + &item_field->name, field_name)) { /* If table name was not given we should scan through aliases @@ -6210,7 +6366,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, { if (is_ref_by_name && find->name.str && item->name.str && find->name.length == item->name.length && - !my_strcasecmp(system_charset_info,item->name.str, find->name.str)) + !lex_string_cmp(system_charset_info, &item->name, &find->name)) { found= li.ref(); *counter= i; @@ -6363,6 +6519,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, Query_arena *arena, backup; bool result= TRUE; bool first_outer_loop= TRUE; + Field *field_1, *field_2; + field_visibility_t field_1_invisible, field_2_invisible; /* Leaf table references to which new natural join columns are added if the leaves are != NULL. @@ -6376,7 +6534,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, DBUG_ENTER("mark_common_columns"); DBUG_PRINT("info", ("operand_1: %s operand_2: %s", - table_ref_1->alias, table_ref_2->alias)); + table_ref_1->alias.str, table_ref_2->alias.str)); *found_using_fields= 0; arena= thd->activate_stmt_arena_if_needed(&backup); @@ -6389,13 +6547,23 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, bool is_using_column_1; if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1))) goto err; + + field_1= nj_col_1->field(); + field_1_invisible= field_1 ? field_1->invisible : VISIBLE; + + if (field_1_invisible == INVISIBLE_FULL) + continue; + field_name_1= nj_col_1->name(); is_using_column_1= using_fields && test_if_string_in_list(field_name_1->str, using_fields); DBUG_PRINT ("info", ("field_name_1=%s.%s", - nj_col_1->table_name() ? nj_col_1->table_name() : "", + nj_col_1->safe_table_name(), field_name_1->str)); + if (field_1_invisible && !is_using_column_1) + continue; + /* Find a field with the same name in table_ref_2. @@ -6410,10 +6578,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, const LEX_CSTRING *cur_field_name_2; if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, leaf_2))) goto err; + + field_2= cur_nj_col_2->field(); + field_2_invisible= field_2 ? field_2->invisible : VISIBLE; + + if (field_2_invisible == INVISIBLE_FULL) + continue; + cur_field_name_2= cur_nj_col_2->name(); DBUG_PRINT ("info", ("cur_field_name_2=%s.%s", - cur_nj_col_2->table_name() ? - cur_nj_col_2->table_name() : "", + cur_nj_col_2->safe_table_name(), cur_field_name_2->str)); /* @@ -6427,18 +6601,21 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, here. These columns must be checked only on unqualified reference by name (e.g. in SELECT list). */ - if (!my_strcasecmp(system_charset_info, field_name_1->str, - cur_field_name_2->str)) + if (!lex_string_cmp(system_charset_info, field_name_1, + cur_field_name_2)) { DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); - if (cur_nj_col_2->is_common || - (found && (!using_fields || is_using_column_1))) + if (cur_nj_col_2->is_common || found) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1->str, thd->where); goto err; } - nj_col_2= cur_nj_col_2; - found= TRUE; + if ((!using_fields && !field_2_invisible) || is_using_column_1) + { + DBUG_ASSERT(nj_col_2 == NULL); + nj_col_2= cur_nj_col_2; + found= TRUE; + } } } if (first_outer_loop && leaf_2) @@ -6458,7 +6635,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, clause (if present), mark them as common fields, and add a new equi-join condition to the ON clause. */ - if (nj_col_2 && (!using_fields ||is_using_column_1)) + if (nj_col_2) { /* Create non-fixed fully qualified field and let fix_fields to @@ -6466,8 +6643,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, */ Item *item_1= nj_col_1->create_item(thd); Item *item_2= nj_col_2->create_item(thd); - Field *field_1= nj_col_1->field(); - Field *field_2= nj_col_2->field(); Item_ident *item_ident_1, *item_ident_2; Item_func_eq *eq_cond; @@ -6507,11 +6682,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (!(eq_cond= new (thd->mem_root) Item_func_eq(thd, item_ident_1, item_ident_2))) goto err; /* Out of memory. */ - if (field_1 && field_1->vcol_info) - field_1->table->mark_virtual_col(field_1); - if (field_2 && field_2->vcol_info) - field_2->table->mark_virtual_col(field_2); - /* Add the new equi-join condition to the ON clause. Notice that fix_fields() is applied to all ON conditions in setup_conds() @@ -6523,27 +6693,15 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, nj_col_1->is_common= nj_col_2->is_common= TRUE; DBUG_PRINT ("info", ("%s.%s and %s.%s are common", - nj_col_1->table_name() ? - nj_col_1->table_name() : "", + nj_col_1->safe_table_name(), nj_col_1->name()->str, - nj_col_2->table_name() ? - nj_col_2->table_name() : "", + nj_col_2->safe_table_name(), nj_col_2->name()->str)); if (field_1) - { - TABLE *table_1= nj_col_1->table_ref->table; - /* Mark field_1 used for table cache. */ - bitmap_set_bit(table_1->read_set, field_1->field_index); - table_1->covering_keys.intersect(field_1->part_of_key); - } + update_field_dependencies(thd, field_1, field_1->table); if (field_2) - { - TABLE *table_2= nj_col_2->table_ref->table; - /* Mark field_2 used for table cache. */ - bitmap_set_bit(table_2->read_set, field_2->field_index); - table_2->covering_keys.intersect(field_2->part_of_key); - } + update_field_dependencies(thd, field_2, field_2->table); if (using_fields != NULL) ++(*found_using_fields); @@ -7060,12 +7218,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ****************************************************************************/ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, - List<Item> &fields, enum_mark_columns mark_used_columns, + List<Item> &fields, enum_column_usage column_usage, List<Item> *sum_func_list, List<Item> *pre_fix, bool allow_sum_func) { reg2 Item *item; - enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + enum_column_usage saved_column_usage= thd->column_usage; nesting_map save_allow_sum_func= thd->lex->allow_sum_func; List_iterator<Item> it(fields); bool save_is_item_list_lookup; @@ -7073,8 +7231,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_ENTER("setup_fields"); DBUG_PRINT("enter", ("ref_pointer_array: %p", ref_pointer_array.array())); - thd->mark_used_columns= mark_used_columns; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + 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; @@ -7083,7 +7241,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, thd->lex->current_select->is_item_list_lookup= 0; /* - To prevent fail on forward lookup we fill it with zerows, + To prevent fail on forward lookup we fill it with zeroes, then if we got pointer on zero after find_item_in_list we will know that it is forward lookup. @@ -7127,8 +7285,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, { thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; thd->lex->allow_sum_func= save_allow_sum_func; - thd->mark_used_columns= save_mark_used_columns; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= saved_column_usage; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); DBUG_RETURN(TRUE); /* purecov: inspected */ } if (!ref.is_null()) @@ -7155,8 +7313,8 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; thd->lex->allow_sum_func= save_allow_sum_func; - thd->mark_used_columns= save_mark_used_columns; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= saved_column_usage; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); DBUG_RETURN(MY_TEST(thd->is_error())); } @@ -7447,7 +7605,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, 0) { my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->pos_in_table_list->alias); + table->pos_in_table_list->alias.str); map->set_all(); return 1; } @@ -7518,8 +7676,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, DBUG_ASSERT(tables->is_leaf_for_name_resolution()); if ((table_name && my_strcasecmp(table_alias_charset, table_name, - tables->alias)) || - (db_name && strcmp(tables->db,db_name))) + tables->alias.str)) || + (db_name && strcmp(tables->db.str, db_name))) continue; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -7581,6 +7739,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, for (; !field_iterator.end_of_fields(); field_iterator.next()) { + /* + field() is always NULL for views (see, e.g. Field_iterator_view or + Field_iterator_natural_join). + But view fields can never be invisible. + */ + if ((field= field_iterator.field()) && field->invisible != VISIBLE) + continue; + Item *item; if (!(item= field_iterator.create_item(thd))) @@ -7690,13 +7856,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, */ if (!table_name) my_error(ER_NO_TABLES_USED, MYF(0)); - else if (!db_name && !thd->db) + else if (!db_name && !thd->db.str) my_error(ER_NO_DB_ERROR, MYF(0)); else { char name[FN_REFLEN]; my_snprintf(name, sizeof(name), "%s.%s", - db_name ? db_name : thd->db, table_name); + db_name ? db_name : thd->get_db(), table_name); my_error(ER_BAD_TABLE_ERROR, MYF(0), name); } @@ -7829,14 +7995,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, TABLE_LIST *derived= select_lex->master_unit()->derived; DBUG_ENTER("setup_conds"); - /* Do not fix conditions for the derived tables that have been merged */ - if (derived && derived->merged) - DBUG_RETURN(0); - select_lex->is_item_list_lookup= 0; - thd->mark_used_columns= MARK_COLUMNS_READ; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= MARK_COLUMNS_READ; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); select_lex->cond_count= 0; select_lex->between_count= 0; select_lex->max_equal_elems= 0; @@ -7930,6 +8092,9 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, List_iterator_fast<Item> f(fields),v(values); Item *value, *fld; Item_field *field; + Field *rfield; + TABLE *table; + bool only_unvers_fields= update && table_arg->versioned(); bool save_abort_on_warning= thd->abort_on_warning; bool save_no_errors= thd->no_errors; DBUG_ENTER("fill_record"); @@ -7940,21 +8105,7 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, only one row. */ if (fields.elements) - { - /* - On INSERT or UPDATE fields are checked to be from the same table, - thus we safely can take table from the first field. - */ - fld= (Item_field*)f++; - if (!(field= fld->field_for_view_update())) - { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name.str); - goto err; - } - DBUG_ASSERT(field->field->table == table_arg); table_arg->auto_increment_field_not_null= FALSE; - f.rewind(); - } while ((fld= f++)) { @@ -7964,31 +8115,64 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, goto err; } value=v++; - Field *rfield= field->field; - TABLE* table= rfield->table; + DBUG_ASSERT(value); + rfield= field->field; + table= rfield->table; if (table->next_number_field && rfield->field_index == table->next_number_field->field_index) table->auto_increment_field_not_null= TRUE; Item::Type type= value->type(); - if (rfield->vcol_info && + bool vers_sys_field= table->versioned() && rfield->vers_sys_field(); + if ((rfield->vcol_info || vers_sys_field) && type != Item::DEFAULT_VALUE_ITEM && type != Item::NULL_ITEM && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, - ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN, + ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN), rfield->field_name.str, table->s->table_name.str); + if (vers_sys_field) + continue; } - if (rfield->stored_in_db() && - (value->save_in_field(rfield, 0)) < 0 && !ignore_errors) + if (only_unvers_fields && !rfield->vers_update_unversioned()) + only_unvers_fields= false; + + if (rfield->stored_in_db()) { - my_message(ER_UNKNOWN_ERROR, ER_THD(thd, ER_UNKNOWN_ERROR), MYF(0)); - goto err; + if (value->save_in_field(rfield, 0) < 0 && !ignore_errors) + { + my_message(ER_UNKNOWN_ERROR, ER_THD(thd, ER_UNKNOWN_ERROR), MYF(0)); + goto err; + } + /* + In sql MODE_SIMULTANEOUS_ASSIGNMENT, + move field pointer on value stored in record[1] + which contains row before update (see MDEV-13417) + */ + if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT) + rfield->move_field_offset((my_ptrdiff_t) (table->record[1] - + table->record[0])); } rfield->set_explicit_default(value); } + if (update && thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT) + { + // restore fields pointers on record[0] + f.rewind(); + while ((fld= f++)) + { + rfield= fld->field_for_view_update()->field; + if (rfield->stored_in_db()) + { + table= rfield->table; + rfield->move_field_offset((my_ptrdiff_t) (table->record[0] - + table->record[1])); + } + } + } + if (!update && table_arg->default_field && table_arg->update_default_fields(0, ignore_errors)) goto err; @@ -7996,6 +8180,8 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, if (table_arg->vfield && table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE)) goto err; + if (table_arg->versioned() && !only_unvers_fields) + table_arg->vers_update_fields(); thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; DBUG_RETURN(thd->is_error()); @@ -8048,7 +8234,7 @@ void switch_defaults_to_nullable_trigger_fields(TABLE *table) Field **trigger_field= table->field_to_fill(); /* True if we have NOT NULL fields and BEFORE triggers */ - if (trigger_field != table->field) + if (*trigger_field != *table->field) { for (Field **field_ptr= table->default_field; *field_ptr ; field_ptr++) { @@ -8119,7 +8305,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &values, bool ignore_errors, enum trg_event_type event) { - bool result; + int result; Table_triggers_list *triggers= table->triggers; result= fill_record(thd, table, fields, values, ignore_errors, @@ -8187,7 +8373,6 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, ? table->next_number_field->field_index : ~0U; DBUG_ENTER("fill_record"); - if (!*ptr) { /* No fields to update, quite strange!*/ @@ -8210,10 +8395,19 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, /* Ensure that all fields are from the same table */ DBUG_ASSERT(field->table == table); - value=v++; + if (field->invisible) + { + all_fields_have_values= false; + continue; + } + else + value=v++; + + bool vers_sys_field= table->versioned() && field->vers_sys_field(); + if (field->field_index == autoinc_index) table->auto_increment_field_not_null= TRUE; - if (field->vcol_info) + if (field->vcol_info || (vers_sys_field && !ignore_errors)) { Item::Type type= value->type(); if (type != Item::DEFAULT_VALUE_ITEM && @@ -8221,9 +8415,11 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, table->s->table_category != TABLE_CATEGORY_TEMPORARY) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, - ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN, + ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN), field->field_name.str, table->s->table_name.str); + if (vers_sys_field) + continue; } } @@ -8242,6 +8438,8 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, if (table->vfield && table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE)) goto err; + if (table->versioned()) + table->vers_update_fields(); thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); @@ -8333,8 +8531,8 @@ my_bool mysql_rm_tmp_tables(void) tmp_file_prefix_length)) { char *ext= fn_ext(file->name); - uint ext_len= strlen(ext); - uint filePath_len= my_snprintf(filePath, sizeof(filePath), + size_t ext_len= strlen(ext); + size_t filePath_len= my_snprintf(filePath, sizeof(filePath), "%s%c%s", tmpdir, FN_LIBCHAR, file->name); if (!strcmp(reg_ext, ext)) @@ -8372,84 +8570,6 @@ my_bool mysql_rm_tmp_tables(void) unireg support functions *****************************************************************************/ -/** - A callback to the server internals that is used to address - special cases of the locking protocol. - Invoked when acquiring an exclusive lock, for each thread that - has a conflicting shared metadata lock. - - This function: - - aborts waiting of the thread on a data lock, to make it notice - the pending exclusive lock and back off. - - if the thread is an INSERT DELAYED thread, sends it a KILL - signal to terminate it. - - @note This function does not wait for the thread to give away its - locks. Waiting is done outside for all threads at once. - - @param thd Current thread context - @param in_use The thread to wake up - @param needs_thr_lock_abort Indicates that to wake up thread - this call needs to abort its waiting - on table-level lock. - - @retval TRUE if the thread was woken up - @retval FALSE otherwise. - - @note It is one of two places where border between MDL and the - rest of the server is broken. -*/ - -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - bool signalled= FALSE; - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - !in_use->killed) - { - in_use->set_killed(KILL_SYSTEM_THREAD); - mysql_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - mysql_mutex_lock(in_use->mysys_var->current_mutex); - mysql_cond_broadcast(in_use->mysys_var->current_cond); - mysql_mutex_unlock(in_use->mysys_var->current_mutex); - } - mysql_mutex_unlock(&in_use->mysys_var->mutex); - signalled= TRUE; - } - - if (needs_thr_lock_abort) - { - mysql_mutex_lock(&in_use->LOCK_thd_data); - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* - Check for TABLE::needs_reopen() is needed since in some places we call - handler::close() for table instance (and set TABLE::db_stat to 0) - and do not remove such instances from the THD::open_tables - for some time, during which other thread can see those instances - (e.g. see partitioning code). - */ - if (!thd_table->needs_reopen()) - { - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - if (thd && WSREP(thd) && wsrep_thd_is_BF(thd, true)) - { - WSREP_DEBUG("remove_table_from_cache: %llu", - (unsigned long long) thd->real_id); - wsrep_abort_thd((void *)thd, (void *)in_use, FALSE); - } - } - } - mysql_mutex_unlock(&in_use->LOCK_thd_data); - } - return signalled; -} - - int setup_ftfuncs(SELECT_LEX *select_lex) { List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)), @@ -8478,7 +8598,6 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) { List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)); Item_func_match *ifm; - DBUG_PRINT("info",("Performing FULLTEXT search")); while ((ifm=li++)) if (unlikely(!ifm->fixed)) @@ -8487,8 +8606,8 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) to remove the function from the list. */ li.remove(); - else - ifm->init_search(thd, no_order); + else if (ifm->init_search(thd, no_order)) + return 1; } return 0; } @@ -8674,10 +8793,31 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { - DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG); - /* Make sure all columns get assigned to a default value */ - table->use_all_columns(); - DBUG_ASSERT(table->no_replicate); + if (table->s->table_category == TABLE_CATEGORY_LOG) + { + /* Make sure all columns get assigned to a default value */ + table->use_all_columns(); + DBUG_ASSERT(table->s->no_replicate); + } + else + { + my_error(ER_NOT_LOG_TABLE, MYF(0), table->s->db.str, table->s->table_name.str); + int error= 0; + if (table->current_lock != F_UNLCK) + { + table->current_lock= F_UNLCK; + error= table->file->ha_external_lock(thd, F_UNLCK); + } + if (error) + table->file->print_error(error, MYF(0)); + else + { + tc_release_table(table); + thd->reset_open_tables_state(thd); + thd->restore_backup_open_tables_state(backup); + table= NULL; + } + } } else thd->restore_backup_open_tables_state(backup); diff --git a/sql/sql_base.h b/sql/sql_base.h index bb33af66590..a6a85d47dc9 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -16,7 +16,7 @@ #ifndef SQL_BASE_INCLUDED #define SQL_BASE_INCLUDED -#include "sql_class.h" /* enum_mark_columns */ +#include "sql_class.h" /* enum_column_usage */ #include "sql_trigger.h" /* trg_event_type */ #include "mysqld.h" /* key_map */ #include "table_cache.h" @@ -100,6 +100,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, */ #define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000 #define MYSQL_LOCK_NOT_TEMPORARY 0x2000 +#define MYSQL_LOCK_USE_MALLOC 0x4000 /** Only check THD::killed if waits happen (e.g. wait on MDL, wait on table flush, wait on thr_lock.c locks) while opening and locking table. @@ -135,8 +136,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, const MDL_savepoint &start_of_statement_svp); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, - const char *db_name, - const char *table_name); + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name); void close_thread_tables(THD *thd); void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *); void switch_defaults_to_nullable_trigger_fields(TABLE *table); @@ -158,7 +159,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables, int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, - List<Item> &item, enum_mark_columns mark_used_columns, + List<Item> &item, enum_column_usage column_usage, List<Item> *sum_func_list, List<Item> *pre_fix, bool allow_sum_func); void unfix_fields(List<Item> &items); @@ -174,14 +175,14 @@ find_field_in_tables(THD *thd, Item_ident *item, bool check_privileges, bool register_tree_change); Field * find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, + const char *name, size_t length, const char *item_name, const char *db_name, const char *table_name, Item **ref, bool check_privileges, bool allow_rowid, uint *cached_field_index_ptr, bool register_tree_change, TABLE_LIST **actual_table); Field * -find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, +find_field_in_table(THD *thd, TABLE *table, const char *name, size_t length, bool allow_rowid, uint *cached_field_index_ptr); Field * find_field_in_table_sef(TABLE *table, const char *name); @@ -204,8 +205,8 @@ bool setup_tables_and_check_access(THD *thd, bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function); -void drop_open_table(THD *thd, TABLE *table, const char *db_name, - const char *table_name); +void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name); void update_non_unique_table_error(TABLE_LIST *update, const char *operation, TABLE_LIST *duplicate); @@ -257,6 +258,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, uint dt_phases); bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, bool can_deadlock); +bool open_and_lock_internal_tables(TABLE *table, bool lock); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); @@ -342,31 +344,22 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) } inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) + LEX_CSTRING *db_name, + LEX_CSTRING *table_name) { return find_table_in_list(table, &TABLE_LIST::next_global, db_name, table_name); } -inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) -{ - return find_table_in_list(table, &TABLE_LIST::next_local, - db_name, table_name); -} - - inline bool setup_fields_with_no_wrap(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &item, - enum_mark_columns mark_used_columns, + enum_column_usage column_usage, List<Item> *sum_func_list, bool allow_sum_func) { bool res; thd->lex->select_lex.no_wrap_view_item= TRUE; - res= setup_fields(thd, ref_pointer_array, item, mark_used_columns, + res= setup_fields(thd, ref_pointer_array, item, column_usage, sum_func_list, NULL, allow_sum_func); thd->lex->select_lex.no_wrap_view_item= FALSE; return res; diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index f58bcf2e8fe..00270fb6f32 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_binlog.h" #include "sql_parse.h" @@ -140,7 +140,7 @@ void mysql_client_binlog_statement(THD* thd) my_error(ER_SYNTAX_ERROR, MYF(0)); DBUG_VOID_RETURN; } - size_t decoded_len= my_base64_needed_decoded_length(coded_len); + size_t decoded_len= my_base64_needed_decoded_length((int)coded_len); /* option_bits will be changed when applying the event. But we don't expect diff --git a/sql/sql_bootstrap.cc b/sql/sql_bootstrap.cc index 9733831b41f..9fb22c6b4d4 100644 --- a/sql/sql_bootstrap.cc +++ b/sql/sql_bootstrap.cc @@ -14,7 +14,7 @@ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ -#include <my_global.h> +#include "mariadb.h" #include <ctype.h> #include <string.h> #include "sql_bootstrap.h" diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index b85fb6e5b40..c8f18556d50 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -328,7 +328,7 @@ TODO list: (This could be done with almost no speed penalty) */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_basic_types.h" #include "sql_cache.h" @@ -345,7 +345,6 @@ TODO list: #include "../storage/myisammrg/ha_myisammrg.h" #include "../storage/myisammrg/myrg_def.h" #include "probes_mysql.h" -#include "log_slow.h" #include "transaction.h" #include "strfunc.h" @@ -818,10 +817,10 @@ inline Query_cache_block * Query_cache_block_table::block() Query_cache_block method(s) *****************************************************************************/ -void Query_cache_block::init(ulong block_length) +void Query_cache_block::init(size_t block_length) { DBUG_ENTER("Query_cache_block::init"); - DBUG_PRINT("qcache", ("init block: %p length: %lu", this, + DBUG_PRINT("qcache", ("init block: %p length: %zu", this, block_length)); length = block_length; used = 0; @@ -962,7 +961,7 @@ inline void Query_cache_query::unlock_reading() void Query_cache_query::init_n_lock() { DBUG_ENTER("Query_cache_query::init_n_lock"); - res=0; wri = 0; len = 0; ready= 0; + res=0; wri = 0; len = 0; ready= 0; hit_count = 0; mysql_rwlock_init(key_rwlock_query_cache_query_lock, &lock); lock_writing(); DBUG_PRINT("qcache", ("inited & locked query for block %p", @@ -1038,7 +1037,7 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length, /** libmysql convenience wrapper to insert data into query cache. */ -void query_cache_insert(void *thd_arg, const char *packet, ulong length, +void query_cache_insert(void *thd_arg, const char *packet, size_t length, unsigned pkt_nr) { THD *thd= (THD*) thd_arg; @@ -1054,7 +1053,7 @@ void query_cache_insert(void *thd_arg, const char *packet, ulong length, return; query_cache.insert(thd, &thd->query_cache_tls, - packet, length, + packet, (size_t)length, pkt_nr); } @@ -1065,7 +1064,7 @@ void query_cache_insert(void *thd_arg, const char *packet, ulong length, void Query_cache::insert(THD *thd, Query_cache_tls *query_cache_tls, - const char *packet, ulong length, + const char *packet, size_t length, unsigned pkt_nr) { DBUG_ENTER("Query_cache::insert"); @@ -1098,7 +1097,7 @@ Query_cache::insert(THD *thd, Query_cache_tls *query_cache_tls, Query_cache_block *result= header->result(); DUMP(this); - DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); + DBUG_PRINT("qcache", ("insert packet %zu bytes long",length)); /* On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we @@ -1207,8 +1206,8 @@ void Query_cache::end_of_result(THD *thd) BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *last_result_block; - ulong allign_size; - ulong len; + size_t allign_size; + size_t len; if (header->result() == 0) { @@ -1268,9 +1267,9 @@ void mysql_query_cache_invalidate4(THD *thd, Query_cache methods *****************************************************************************/ -Query_cache::Query_cache(ulong query_cache_limit_arg, - ulong min_allocation_unit_arg, - ulong min_result_data_size_arg, +Query_cache::Query_cache(size_t query_cache_limit_arg, + size_t min_allocation_unit_arg, + size_t min_result_data_size_arg, uint def_query_hash_size_arg, uint def_table_hash_size_arg) :query_cache_size(0), @@ -1284,7 +1283,7 @@ Query_cache::Query_cache(ulong query_cache_limit_arg, def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)), initialized(0) { - ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) + + size_t min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_block_table)) + ALIGN_SIZE(sizeof(Query_cache_query)) + 3); set_if_bigger(min_allocation_unit,min_needed); @@ -1293,11 +1292,11 @@ Query_cache::Query_cache(ulong query_cache_limit_arg, } -ulong Query_cache::resize(ulong query_cache_size_arg) +size_t Query_cache::resize(size_t query_cache_size_arg) { - ulong new_query_cache_size; + size_t new_query_cache_size; DBUG_ENTER("Query_cache::resize"); - DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size, + DBUG_PRINT("qcache", ("from %zu to %zu",query_cache_size, query_cache_size_arg)); DBUG_ASSERT(initialized); @@ -1351,7 +1350,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg) } -ulong Query_cache::set_min_res_unit(ulong size) +size_t Query_cache::set_min_res_unit(size_t size) { DBUG_ASSERT(size % 8 == 0); if (size < min_allocation_unit) @@ -1363,7 +1362,7 @@ ulong Query_cache::set_min_res_unit(ulong size) void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) { TABLE_COUNTER_TYPE local_tables; - ulong tot_length; + size_t tot_length; const char *query; size_t query_length; uint8 tables_type; @@ -1451,8 +1450,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_PRINT("qcache", ("\ long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \ -sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %lu, \ -def_week_frmt: %lu, in_trans: %d, autocommit: %d", +sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \ +def_week_frmt: %zu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, (int)flags.client_depr_eof, @@ -1503,18 +1502,18 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", query_length= thd->base_query.length(); /* Key is query + database + flag */ - if (thd->db_length) + if (thd->db.length) { memcpy((char*) (query + query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE), - thd->db, thd->db_length); + thd->db.str, thd->db.length); DBUG_PRINT("qcache", ("database: %s length: %u", - thd->db, (unsigned) thd->db_length)); + thd->db.str, (unsigned) thd->db.length)); } else { DBUG_PRINT("qcache", ("No active database")); } - tot_length= (query_length + thd->db_length + 1 + + tot_length= (query_length + thd->db.length + 1 + QUERY_CACHE_DB_LENGTH_SIZE + QUERY_CACHE_FLAGS_SIZE); /* We should only copy structure (don't use it location directly) @@ -1536,7 +1535,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", Query_cache_block::QUERY, local_tables); if (query_block != 0) { - DBUG_PRINT("qcache", ("query block %p allocated, %lu", + DBUG_PRINT("qcache", ("query block %p allocated, %zu", query_block, query_block->used)); Query_cache_query *header = query_block->query(); @@ -1616,7 +1615,7 @@ end: @retval TRUE On error */ static bool -send_data_in_chunks(NET *net, const uchar *packet, ulong len) +send_data_in_chunks(NET *net, const uchar *packet, size_t len) { /* On the client we may require more memory than max_allowed_packet @@ -1632,7 +1631,7 @@ send_data_in_chunks(NET *net, const uchar *packet, ulong len) for max_allowed_packet, but large enough to ensure there is no unnecessary overhead from too many syscalls per result set. */ - static const ulong MAX_CHUNK_LENGTH= 1024*1024; + static const size_t MAX_CHUNK_LENGTH= 1024*1024; while (len > MAX_CHUNK_LENGTH) { @@ -1700,7 +1699,7 @@ size_t build_normalized_name(char *buff, size_t bufflen, In case of -1, no error is sent to the client. *) The buffer must be allocated memory of size: - tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; + tot_length= query_length + thd->db.length + 1 + QUERY_CACHE_FLAGS_SIZE; */ int @@ -1713,7 +1712,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) #endif Query_cache_block *result_block; Query_cache_block_table *block_table, *block_table_end; - ulong tot_length; + size_t tot_length; Query_cache_query_flags flags; const char *sql, *sql_end, *found_brace= 0; DBUG_ENTER("Query_cache::send_result_to_client"); @@ -1859,7 +1858,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) as the previous one. */ size_t db_len= uint2korr(sql_end+1); - if (thd->db_length != db_len) + if (thd->db.length != db_len) { /* We should probably reallocate the buffer in this case, @@ -1893,7 +1892,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) if (found_brace) sql= found_brace; make_base_query(&thd->base_query, sql, (size_t) (sql_end - sql), - thd->db_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE + + thd->db.length + 1 + QUERY_CACHE_DB_LENGTH_SIZE + QUERY_CACHE_FLAGS_SIZE); sql= thd->base_query.ptr(); query_length= thd->base_query.length(); @@ -1905,14 +1904,14 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) } tot_length= (query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE + - thd->db_length + QUERY_CACHE_FLAGS_SIZE); + thd->db.length + QUERY_CACHE_FLAGS_SIZE); - if (thd->db_length) + if (thd->db.length) { memcpy((uchar*) sql + query_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE, - thd->db, thd->db_length); + thd->db.str, thd->db.length); DBUG_PRINT("qcache", ("database: '%s' length: %u", - thd->db, (uint) thd->db_length)); + thd->db.str, (uint) thd->db.length)); } else { @@ -1951,8 +1950,8 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) DBUG_PRINT("qcache", ("\ long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %llu, TZ: %p, \ -sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %lu, \ -def_week_frmt: %lu, in_trans: %d, autocommit: %d", +sql mode: 0x%llx, sort len: %llu, conncat len: %llu, div_precision: %zu, \ +def_week_frmt: %zu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, (int)flags.client_depr_eof, @@ -2068,14 +2067,17 @@ lookup: } bzero((char*) &table_list,sizeof(table_list)); - table_list.db = table->db(); - table_list.alias= table_list.table_name= table->table(); + table_list.db.str= table->db(); + table_list.db.length= strlen(table_list.db.str); + table_list.alias.str= table_list.table_name.str= table->table(); + table_list.alias.length= table_list.table_name.length= strlen(table->table()); + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_table_access(thd,SELECT_ACL,&table_list, FALSE, 1,TRUE)) { DBUG_PRINT("qcache", ("probably no SELECT access to %s.%s => return to normal processing", - table_list.db, table_list.alias)); + table_list.db.str, table_list.alias.str)); unlock(); thd->query_cache_is_applicable= 0; // Query can't be cached thd->lex->safe_to_cache_query= 0; // For prepared statements @@ -2085,7 +2087,7 @@ lookup: if (table_list.grant.want_privilege) { DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s", - table_list.db, table_list.alias)); + table_list.db.str, table_list.alias.str)); BLOCK_UNLOCK_RD(query_block); thd->query_cache_is_applicable= 0; // Query can't be cached thd->lex->safe_to_cache_query= 0; // For prepared statements @@ -2096,7 +2098,7 @@ lookup: if (table->callback()) { char qcache_se_key_name[FN_REFLEN + 10]; - uint qcache_se_key_len, db_length= strlen(table->db()); + size_t qcache_se_key_len, db_length= strlen(table->db()); engine_data= table->engine_data(); qcache_se_key_len= build_normalized_name(qcache_se_key_name, @@ -2110,16 +2112,16 @@ lookup: table->suffix_length()); if (!(*table->callback())(thd, qcache_se_key_name, - qcache_se_key_len, &engine_data)) + (uint)qcache_se_key_len, &engine_data)) { DBUG_PRINT("qcache", ("Handler does not allow caching for %.*s", - qcache_se_key_len, qcache_se_key_name)); + (int)qcache_se_key_len, qcache_se_key_name)); BLOCK_UNLOCK_RD(query_block); if (engine_data != table->engine_data()) { DBUG_PRINT("qcache", ("Handler require invalidation queries of %.*s %llu-%llu", - qcache_se_key_len, qcache_se_key_name, + (int)qcache_se_key_len, qcache_se_key_name, engine_data, table->engine_data())); invalidate_table_internal(thd, (uchar *) table->db(), @@ -2145,10 +2147,11 @@ lookup: } else DBUG_PRINT("qcache", ("handler allow caching %s,%s", - table_list.db, table_list.alias)); + table_list.db.str, table_list.alias.str)); } move_to_query_list_end(query_block); hits++; + query->increment_hits(); unlock(); /* @@ -2158,7 +2161,7 @@ lookup: THD_STAGE_INFO(thd, stage_sending_cached_result_to_client); do { - DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %u)", + DBUG_PRINT("qcache", ("Results (len: %zu used: %zu headers: %u)", result_block->length, result_block->used, (uint) (result_block->headers_len()+ ALIGN_SIZE(sizeof(Query_cache_result))))); @@ -2320,7 +2323,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table, DBUG_VOID_RETURN; } -void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, +void Query_cache::invalidate(THD *thd, const char *key, size_t key_length, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (key)"); @@ -2347,7 +2350,7 @@ void Query_cache::invalidate(THD *thd, const char *db) if (is_disabled()) DBUG_VOID_RETURN; - DBUG_ASSERT(ok_for_lower_case_names(db)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(db)); bool restart= FALSE; /* @@ -2464,7 +2467,7 @@ void Query_cache::flush() */ -void Query_cache::pack(THD *thd, ulong join_limit, uint iteration_limit) +void Query_cache::pack(THD *thd, size_t join_limit, uint iteration_limit) { DBUG_ENTER("Query_cache::pack"); @@ -2584,11 +2587,11 @@ void Query_cache::init() } -ulong Query_cache::init_cache() +size_t Query_cache::init_cache() { - uint mem_bin_count, num, step; - ulong mem_bin_size, prev_size, inc; - ulong additional_data_size, max_mem_bin_size, approx_additional_data_size; + size_t mem_bin_count, num, step; + size_t mem_bin_size, prev_size, inc; + size_t additional_data_size, max_mem_bin_size, approx_additional_data_size; int align; DBUG_ENTER("Query_cache::init_cache"); @@ -2654,7 +2657,7 @@ ulong Query_cache::init_cache() my_malloc_lock(query_cache_size+additional_data_size, MYF(0)))) goto err; - DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins", + DBUG_PRINT("qcache", ("cache length %zu, min unit %zu, %zu bins", query_cache_size, min_allocation_unit, mem_bin_num)); steps = (Query_cache_memory_bin_step *) cache; @@ -2678,9 +2681,9 @@ ulong Query_cache::init_cache() mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; while (mem_bin_size > min_allocation_unit) { - ulong incr = (steps[step-1].size - mem_bin_size) / mem_bin_count; - unsigned long size = mem_bin_size; - for (uint i= mem_bin_count; i > 0; i--) + size_t incr = (steps[step-1].size - mem_bin_size) / mem_bin_count; + size_t size = mem_bin_size; + for (size_t i= mem_bin_count; i > 0; i--) { bins[num+i-1].init(size); size += incr; @@ -2703,9 +2706,9 @@ ulong Query_cache::init_cache() steps[step].init(mem_bin_size, num + mem_bin_count - 1, inc); { - uint skiped = (min_allocation_unit - mem_bin_size)/inc; - ulong size = mem_bin_size + inc*skiped; - uint i = mem_bin_count - skiped; + size_t skiped = (min_allocation_unit - mem_bin_size)/inc; + size_t size = mem_bin_size + inc*skiped; + size_t i = mem_bin_count - skiped; while (i-- > 0) { bins[num+i].init(size); @@ -2917,7 +2920,7 @@ my_bool Query_cache::free_old_query() void Query_cache::free_query_internal(Query_cache_block *query_block) { DBUG_ENTER("Query_cache::free_query_internal"); - DBUG_PRINT("qcache", ("free query %p %lu bytes result", + DBUG_PRINT("qcache", ("free query %p %zu bytes result", query_block, query_block->query()->length() )); @@ -2987,7 +2990,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block) void Query_cache::free_query(Query_cache_block *query_block) { DBUG_ENTER("Query_cache::free_query"); - DBUG_PRINT("qcache", ("free query %p %lu bytes result", + DBUG_PRINT("qcache", ("free query %p %zu bytes result", query_block, query_block->query()->length() )); @@ -3002,18 +3005,18 @@ void Query_cache::free_query(Query_cache_block *query_block) *****************************************************************************/ Query_cache_block * -Query_cache::write_block_data(ulong data_len, uchar* data, - ulong header_len, +Query_cache::write_block_data(size_t data_len, uchar* data, + size_t header_len, Query_cache_block::block_type type, TABLE_COUNTER_TYPE ntab) { - ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + size_t all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) + header_len); - ulong len = data_len + all_headers_len; - ulong align_len= ALIGN_SIZE(len); + size_t len = data_len + all_headers_len; + size_t align_len= ALIGN_SIZE(len); DBUG_ENTER("Query_cache::write_block_data"); - DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld", + DBUG_PRINT("qcache", ("data: %zd, header: %zd, all header: %zd", data_len, header_len, all_headers_len)); Query_cache_block *block= allocate_block(MY_MAX(align_len, min_allocation_unit),1, 0); @@ -3031,33 +3034,33 @@ Query_cache::write_block_data(ulong data_len, uchar* data, my_bool Query_cache::append_result_data(Query_cache_block **current_block, - ulong data_len, uchar* data, + size_t data_len, uchar* data, Query_cache_block *query_block) { DBUG_ENTER("Query_cache::append_result_data"); - DBUG_PRINT("qcache", ("append %lu bytes to %p query", + DBUG_PRINT("qcache", ("append %zu bytes to %p query", data_len, query_block)); if (query_block->query()->add(data_len) > query_cache_limit) { - DBUG_PRINT("qcache", ("size limit reached %lu > %lu", + DBUG_PRINT("qcache", ("size limit reached %zu > %zu", query_block->query()->length(), query_cache_limit)); DBUG_RETURN(0); } if (*current_block == 0) { - DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len)); + DBUG_PRINT("qcache", ("allocated first result data block %zu", data_len)); DBUG_RETURN(write_result_data(current_block, data_len, data, query_block, Query_cache_block::RES_BEG)); } Query_cache_block *last_block = (*current_block)->prev; - DBUG_PRINT("qcache", ("lastblock %p len %lu used %lu", + DBUG_PRINT("qcache", ("lastblock %p len %zu used %zu", last_block, last_block->length, last_block->used)); my_bool success = 1; - ulong last_block_free_space= last_block->length - last_block->used; + size_t last_block_free_space= last_block->length - last_block->used; /* We will first allocate and write the 'tail' of data, that doesn't fit @@ -3066,8 +3069,8 @@ Query_cache::append_result_data(Query_cache_block **current_block, */ // Try join blocks if physically next block is free... - ulong tail = data_len - last_block_free_space; - ulong append_min = get_min_append_result_data_size(); + size_t tail = data_len - last_block_free_space; + size_t append_min = get_min_append_result_data_size(); if (last_block_free_space < data_len && append_next_free_block(last_block, MY_MAX(tail, append_min))) @@ -3075,7 +3078,7 @@ Query_cache::append_result_data(Query_cache_block **current_block, // If no space in last block (even after join) allocate new block if (last_block_free_space < data_len) { - DBUG_PRINT("qcache", ("allocate new block for %lu bytes", + DBUG_PRINT("qcache", ("allocate new block for %zu bytes", data_len-last_block_free_space)); Query_cache_block *new_block = 0; success = write_result_data(&new_block, data_len-last_block_free_space, @@ -3098,8 +3101,8 @@ Query_cache::append_result_data(Query_cache_block **current_block, // Now finally write data to the last block if (success && last_block_free_space > 0) { - ulong to_copy = MY_MIN(data_len,last_block_free_space); - DBUG_PRINT("qcache", ("use free space %lub at block %p to copy %lub", + size_t to_copy = MY_MIN(data_len,last_block_free_space); + DBUG_PRINT("qcache", ("use free space %zub at block %p to copy %zub", last_block_free_space,last_block, to_copy)); memcpy((uchar*) last_block + last_block->used, data, to_copy); last_block->used+=to_copy; @@ -3109,12 +3112,12 @@ Query_cache::append_result_data(Query_cache_block **current_block, my_bool Query_cache::write_result_data(Query_cache_block **result_block, - ulong data_len, uchar* data, + size_t data_len, uchar* data, Query_cache_block *query_block, Query_cache_block::block_type type) { DBUG_ENTER("Query_cache::write_result_data"); - DBUG_PRINT("qcache", ("data_len %lu",data_len)); + DBUG_PRINT("qcache", ("data_len %zu",data_len)); /* Reserve block(s) for filling @@ -3140,8 +3143,8 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block, do { block->type = type; - ulong length = block->used - headers_len; - DBUG_PRINT("qcache", ("write %lu byte in block %p",length, + size_t length = block->used - headers_len; + DBUG_PRINT("qcache", ("write %zu byte in block %p",length, block)); memcpy((uchar*) block+headers_len, rest, length); rest += length; @@ -3181,16 +3184,16 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block, DBUG_RETURN(success); } -inline ulong Query_cache::get_min_first_result_data_size() +inline size_t Query_cache::get_min_first_result_data_size() { if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER) return min_result_data_size; - ulong avg_result = (query_cache_size - free_memory) / queries_in_cache; + size_t avg_result = (query_cache_size - free_memory) / queries_in_cache; avg_result = MY_MIN(avg_result, query_cache_limit); return MY_MAX(min_result_data_size, avg_result); } -inline ulong Query_cache::get_min_append_result_data_size() +inline size_t Query_cache::get_min_append_result_data_size() { return min_result_data_size; } @@ -3199,25 +3202,25 @@ inline ulong Query_cache::get_min_append_result_data_size() Allocate one or more blocks to hold data */ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, - ulong data_len, + size_t data_len, Query_cache_block *query_block, my_bool first_block_arg) { - ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + size_t all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_result))); - ulong min_size = (first_block_arg ? + size_t min_size = (first_block_arg ? get_min_first_result_data_size(): get_min_append_result_data_size()); Query_cache_block *prev_block= NULL; Query_cache_block *new_block; DBUG_ENTER("Query_cache::allocate_data_chain"); - DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu", + DBUG_PRINT("qcache", ("data_len %zu, all_headers_len %zu", data_len, all_headers_len)); do { - ulong len= data_len + all_headers_len; - ulong align_len= ALIGN_SIZE(len); + size_t len= data_len + all_headers_len; + size_t align_len= ALIGN_SIZE(len); if (!(new_block= allocate_block(MY_MAX(min_size, align_len), min_result_data_size == 0, @@ -3234,7 +3237,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, Query_cache_result *header = new_block->result(); header->parent(query_block); - DBUG_PRINT("qcache", ("Block len %lu used %lu", + DBUG_PRINT("qcache", ("Block len %zu used %zu", new_block->length, new_block->used)); if (prev_block) @@ -3284,7 +3287,7 @@ void Query_cache::invalidate_table(THD *thd, TABLE *table) table->s->table_cache_key.length); } -void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length) +void Query_cache::invalidate_table(THD *thd, uchar * key, size_t key_length) { DEBUG_SYNC(thd, "wait_in_query_cache_invalidate1"); @@ -3312,7 +3315,7 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length) */ void -Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length) +Query_cache::invalidate_table_internal(THD *thd, uchar *key, size_t key_length) { Query_cache_block *table_block= (Query_cache_block*)my_hash_search(&tables, key, key_length); @@ -3415,7 +3418,7 @@ Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used, if (!insert_table(thd, tables_used->table->s->table_cache_key.length, tables_used->table->s->table_cache_key.str, (*block_table), - tables_used->db_length, 0, + tables_used->db.length, 0, tables_used->table->file->table_cache_type(), tables_used->callback_func, tables_used->engine_data, @@ -3478,16 +3481,15 @@ my_bool Query_cache::register_all_tables(THD *thd, */ my_bool -Query_cache::insert_table(THD *thd, uint key_len, const char *key, - Query_cache_block_table *node, - uint32 db_length, uint8 suffix_length_arg, +Query_cache::insert_table(THD *thd, size_t key_len, const char *key, + Query_cache_block_table *node, size_t db_length, uint8 suffix_length_arg, uint8 cache_type, qc_engine_callback callback, ulonglong engine_data, my_bool hash) { DBUG_ENTER("Query_cache::insert_table"); - DBUG_PRINT("qcache", ("insert table node %p, len %d", + DBUG_PRINT("qcache", ("insert table node %p, len %zu", node, key_len)); Query_cache_block *table_block= @@ -3553,7 +3555,7 @@ Query_cache::insert_table(THD *thd, uint key_len, const char *key, } char *db= header->db(); header->table(db + db_length + 1); - header->key_length(key_len); + header->key_length((uint32)key_len); header->suffix_length(suffix_length_arg); header->type(cache_type); header->callback(callback); @@ -3625,15 +3627,15 @@ void Query_cache::unlink_table(Query_cache_block_table *node) *****************************************************************************/ Query_cache_block * -Query_cache::allocate_block(ulong len, my_bool not_less, ulong min) +Query_cache::allocate_block(size_t len, my_bool not_less, size_t min) { DBUG_ENTER("Query_cache::allocate_block"); - DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu", + DBUG_PRINT("qcache", ("len %zu, not less %d, min %zu", len, not_less,min)); if (len >= MY_MIN(query_cache_size, query_cache_limit)) { - DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu", + DBUG_PRINT("qcache", ("Query cache hase only %zu memory and limit %zu", query_cache_size, query_cache_limit)); DBUG_RETURN(0); // in any case we don't have such piece of memory } @@ -3657,11 +3659,11 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min) Query_cache_block * -Query_cache::get_free_block(ulong len, my_bool not_less, ulong min) +Query_cache::get_free_block(size_t len, my_bool not_less, size_t min) { Query_cache_block *block = 0, *first = 0; DBUG_ENTER("Query_cache::get_free_block"); - DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len, + DBUG_PRINT("qcache",("length %zu, not_less %d, min %zu", len, (int)not_less, min)); /* Find block with minimal size > len */ @@ -3752,7 +3754,7 @@ void Query_cache::free_memory_block(Query_cache_block *block) } -void Query_cache::split_block(Query_cache_block *block, ulong len) +void Query_cache::split_block(Query_cache_block *block, size_t len) { DBUG_ENTER("Query_cache::split_block"); Query_cache_block *new_block = (Query_cache_block*)(((uchar*) block)+len); @@ -3773,7 +3775,7 @@ void Query_cache::split_block(Query_cache_block *block, ulong len) else free_memory_block(new_block); - DBUG_PRINT("qcache", ("split %p (%lu) new %p", + DBUG_PRINT("qcache", ("split %p (%zu) new %p", block, len, new_block)); DBUG_VOID_RETURN; } @@ -3806,16 +3808,16 @@ Query_cache::join_free_blocks(Query_cache_block *first_block_arg, my_bool Query_cache::append_next_free_block(Query_cache_block *block, - ulong add_size) + size_t add_size) { Query_cache_block *next_block = block->pnext; DBUG_ENTER("Query_cache::append_next_free_block"); - DBUG_PRINT("enter", ("block %p, add_size %lu", block, + DBUG_PRINT("enter", ("block %p, add_size %zu", block, add_size)); if (next_block != first_block && next_block->is_free()) { - ulong old_len = block->length; + size_t old_len = block->length; exclude_from_free_memory_list(next_block); next_block->destroy(); total_blocks--; @@ -3865,14 +3867,14 @@ void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block) DBUG_VOID_RETURN; } -uint Query_cache::find_bin(ulong size) +uint Query_cache::find_bin(size_t size) { DBUG_ENTER("Query_cache::find_bin"); // Binary search - int left = 0, right = mem_bin_steps; + size_t left = 0, right = mem_bin_steps; do { - int middle = (left + right) / 2; + size_t middle = (left + right) / 2; if (steps[middle].size > size) left = middle+1; else @@ -3881,15 +3883,15 @@ uint Query_cache::find_bin(ulong size) if (left == 0) { // first bin not subordinate of common rules - DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size)); + DBUG_PRINT("qcache", ("first bin (# 0), size %zu",size)); DBUG_RETURN(0); } - uint bin = steps[left].idx - - (uint)((size - steps[left].size)/steps[left].increment); + size_t bin = steps[left].idx - + ((size - steps[left].size)/steps[left].increment); - DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu", + DBUG_PRINT("qcache", ("bin %zu step %zu, size %zu step size %zu", bin, left, size, steps[left].size)); - DBUG_RETURN(bin); + DBUG_RETURN((uint)bin); } @@ -4067,41 +4069,35 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, tables_used->view_name.str, tables_used->view_db.str)); *tables_type|= HA_CACHE_TBL_NONTRANSACT; + continue; } - else + if (tables_used->derived) { - if (tables_used->derived) - { - DBUG_PRINT("qcache", ("table: %s", tables_used->alias)); - table_count--; - DBUG_PRINT("qcache", ("derived table skipped")); - continue; - } - DBUG_PRINT("qcache", ("table: %s db: %s type: %u", - tables_used->table->s->table_name.str, - tables_used->table->s->db.str, - tables_used->table->s->db_type()->db_type)); - *tables_type|= tables_used->table->file->table_cache_type(); + DBUG_PRINT("qcache", ("table: %s", tables_used->alias.str)); + table_count--; + DBUG_PRINT("qcache", ("derived table skipped")); + continue; + } - /* - table_alias_charset used here because it depends of - lower_case_table_names variable - */ - table_count+= tables_used->table->file-> - count_query_cache_dependant_tables(tables_type); - - if (tables_used->table->s->tmp_table != NO_TMP_TABLE || - (*tables_type & HA_CACHE_TBL_NOCACHE) || - (tables_used->db_length == 5 && - my_strnncoll(table_alias_charset, - (uchar*)tables_used->table->s->table_cache_key.str, 6, - (uchar*)"mysql",6) == 0)) - { - DBUG_PRINT("qcache", - ("select not cacheable: temporary, system or " - "other non-cacheable table(s)")); - DBUG_RETURN(0); - } + DBUG_PRINT("qcache", ("table: %s db: %s type: %u", + tables_used->table->s->table_name.str, + tables_used->table->s->db.str, + tables_used->table->s->db_type()->db_type)); + *tables_type|= tables_used->table->file->table_cache_type(); + + /* + table_alias_charset used here because it depends of + lower_case_table_names variable + */ + table_count+= tables_used->table->file-> + count_query_cache_dependant_tables(tables_type); + + if (tables_used->table->s->not_usable_by_query_cache) + { + DBUG_PRINT("qcache", + ("select not cacheable: temporary, system or " + "other non-cacheable table(s)")); + DBUG_RETURN(0); } } DBUG_RETURN(table_count); @@ -4193,7 +4189,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, handler= table->file; if (!handler->register_query_cache_table(thd, table->s->normalized_path.str, - table->s->normalized_path.length, + (uint)table->s->normalized_path.length, &tables_used->callback_func, &tables_used->engine_data)) { @@ -4219,7 +4215,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, /** Rearrange all memory blocks so that free memory joins at the 'bottom' of the allocated memory block containing all cache data. - @see Query_cache::pack(ulong join_limit, uint iteration_limit) + @see Query_cache::pack(size_t join_limit, uint iteration_limit) */ void Query_cache::pack_cache() @@ -4230,7 +4226,7 @@ void Query_cache::pack_cache() uchar *border = 0; Query_cache_block *before = 0; - ulong gap = 0; + size_t gap = 0; my_bool ok = 1; Query_cache_block *block = first_block; DUMP(this); @@ -4264,7 +4260,7 @@ void Query_cache::pack_cache() my_bool Query_cache::move_by_type(uchar **border, - Query_cache_block **before, ulong *gap, + Query_cache_block **before, size_t *gap, Query_cache_block *block) { DBUG_ENTER("Query_cache::move_by_type"); @@ -4286,7 +4282,7 @@ my_bool Query_cache::move_by_type(uchar **border, block->pnext->pprev=block->pprev; block->destroy(); total_blocks--; - DBUG_PRINT("qcache", ("added to gap (%lu)", *gap)); + DBUG_PRINT("qcache", ("added to gap (%zu)", *gap)); break; } case Query_cache_block::TABLE: @@ -4295,7 +4291,7 @@ my_bool Query_cache::move_by_type(uchar **border, DBUG_PRINT("qcache", ("block %p TABLE", block)); if (*border == 0) break; - ulong len = block->length, used = block->used; + size_t len = block->length, used = block->used; Query_cache_block_table *list_root = block->table(0); Query_cache_block_table *tprev = list_root->prev, *tnext = list_root->next; @@ -4345,7 +4341,7 @@ my_bool Query_cache::move_by_type(uchar **border, /* Fix hash to point at moved block */ my_hash_replace(&tables, &record_idx, (uchar*) new_block); - DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p", + DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p", len, new_block, *border)); break; } @@ -4356,7 +4352,7 @@ my_bool Query_cache::move_by_type(uchar **border, if (*border == 0) break; BLOCK_LOCK_WR(block); - ulong len = block->length, used = block->used; + size_t len = block->length, used = block->used; TABLE_COUNTER_TYPE n_tables = block->n_tables; Query_cache_block *prev = block->prev, *next = block->next, @@ -4438,7 +4434,7 @@ my_bool Query_cache::move_by_type(uchar **border, } /* Fix hash to point at moved block */ my_hash_replace(&queries, &record_idx, (uchar*) new_block); - DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p", + DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p", len, new_block, *border)); break; } @@ -4455,7 +4451,7 @@ my_bool Query_cache::move_by_type(uchar **border, BLOCK_LOCK_WR(query_block); Query_cache_block *next= block->next, *prev= block->prev; Query_cache_block::block_type type= block->type; - ulong len = block->length, used = block->used; + size_t len = block->length, used = block->used; Query_cache_block *pprev = block->pprev, *pnext = block->pnext, *new_block =(Query_cache_block*) *border; @@ -4473,7 +4469,7 @@ my_bool Query_cache::move_by_type(uchar **border, *border += len; *before = new_block; /* If result writing complete && we have free space in block */ - ulong free_space= new_block->length - new_block->used; + size_t free_space= new_block->length - new_block->used; free_space-= free_space % ALIGN_SIZE(1); if (query->result()->type == Query_cache_block::RESULT && new_block->length > new_block->used && @@ -4483,11 +4479,11 @@ my_bool Query_cache::move_by_type(uchar **border, *border-= free_space; *gap+= free_space; DBUG_PRINT("qcache", - ("rest of result free space added to gap (%lu)", *gap)); + ("rest of result free space added to gap (%zu)", *gap)); new_block->length -= free_space; } BLOCK_UNLOCK_WR(query_block); - DBUG_PRINT("qcache", ("moved %lu bytes to %p, new gap at %p", + DBUG_PRINT("qcache", ("moved %zu bytes to %p, new gap at %p", len, new_block, *border)); break; } @@ -4527,7 +4523,7 @@ void Query_cache::relink(Query_cache_block *oblock, } -my_bool Query_cache::join_results(ulong join_limit) +my_bool Query_cache::join_results(size_t join_limit) { my_bool has_moving = 0; DBUG_ENTER("Query_cache::join_results"); @@ -4551,7 +4547,7 @@ my_bool Query_cache::join_results(ulong join_limit) { has_moving = 1; Query_cache_block *first_result = header->result(); - ulong new_len = (header->length() + + size_t new_len = (header->length() + ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_result))); if (new_result_block->length > @@ -4564,7 +4560,7 @@ my_bool Query_cache::join_results(ulong join_limit) new_result_block->used = new_len; new_result_block->next = new_result_block->prev = new_result_block; - DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)", + DBUG_PRINT("qcache", ("new block %zu/%zu (%zu)", new_result_block->length, new_result_block->used, header->length())); @@ -4575,9 +4571,9 @@ my_bool Query_cache::join_results(ulong join_limit) Query_cache_block *result_block = first_result; do { - ulong len = (result_block->used - result_block->headers_len() - + size_t len = (result_block->used - result_block->headers_len() - ALIGN_SIZE(sizeof(Query_cache_result))); - DBUG_PRINT("loop", ("add block %lu/%lu (%lu)", + DBUG_PRINT("loop", ("add block %zu/%zu (%zu)", result_block->length, result_block->used, len)); @@ -4679,14 +4675,14 @@ void Query_cache::bins_dump() return; } - DBUG_PRINT("qcache", ("mem_bin_num=%u, mem_bin_steps=%u", + DBUG_PRINT("qcache", ("mem_bin_num=%zu, mem_bin_steps=%zu", mem_bin_num, mem_bin_steps)); DBUG_PRINT("qcache", ("-------------------------")); DBUG_PRINT("qcache", (" size idx step")); DBUG_PRINT("qcache", ("-------------------------")); for (i=0; i < mem_bin_steps; i++) { - DBUG_PRINT("qcache", ("%10lu %3d %10lu", steps[i].size, steps[i].idx, + DBUG_PRINT("qcache", ("%10zu %3zd %10zu", steps[i].size, steps[i].idx, steps[i].increment)); } DBUG_PRINT("qcache", ("-------------------------")); @@ -4694,13 +4690,13 @@ void Query_cache::bins_dump() DBUG_PRINT("qcache", ("-------------------------")); for (i=0; i < mem_bin_num; i++) { - DBUG_PRINT("qcache", ("%10lu %3d %p", bins[i].size, bins[i].number, + DBUG_PRINT("qcache", ("%10zu %3d %p", bins[i].size, bins[i].number, &(bins[i]))); if (bins[i].free_blocks) { Query_cache_block *block = bins[i].free_blocks; do{ - DBUG_PRINT("qcache", ("\\-- %lu %p %p %p %p %p", + DBUG_PRINT("qcache", ("\\-- %zu %p %p %p %p %p", block->length,block, block->next,block->prev, block->pnext,block->pprev)); @@ -4727,7 +4723,7 @@ void Query_cache::cache_dump() do { DBUG_PRINT("qcache", - ("%10lu %10lu %1d %2d %p %p %p %p %p", + ("%10zu %10zu %1d %2d %p %p %p %p %p", i->length, i->used, (int)i->type, i->n_tables,i, i->next,i->prev,i->pnext, @@ -4783,7 +4779,7 @@ void Query_cache::queries_dump() Query_cache_block *result_beg = result_block; do { - DBUG_PRINT("qcache", ("-r- %u %lu/%lu %p %p %p %p %p", + DBUG_PRINT("qcache", ("-r- %u %zu/%zu %p %p %p %p %p", (uint) result_block->type, result_block->length, result_block->used, result_block, @@ -4861,7 +4857,7 @@ my_bool Query_cache::check_integrity(bool locked) } DBUG_PRINT("qcache", ("physical address check ...")); - ulong free=0, used=0; + size_t free=0, used=0; Query_cache_block * block = first_block; do { @@ -4926,7 +4922,7 @@ my_bool Query_cache::check_integrity(bool locked) } else { - int idx = (((uchar*)bin) - ((uchar*)bins)) / + size_t idx = (((uchar*)bin) - ((uchar*)bins)) / sizeof(Query_cache_memory_bin); if (in_list(bins[idx].free_blocks, block, "free memory")) result = 1; @@ -4999,7 +4995,7 @@ my_bool Query_cache::check_integrity(bool locked) if (used + free != query_cache_size) { DBUG_PRINT("error", - ("used memory (%lu) + free memory (%lu) != query_cache_size (%lu)", + ("used memory (%zu) + free memory (%zu) != query_cache_size (%zu)", used, free, query_cache_size)); result = 1; } @@ -5007,7 +5003,7 @@ my_bool Query_cache::check_integrity(bool locked) if (free != free_memory) { DBUG_PRINT("error", - ("free memory (%lu) != free_memory (%lu)", + ("free memory (%zu) != free_memory (%zu)", free, free_memory)); result = 1; } diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 07a80f778b8..eb046b4d167 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -126,8 +126,8 @@ struct Query_cache_block enum block_type {FREE, QUERY, RESULT, RES_CONT, RES_BEG, RES_INCOMPLETE, TABLE, INCOMPLETE}; - ulong length; // length of all block - ulong used; // length of data + size_t length; // length of all block + size_t used; // length of data /* Not used **pprev, **prev because really needed access to pervious block: *pprev to join free blocks @@ -139,7 +139,7 @@ struct Query_cache_block TABLE_COUNTER_TYPE n_tables; // number of tables in query inline bool is_free(void) { return type == FREE; } - void init(ulong length); + void init(size_t length); void destroy(); uint headers_len(); uchar* data(void); @@ -155,10 +155,11 @@ struct Query_cache_query mysql_rwlock_t lock; Query_cache_block *res; Query_cache_tls *wri; - ulong len; + size_t len; unsigned int last_pkt_nr; uint8 tbls_type; uint8 ready; + ulonglong hit_count; Query_cache_query() {} /* Remove gcc warning */ inline void init_n_lock(); @@ -171,9 +172,9 @@ struct Query_cache_query inline void writer(Query_cache_tls *p) { wri= p; } inline uint8 tables_type() { return tbls_type; } inline void tables_type(uint8 type) { tbls_type= type; } - inline ulong length() { return len; } - inline ulong add(ulong packet_len) { return(len+= packet_len); } - inline void length(ulong length_arg) { len= length_arg; } + inline size_t length() { return len; } + inline size_t add(size_t packet_len) { return(len+= packet_len); } + inline void length(size_t length_arg) { len= length_arg; } inline uchar* query() { return (((uchar*)this) + ALIGN_SIZE(sizeof(Query_cache_query))); @@ -184,6 +185,8 @@ struct Query_cache_query */ inline void set_results_ready() { ready= 1; } inline bool is_results_ready() { return ready; } + inline void increment_hits() { hit_count++; } + inline ulonglong hits() { return hit_count; } void lock_writing(); void lock_reading(); bool try_lock_writing(); @@ -265,12 +268,12 @@ struct Query_cache_memory_bin { Query_cache_memory_bin() {} /* Remove gcc warning */ #ifndef DBUG_OFF - ulong size; + size_t size; #endif uint number; Query_cache_block *free_blocks; - inline void init(ulong size_arg) + inline void init(size_t size_arg) { #ifndef DBUG_OFF size = size_arg; @@ -283,10 +286,10 @@ struct Query_cache_memory_bin struct Query_cache_memory_bin_step { Query_cache_memory_bin_step() {} /* Remove gcc warning */ - ulong size; - ulong increment; - uint idx; - inline void init(ulong size_arg, uint idx_arg, ulong increment_arg) + size_t size; + size_t increment; + size_t idx; + inline void init(size_t size_arg, size_t idx_arg, size_t increment_arg) { size = size_arg; idx = idx_arg; @@ -298,9 +301,9 @@ class Query_cache { public: /* Info */ - ulong query_cache_size, query_cache_limit; + size_t query_cache_size, query_cache_limit; /* statistics */ - ulong free_memory, queries_in_cache, hits, inserts, refused, + size_t free_memory, queries_in_cache, hits, inserts, refused, free_memory_blocks, total_blocks, lowmem_prunes; @@ -316,7 +319,7 @@ private: Cache_staus m_cache_status; void free_query_internal(Query_cache_block *point); - void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length); + void invalidate_table_internal(THD *thd, uchar *key, size_t key_length); protected: /* @@ -343,10 +346,10 @@ protected: Query_cache_memory_bin_step *steps; // bins spacing info HASH queries, tables; /* options */ - ulong min_allocation_unit, min_result_data_size; + size_t min_allocation_unit, min_result_data_size; uint def_query_hash_size, def_table_hash_size; - uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin + size_t mem_bin_num, mem_bin_steps; // See at init_cache & find_bin bool initialized; @@ -364,12 +367,12 @@ protected: my_bool free_old_query(); void free_query(Query_cache_block *point); my_bool allocate_data_chain(Query_cache_block **result_block, - ulong data_len, + size_t data_len, Query_cache_block *query_block, my_bool first_block); void invalidate_table(THD *thd, TABLE_LIST *table); void invalidate_table(THD *thd, TABLE *table); - void invalidate_table(THD *thd, uchar *key, uint32 key_length); + void invalidate_table(THD *thd, uchar *key, size_t key_length); void invalidate_table(THD *thd, Query_cache_block *table_block); void invalidate_query_block_list(THD *thd, Query_cache_block_table *list_root); @@ -382,19 +385,19 @@ protected: TABLE_LIST *tables_used, TABLE_COUNTER_TYPE tables); void unlink_table(Query_cache_block_table *node); - Query_cache_block *get_free_block (ulong len, my_bool not_less, - ulong min); + Query_cache_block *get_free_block (size_t len, my_bool not_less, + size_t min); void free_memory_block(Query_cache_block *point); - void split_block(Query_cache_block *block, ulong len); + void split_block(Query_cache_block *block, size_t len); Query_cache_block *join_free_blocks(Query_cache_block *first_block, Query_cache_block *block_in_list); my_bool append_next_free_block(Query_cache_block *block, - ulong add_size); + size_t add_size); void exclude_from_free_memory_list(Query_cache_block *free_block); void insert_into_free_memory_list(Query_cache_block *new_block); my_bool move_by_type(uchar **border, Query_cache_block **before, - ulong *gap, Query_cache_block *i); - uint find_bin(ulong size); + size_t *gap, Query_cache_block *i); + uint find_bin(size_t size); void move_to_query_list_end(Query_cache_block *block); void insert_into_free_memory_sorted_list(Query_cache_block *new_block, Query_cache_block **list); @@ -405,31 +408,31 @@ protected: Query_cache_block *prev, Query_cache_block *pnext, Query_cache_block *pprev); - my_bool join_results(ulong join_limit); + my_bool join_results(size_t join_limit); /* Following function control structure_guard_mutex by themself or don't need structure_guard_mutex */ - ulong init_cache(); + size_t init_cache(); void make_disabled(); void free_cache(); - Query_cache_block *write_block_data(ulong data_len, uchar* data, - ulong header_len, + Query_cache_block *write_block_data(size_t data_len, uchar* data, + size_t header_len, Query_cache_block::block_type type, TABLE_COUNTER_TYPE ntab = 0); my_bool append_result_data(Query_cache_block **result, - ulong data_len, uchar* data, + size_t data_len, uchar* data, Query_cache_block *parent); my_bool write_result_data(Query_cache_block **result, - ulong data_len, uchar* data, + size_t data_len, uchar* data, Query_cache_block *parent, Query_cache_block::block_type type=Query_cache_block::RESULT); - inline ulong get_min_first_result_data_size(); - inline ulong get_min_append_result_data_size(); - Query_cache_block *allocate_block(ulong len, my_bool not_less, - ulong min); + inline size_t get_min_first_result_data_size(); + inline size_t get_min_append_result_data_size(); + Query_cache_block *allocate_block(size_t len, my_bool not_less, + size_t min); /* If query is cacheable return number tables in query (query without tables not cached) @@ -444,9 +447,9 @@ protected: static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used); public: - Query_cache(ulong query_cache_limit = ULONG_MAX, - ulong min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT, - ulong min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE, + Query_cache(size_t query_cache_limit = ULONG_MAX, + size_t min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT, + size_t min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE, uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE, uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE); @@ -457,11 +460,11 @@ protected: /* initialize cache (mutex) */ void init(); /* resize query cache (return real query size, 0 if disabled) */ - ulong resize(ulong query_cache_size); + size_t resize(size_t query_cache_size); /* set limit on result size */ - inline void result_size_limit(ulong limit){query_cache_limit=limit;} + inline void result_size_limit(size_t limit){query_cache_limit=limit;} /* set minimal result data allocation unit size */ - ulong set_min_res_unit(ulong size); + size_t set_min_res_unit(size_t size); /* register query in cache */ void store_query(THD *thd, TABLE_LIST *used_tables); @@ -478,7 +481,7 @@ protected: void invalidate(THD *thd, CHANGED_TABLE_LIST *tables_used); void invalidate_locked_for_write(THD *thd, TABLE_LIST *tables_used); void invalidate(THD *thd, TABLE *table, my_bool using_transactions); - void invalidate(THD *thd, const char *key, uint32 key_length, + void invalidate(THD *thd, const char *key, size_t key_length, my_bool using_transactions); /* Remove all queries that uses any of the tables in following database */ @@ -489,18 +492,18 @@ protected: void flush(); void pack(THD *thd, - ulong join_limit = QUERY_CACHE_PACK_LIMIT, + size_t join_limit = QUERY_CACHE_PACK_LIMIT, uint iteration_limit = QUERY_CACHE_PACK_ITERATION); void destroy(); void insert(THD *thd, Query_cache_tls *query_cache_tls, const char *packet, - ulong length, + size_t length, unsigned pkt_nr); - my_bool insert_table(THD *thd, uint key_len, const char *key, + my_bool insert_table(THD *thd, size_t key_len, const char *key, Query_cache_block_table *node, - uint32 db_length, uint8 suffix_length_arg, + size_t db_length, uint8 suffix_length_arg, uint8 cache_type, qc_engine_callback callback, ulonglong engine_data, @@ -559,8 +562,8 @@ struct Query_cache_query_flags sql_mode_t sql_mode; ulonglong max_sort_length; ulonglong group_concat_max_len; - ulong default_week_format; - ulong div_precision_increment; + size_t default_week_format; + size_t div_precision_increment; MY_LOCALE *lc_time_names; }; #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 1cd9cd19a7b..f10a5e51b59 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -28,7 +28,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" #include "sql_cache.h" // query_cache_abort @@ -121,8 +121,8 @@ extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry) bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && - !my_strcasecmp(system_charset_info, field_name.str, - other.field_name.str); + !lex_string_cmp(system_charset_info, &field_name, + &other.field_name); } /** @@ -138,7 +138,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root) columns(rhs.columns, mem_root), name(rhs.name), option_list(rhs.option_list), - generated(rhs.generated) + generated(rhs.generated), invisible(false) { list_copy_and_replace_each_value(columns, mem_root); } @@ -249,9 +249,9 @@ bool Foreign_key::validate(List<Create_field> &table_fields) { it.rewind(); while ((sql_field= it++) && - my_strcasecmp(system_charset_info, - column->field_name.str, - sql_field->field_name.str)) {} + lex_string_cmp(system_charset_info, + &column->field_name, + &sql_field->field_name)) {} if (!sql_field) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); @@ -553,7 +553,7 @@ char *thd_get_error_context_description(THD *thd, char *buffer, String str(buffer, length, &my_charset_latin1); const Security_context *sctx= &thd->main_security_ctx; char header[256]; - int len; + size_t len; /* The pointers thd->query and thd->proc_info might change since they are @@ -567,8 +567,8 @@ char *thd_get_error_context_description(THD *thd, char *buffer, const char *proc_info= thd->proc_info; len= my_snprintf(header, sizeof(header), - "MySQL thread id %lu, OS thread handle %lu, query id %lu", - (ulong) thd->thread_id, (ulong) thd->real_id, (ulong) thd->query_id); + "MySQL thread id %u, OS thread handle %lu, query id %llu", + (uint)thd->thread_id, (ulong) thd->real_id, (ulonglong) thd->query_id); str.length(0); str.append(header, len); @@ -705,8 +705,7 @@ extern "C" void thd_kill_timeout(THD* thd) thd->awake(KILL_TIMEOUT); } - -THD::THD(my_thread_id id, bool is_wsrep_applier) +THD::THD(my_thread_id id, bool is_wsrep_applier, bool skip_global_sys_var_lock) :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, /* statement id */ 0), rli_fake(0), rgi_fake(0), rgi_slave(NULL), @@ -820,6 +819,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; start_utime= utime_after_query= 0; + system_time= 0; + system_time_sec_part= 0; utime_after_lock= 0L; progress.arena= 0; progress.report_to_client= 0; @@ -840,7 +841,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) enable_slow_log= 0; durability_property= HA_REGULAR_DURABILITY; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS dbug_sentry=THD_SENTRY_MAGIC; #endif mysql_audit_init_thd(this); @@ -899,7 +900,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) /* Call to init() below requires fully initialized Open_tables_state. */ reset_open_tables_state(this); - init(); + init(skip_global_sys_var_lock); #if defined(ENABLED_PROFILING) profiling.set_thd(this); #endif @@ -1270,10 +1271,11 @@ const Type_handler *THD::type_handler_for_date() const Init common variables that has to be reset on start and on change_user */ -void THD::init(void) +void THD::init(bool skip_lock) { DBUG_ENTER("thd::init"); - mysql_mutex_lock(&LOCK_global_system_variables); + if (!skip_lock) + mysql_mutex_lock(&LOCK_global_system_variables); plugin_thdvar_init(this); /* plugin_thd_var_init() sets variables= global_system_variables, which @@ -1286,8 +1288,8 @@ void THD::init(void) ::strmake(default_master_connection_buff, global_system_variables.default_master_connection.str, variables.default_master_connection.length); - - mysql_mutex_unlock(&LOCK_global_system_variables); + if (!skip_lock) + mysql_mutex_unlock(&LOCK_global_system_variables); user_time.val= start_time= start_time_sec_part= 0; @@ -1479,6 +1481,75 @@ void THD::change_user(void) sp_cache_clear(&sp_func_cache); } +/** + Change default database + + @note This is coded to have as few instructions as possible under + LOCK_thd_data +*/ + +bool THD::set_db(const LEX_CSTRING *new_db) +{ + bool result= 0; + /* + Acquiring mutex LOCK_thd_data as we either free the memory allocated + for the database and reallocating the memory for the new db or memcpy + the new_db to the db. + */ + /* Do not reallocate memory if current chunk is big enough. */ + if (db.str && new_db->str && db.length >= new_db->length) + { + mysql_mutex_lock(&LOCK_thd_data); + db.length= new_db->length; + memcpy((char*) db.str, new_db->str, new_db->length+1); + mysql_mutex_unlock(&LOCK_thd_data); + } + else + { + const char *org_db= db.str; + const char *tmp= NULL; + if (new_db->str) + { + if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATALERROR)))) + result= 1; + } + + mysql_mutex_lock(&LOCK_thd_data); + db.str= tmp; + db.length= tmp ? new_db->length : 0; + mysql_mutex_unlock(&LOCK_thd_data); + my_free((char*) org_db); + } + PSI_CALL_set_thread_db(db.str, (int) db.length); + return result; +} + + +/** + Set the current database + + @param new_db a pointer to the new database name. + @param new_db_len length of the new database name. + + @note This operation just sets {db, db_length}. Switching the current + database usually involves other actions, like switching other database + attributes including security context. In the future, this operation + will be made private and more convenient interface will be provided. +*/ + +void THD::reset_db(const LEX_CSTRING *new_db) +{ + if (new_db->str != db.str || new_db->length != db.length) + { + if (db.str != 0) + DBUG_PRINT("QQ", ("Overwriting: %p", db.str)); + mysql_mutex_lock(&LOCK_thd_data); + db= *new_db; + mysql_mutex_unlock(&LOCK_thd_data); + PSI_CALL_set_thread_db(db.str, (int) db.length); + } +} + /* Do operations that may take a long time */ @@ -1557,8 +1628,8 @@ void THD::cleanup(void) void THD::free_connection() { DBUG_ASSERT(free_connection_done == 0); - my_free(db); - db= NULL; + my_free((char*) db.str); + db= null_clex_str; #ifndef EMBEDDED_LIBRARY if (net.vio) vio_delete(net.vio); @@ -1652,7 +1723,7 @@ THD::~THD() mysql_mutex_destroy(&LOCK_wakeup_ready); mysql_mutex_destroy(&LOCK_thd_data); mysql_mutex_destroy(&LOCK_thd_kill); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS dbug_sentry= THD_SENTRY_GONE; #endif #ifndef EMBEDDED_LIBRARY @@ -1733,6 +1804,9 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) to_var->binlog_bytes_written+= from_var->binlog_bytes_written; to_var->cpu_time+= from_var->cpu_time; to_var->busy_time+= from_var->busy_time; + to_var->table_open_cache_hits+= from_var->table_open_cache_hits; + to_var->table_open_cache_misses+= from_var->table_open_cache_misses; + to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows; /* Update global_memory_used. We have to do this with atomic_add as the @@ -1784,6 +1858,12 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, dec_var->binlog_bytes_written; to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time; to_var->busy_time+= from_var->busy_time - dec_var->busy_time; + to_var->table_open_cache_hits+= from_var->table_open_cache_hits - + dec_var->table_open_cache_hits; + to_var->table_open_cache_misses+= from_var->table_open_cache_misses - + dec_var->table_open_cache_misses; + to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows - + dec_var->table_open_cache_overflows; /* We don't need to accumulate memory_used as these are not reset or used by @@ -2069,6 +2149,23 @@ int THD::killed_errno() } +void THD::reset_killed() +{ + /* + Resetting killed has to be done under a mutex to ensure + its not done during an awake() call. + */ + DBUG_ENTER("reset_killed"); + if (killed != NOT_KILLED) + { + mysql_mutex_lock(&LOCK_thd_kill); + killed= NOT_KILLED; + killed_err= 0; + mysql_mutex_unlock(&LOCK_thd_kill); + } + DBUG_VOID_RETURN; +} + /* Remember the location of thread info, the structure needed for the structure for the net buffer @@ -2265,7 +2362,7 @@ void THD::cleanup_after_query() */ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, - const char *from, uint from_length, + const char *from, size_t from_length, CHARSET_INFO *from_cs) { DBUG_ENTER("THD::convert_string"); @@ -2292,7 +2389,7 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, dstcs and srccs cannot be &my_charset_bin. */ bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, - CHARSET_INFO *srccs, const char *src, uint src_length, + CHARSET_INFO *srccs, const char *src, size_t src_length, String_copier *status) { DBUG_ENTER("THD::convert_fix"); @@ -2310,7 +2407,7 @@ bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, Copy or convert a string. */ bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, - CHARSET_INFO *srccs, const char *src, uint src_length, + CHARSET_INFO *srccs, const char *src, size_t src_length, String_copier *status) { DBUG_ENTER("THD::copy_fix"); @@ -2327,7 +2424,7 @@ bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, class String_copier_with_error: public String_copier { public: - bool check_errors(CHARSET_INFO *srccs, const char *src, uint src_length) + bool check_errors(CHARSET_INFO *srccs, const char *src, size_t src_length) { if (most_important_error_pos()) { @@ -2342,7 +2439,7 @@ public: bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, CHARSET_INFO *srccs, - const char *src, uint src_length) + const char *src, size_t src_length) { String_copier_with_error status; return convert_fix(dstcs, dst, srccs, src, src_length, &status) || @@ -2352,7 +2449,7 @@ bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, CHARSET_INFO *srccs, - const char *src, uint src_length) + const char *src, size_t src_length) { String_copier_with_error status; return copy_fix(dstcs, dst, srccs, src, src_length, &status) || @@ -2407,7 +2504,7 @@ Item *THD::make_string_literal(const char *str, size_t length, str= to.str; length= to.length; } - return new (mem_root) Item_string(this, str, length, + return new (mem_root) Item_string(this, str, (uint)length, variables.collation_connection, DERIVATION_COERCIBLE, repertoire); } @@ -2419,7 +2516,7 @@ Item *THD::make_string_literal_nchar(const Lex_string_with_metadata_st &str) if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL)) return new (mem_root) Item_null(this, 0, national_charset_info); - return new (mem_root) Item_string(this, str.str, str.length, + return new (mem_root) Item_string(this, str.str, (uint)str.length, national_charset_info, DERIVATION_COERCIBLE, str.repertoire()); @@ -2432,7 +2529,7 @@ Item *THD::make_string_literal_charset(const Lex_string_with_metadata_st &str, if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL)) return new (mem_root) Item_null(this, 0, cs); return new (mem_root) Item_string_with_introducer(this, - str.str, str.length, cs); + str.str, (uint)str.length, cs); } @@ -2445,7 +2542,7 @@ Item *THD::make_string_literal_concat(Item *item, const LEX_CSTRING &str) { CHARSET_INFO *cs= variables.collation_connection; uint repertoire= my_string_repertoire(cs, str.str, str.length); - return new (mem_root) Item_string(this, str.str, str.length, cs, + return new (mem_root) Item_string(this, str.str, (uint)str.length, cs, DERIVATION_COERCIBLE, repertoire); } return item; @@ -2453,7 +2550,7 @@ Item *THD::make_string_literal_concat(Item *item, const LEX_CSTRING &str) DBUG_ASSERT(item->type() == Item::STRING_ITEM); DBUG_ASSERT(item->basic_const_item()); - static_cast<Item_string*>(item)->append(str.str, str.length); + static_cast<Item_string*>(item)->append(str.str, (uint)str.length); if (!(item->collation.repertoire & MY_REPERTOIRE_EXTENDED)) { // If the string has been pure ASCII so far, check the new part. @@ -2515,7 +2612,7 @@ void THD::add_changed_table(TABLE *table) } -void THD::add_changed_table(const char *key, long key_length) +void THD::add_changed_table(const char *key, size_t key_length) { DBUG_ENTER("THD::add_changed_table(key)"); CHANGED_TABLE_LIST **prev_changed = &transaction.changed_tables; @@ -2528,7 +2625,7 @@ void THD::add_changed_table(const char *key, long key_length) { list_include(prev_changed, curr, changed_table_dup(key, key_length)); DBUG_PRINT("info", - ("key_length: %ld %u", key_length, + ("key_length: %zu %zu", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } @@ -2539,7 +2636,7 @@ void THD::add_changed_table(const char *key, long key_length) { list_include(prev_changed, curr, changed_table_dup(key, key_length)); DBUG_PRINT("info", - ("key_length: %ld %u", key_length, + ("key_length: %zu %zu", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } @@ -2551,13 +2648,13 @@ void THD::add_changed_table(const char *key, long key_length) } } *prev_changed = changed_table_dup(key, key_length); - DBUG_PRINT("info", ("key_length: %ld %u", key_length, + DBUG_PRINT("info", ("key_length: %zu %zu", key_length, (*prev_changed)->key_length)); DBUG_VOID_RETURN; } -CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length) +CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length) { CHANGED_TABLE_LIST* new_table = (CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+ @@ -3021,8 +3118,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, if (!dirname_length(exchange->file_name)) { - strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->db ? thd->db : "", - NullS); + strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->get_db(), NullS); (void) fn_format(path, exchange->file_name, path, "", option); } else @@ -3696,6 +3792,7 @@ void Query_arena::set_query_arena(Query_arena *set) mem_root= set->mem_root; free_list= set->free_list; state= set->state; + is_stored_procedure= set->is_stored_procedure; } @@ -3712,12 +3809,11 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, enum enum_state state_arg, ulong id_arg) :Query_arena(mem_root_arg, state_arg), id(id_arg), - mark_used_columns(MARK_COLUMNS_READ), + column_usage(MARK_COLUMNS_READ), lex(lex_arg), - db(NULL), - db_length(0) + db(null_clex_str) { - name.str= NULL; + name= null_clex_str; } @@ -3730,7 +3826,7 @@ Query_arena::Type Statement::type() const void Statement::set_statement(Statement *stmt) { id= stmt->id; - mark_used_columns= stmt->mark_used_columns; + column_usage= stmt->column_usage; stmt_lex= lex= stmt->lex; query_string= stmt->query_string; } @@ -3783,7 +3879,7 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup) backup->set_query_arena(this); set_query_arena(set); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS backup->is_backup_arena= TRUE; #endif DBUG_VOID_RETURN; @@ -3802,7 +3898,7 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup) DBUG_ASSERT(backup->is_backup_arena); set->set_query_arena(this); set_query_arena(backup); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS backup->is_backup_arena= FALSE; #endif DBUG_VOID_RETURN; @@ -4054,7 +4150,7 @@ bool select_materialize_with_stats:: create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, - const char *table_alias, bool bit_fields_as_long, + const LEX_CSTRING *table_alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, uint hidden) @@ -4065,7 +4161,7 @@ create_result_table(THD *thd_arg, List<Item> *column_types, if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, is_union_distinct, 1, - options, HA_POS_ERROR, (char*) table_alias, + options, HA_POS_ERROR, table_alias, !create_table, keep_row_order))) return TRUE; @@ -4166,7 +4262,7 @@ void TMP_TABLE_PARAM::init() } -void thd_increment_bytes_sent(void *thd, ulong length) +void thd_increment_bytes_sent(void *thd, size_t length) { /* thd == 0 when close_connection() calls net_send_error() */ if (likely(thd != 0)) @@ -4182,15 +4278,10 @@ my_bool thd_net_is_killed() } -void thd_increment_bytes_received(void *thd, ulong length) -{ - ((THD*) thd)->status_var.bytes_received+= length; -} - - -void thd_increment_net_big_packet_count(void *thd, ulong length) +void thd_increment_bytes_received(void *thd, size_t length) { - ((THD*) thd)->status_var.net_big_packet_count+= length; + if (thd != NULL) // MDEV-13073 Ack collector having NULL + ((THD*) thd)->status_var.bytes_received+= length; } @@ -4198,6 +4289,12 @@ void THD::set_status_var_init() { bzero((char*) &status_var, offsetof(STATUS_VAR, last_cleared_system_status_var)); + /* + Session status for Threads_running is always 1. It can only be queried + by thread itself via INFORMATION_SCHEMA.SESSION_STATUS or SHOW [SESSION] + STATUS. And at this point thread is guaranteed to be running. + */ + status_var.threads_running= 1; } @@ -4633,8 +4730,10 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen, Open_table_context ot_ctx(thd, 0); TABLE_LIST *tl= (TABLE_LIST*)thd->alloc(sizeof(TABLE_LIST)); + LEX_CSTRING db_name= {db, dblen }; + LEX_CSTRING table_name= { tb, tblen }; - tl->init_one_table(db, dblen, tb, tblen, tb, TL_READ); + tl->init_one_table(&db_name, &table_name, 0, TL_READ); tl->i_s_requested_object= OPEN_TABLE_ONLY; bool error= open_table(thd, tl, &ot_ctx); @@ -4671,7 +4770,7 @@ TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len, { if (t->s->db.length == db_len && t->s->table_name.length == table_len && !strcmp(t->s->db.str, db) && !strcmp(t->s->table_name.str, table) && - t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::FK) + t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_FK) return t; } return NULL; @@ -4696,7 +4795,6 @@ void destroy_thd(MYSQL_THD thd) thd->add_status_to_global(); unlink_not_visible_thd(thd); delete thd; - dec_thread_running(); } void reset_thd(MYSQL_THD thd) @@ -4763,18 +4861,11 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd) return(thd->slave_thread); } -/* Returns true for a worker thread in parallel replication. */ -extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd) -{ - return thd->rgi_slave && thd->rgi_slave->is_parallel_exec; -} - - /* Returns high resolution timestamp for the start of the current query. */ extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd) { - return thd->start_utime; + return thd->start_time * 1000000 + thd->start_time_sec_part; } @@ -4807,7 +4898,7 @@ thd_need_wait_reports(const MYSQL_THD thd) } /* - Used by storage engines (currently TokuDB and InnoDB/XtraDB) to report that + Used by storage engines (currently TokuDB and InnoDB) to report that one transaction THD is about to go to wait for a transactional lock held by another transactions OTHER_THD. @@ -4829,7 +4920,7 @@ thd_need_wait_reports(const MYSQL_THD thd) transaction, and later re-try it, to resolve the deadlock. This call need only receive reports about waits for locks that will remain - until the holding transaction commits. InnoDB/XtraDB auto-increment locks, + until the holding transaction commits. InnoDB auto-increment locks, for example, are released earlier, and so need not be reported. (Such false positives are not harmful, but could lead to unnecessary kill and retry, so best avoided). @@ -4882,7 +4973,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd) } /* - This function is called from InnoDB/XtraDB to check if the commit order of + This function is called from InnoDB to check if the commit order of two transactions has already been decided by the upper layer. This happens in parallel replication, where the commit order is forced to be the same on the slave as it was originally on the master. @@ -4912,7 +5003,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd) If this function returns true, normal locking should be done as required by the binlogging and transaction isolation level in effect. But if it returns - false, the correct order will be enforced anyway, and InnoDB/XtraDB can + false, the correct order will be enforced anyway, and InnoDB can avoid taking the gap lock, preventing the lock conflict. Calling this function is just an optimisation to avoid unnecessary @@ -5035,7 +5126,7 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd) { - return binlog_filter->db_ok(thd->db); + return binlog_filter->db_ok(thd->db.str); } /* @@ -5191,10 +5282,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->count_cuted_fields= count_cuted_fields; backup->in_sub_stmt= in_sub_stmt; backup->enable_slow_log= enable_slow_log; - backup->query_plan_flags= query_plan_flags; backup->limit_found_rows= limit_found_rows; - backup->examined_row_count= m_examined_row_count; - backup->sent_row_count= m_sent_row_count; backup->cuted_fields= cuted_fields; backup->client_capabilities= client_capabilities; backup->savepoints= transaction.savepoints; @@ -5202,6 +5290,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, first_successful_insert_id_in_prev_stmt; backup->first_successful_insert_id_in_cur_stmt= first_successful_insert_id_in_cur_stmt; + store_slow_query_state(backup); if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !is_current_stmt_binlog_format_row()) @@ -5217,14 +5306,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; - m_examined_row_count= 0; - m_sent_row_count= 0; cuted_fields= 0; transaction.savepoints= 0; first_successful_insert_id_in_cur_stmt= 0; + reset_slow_query_state(); } - void THD::restore_sub_statement_state(Sub_statement_state *backup) { DBUG_ENTER("THD::restore_sub_statement_state"); @@ -5259,7 +5346,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) variables.option_bits= backup->option_bits; in_sub_stmt= backup->in_sub_stmt; enable_slow_log= backup->enable_slow_log; - query_plan_flags= backup->query_plan_flags; first_successful_insert_id_in_prev_stmt= backup->first_successful_insert_id_in_prev_stmt; first_successful_insert_id_in_cur_stmt= @@ -5267,6 +5353,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) limit_found_rows= backup->limit_found_rows; set_sent_row_count(backup->sent_row_count); client_capabilities= backup->client_capabilities; + + /* Restore statistic needed for slow log */ + add_slow_query_state(backup); + /* If we've left sub-statement mode, reset the fatal error flag. Otherwise keep the current value, to propagate it up the sub-statement @@ -5291,6 +5381,56 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) DBUG_VOID_RETURN; } +/* + Store slow query state at start of a stored procedure statment +*/ + +void THD::store_slow_query_state(Sub_statement_state *backup) +{ + backup->affected_rows= affected_rows; + backup->bytes_sent_old= bytes_sent_old; + backup->examined_row_count= m_examined_row_count; + backup->query_plan_flags= query_plan_flags; + backup->query_plan_fsort_passes= query_plan_fsort_passes; + backup->sent_row_count= m_sent_row_count; + backup->tmp_tables_disk_used= tmp_tables_disk_used; + backup->tmp_tables_size= tmp_tables_size; + backup->tmp_tables_used= tmp_tables_used; +} + +/* Reset variables related to slow query log */ + +void THD::reset_slow_query_state() +{ + affected_rows= 0; + bytes_sent_old= status_var.bytes_sent; + m_examined_row_count= 0; + m_sent_row_count= 0; + query_plan_flags= QPLAN_INIT; + query_plan_fsort_passes= 0; + tmp_tables_disk_used= 0; + tmp_tables_size= 0; + tmp_tables_used= 0; +} + +/* + Add back the stored values to the current counters to be able to get + right status for 'call procedure_name' +*/ + +void THD::add_slow_query_state(Sub_statement_state *backup) +{ + affected_rows+= backup->affected_rows; + bytes_sent_old= backup->bytes_sent_old; + m_examined_row_count+= backup->examined_row_count; + m_sent_row_count+= backup->sent_row_count; + query_plan_flags|= backup->query_plan_flags; + query_plan_fsort_passes+= backup->query_plan_fsort_passes; + tmp_tables_disk_used+= backup->tmp_tables_disk_used; + tmp_tables_size+= backup->tmp_tables_size; + tmp_tables_used+= backup->tmp_tables_used; +} + void THD::set_statement(Statement *stmt) { @@ -5325,6 +5465,8 @@ void THD::inc_examined_row_count(ha_rows count) void THD::inc_status_created_tmp_disk_tables() { + tmp_tables_disk_used++; + query_plan_flags|= QPLAN_TMP_DISK; status_var_increment(status_var.created_tmp_disk_tables_); #ifdef HAVE_PSI_STATEMENT_INTERFACE PSI_STATEMENT_CALL(inc_statement_created_tmp_disk_tables)(m_statement_psi, 1); @@ -5333,6 +5475,8 @@ void THD::inc_status_created_tmp_disk_tables() void THD::inc_status_created_tmp_tables() { + tmp_tables_used++; + query_plan_flags|= QPLAN_TMP_TABLE; status_var_increment(status_var.created_tmp_tables_); #ifdef HAVE_PSI_STATEMENT_INTERFACE PSI_STATEMENT_CALL(inc_statement_created_tmp_tables)(m_statement_psi, 1); @@ -5884,7 +6028,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) */ if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) && !(wsrep_binlog_format() == BINLOG_FORMAT_STMT && - !binlog_filter->db_ok(db))) + !binlog_filter->db_ok(db.str))) { if (is_bulk_op()) @@ -5987,9 +6131,9 @@ int THD::decide_logging_format(TABLE_LIST *tables) handler::Table_flags const flags= table->table->file->ha_table_flags(); DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx", - table->table_name, flags)); + table->table_name.str, flags)); - if (table->table->no_replicate) + if (table->table->s->no_replicate) { /* The statement uses a table that is not replicated. @@ -6019,7 +6163,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) replicated_tables_count++; if (table->lock_type <= TL_READ_NO_INSERT && - table->prelocking_placeholder != TABLE_LIST::FK) + table->prelocking_placeholder != TABLE_LIST::PRELOCK_FK) has_read_tables= true; else if (table->table->found_next_number_field && (table->lock_type >= TL_WRITE_ALLOW_WRITE)) @@ -6291,7 +6435,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (table->table->file->ht->db_type == DB_TYPE_BLACKHOLE_DB && table->lock_type >= TL_WRITE_ALLOW_WRITE) { - table_names.append(table->table_name); + table_names.append(&table->table_name); table_names.append(","); } } @@ -6323,7 +6467,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) mysql_bin_log.is_open(), (variables.option_bits & OPTION_BIN_LOG), (uint) wsrep_binlog_format(), - binlog_filter->db_ok(db))); + binlog_filter->db_ok(db.str))); #endif DBUG_RETURN(0); @@ -6503,7 +6647,7 @@ CPP_UNNAMED_NS_START { DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr)); DBUG_ASSERT(m_ptr[s] != 0); - DBUG_ASSERT(m_alloc_checked == TRUE); + DBUG_SLOW_ASSERT(m_alloc_checked == TRUE); return m_ptr[s]; } @@ -6992,6 +7136,15 @@ static bool protect_against_unsafe_warning_flood(int unsafe_type) DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]); } +MYSQL_TIME THD::query_start_TIME() +{ + MYSQL_TIME res; + variables.time_zone->gmt_sec_to_TIME(&res, query_start()); + res.second_part= query_start_sec_part(); + time_zone_used= 1; + return res; +} + /** Auxiliary method used by @c binlog_query() to raise warnings. @@ -7611,3 +7764,16 @@ void Database_qualified_name::copy(MEM_ROOT *mem_root, #endif /* !defined(MYSQL_CLIENT) */ + + +Query_arena_stmt::Query_arena_stmt(THD *_thd) : + thd(_thd) +{ + arena= thd->activate_stmt_arena_if_needed(&backup); +} + +Query_arena_stmt::~Query_arena_stmt() +{ + if (arena) + thd->restore_active_arena(arena, &backup); +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 0edf61ce7d5..4722f3f5989 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -20,7 +20,6 @@ /* Classes in mysql */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "dur_prop.h" #include <waiting_threads.h> #include "sql_const.h" @@ -38,6 +37,7 @@ #include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */ #include "thr_timer.h" #include "thr_malloc.h" +#include "log_slow.h" /* LOG_SLOW_DISABLE_... */ #include "sql_digest_stream.h" // sql_digest_state @@ -92,8 +92,25 @@ enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO, SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING}; enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY, SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY}; -enum enum_mark_columns -{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; + +/* + MARK_COLUMNS_READ: A column is goind to be read. + MARK_COLUMNS_WRITE: A column is going to be written to. + MARK_COLUMNS_READ: A column is goind to be read. + A bit in read set is set to inform handler that the field + is to be read. If field list contains duplicates, then + thd->dup_field is set to point to the last found + duplicate. + MARK_COLUMNS_WRITE: A column is going to be written to. + A bit is set in write set to inform handler that it needs + to update this field in write_row and update_row. +*/ +enum enum_column_usage +{ COLUMNS_READ, COLUMNS_WRITE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; + +static inline bool should_mark_column(enum_column_usage column_usage) +{ return column_usage >= MARK_COLUMNS_READ; } + enum enum_filetype { FILETYPE_CSV, FILETYPE_XML }; enum enum_binlog_row_image { @@ -140,6 +157,7 @@ enum enum_binlog_row_image { #define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30) #define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31) #define MODE_EMPTY_STRING_IS_NULL (1ULL << 32) +#define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33) /* Bits for different old style modes */ #define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0) @@ -148,7 +166,6 @@ enum enum_binlog_row_image { extern char internal_table_name[2]; extern char empty_c_string[1]; -extern LEX_STRING EMPTY_STR; extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern bool volatile shutdown_in_progress; @@ -195,7 +212,7 @@ typedef struct st_user_var_events { user_var_entry *user_var_event; char *value; - ulong length; + size_t length; Item_result type; uint charset_number; bool unsigned_flag; @@ -284,8 +301,9 @@ class Alter_column :public Sql_alloc { public: const char *name; Virtual_column_info *default_value; - Alter_column(const char *par_name, Virtual_column_info *expr) - :name(par_name), default_value(expr) {} + bool alter_if_exists; + Alter_column(const char *par_name, Virtual_column_info *expr, bool par_exists) + :name(par_name), default_value(expr), alter_if_exists(par_exists) {} /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone @@ -304,22 +322,25 @@ public: LEX_CSTRING name; engine_option_value *option_list; bool generated; + bool invisible; Key(enum Keytype type_par, const LEX_CSTRING *name_arg, ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(default_key_create_info), - name(*name_arg), option_list(NULL), generated(generated_arg) + name(*name_arg), option_list(NULL), generated(generated_arg), + invisible(false) { key_create_info.algorithm= algorithm_arg; - } + } Key(enum Keytype type_par, const LEX_CSTRING *name_arg, KEY_CREATE_INFO *key_info_arg, bool generated_arg, List<Key_part_spec> *cols, engine_option_value *create_opt, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(*key_info_arg), columns(*cols), - name(*name_arg), option_list(create_opt), generated(generated_arg) + name(*name_arg), option_list(create_opt), generated(generated_arg), + invisible(false) {} Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} @@ -370,8 +391,9 @@ public: typedef struct st_mysql_lock { TABLE **table; - uint table_count,lock_count; THR_LOCK_DATA **locks; + uint table_count,lock_count; + uint flags; } MYSQL_LOCK; @@ -439,7 +461,7 @@ typedef enum enum_diag_condition_item_name Name of each diagnostic condition item. This array is indexed by Diag_condition_item_name. */ -extern const LEX_STRING Diag_condition_item_names[]; +extern const LEX_CSTRING Diag_condition_item_names[]; /** These states are bit coded with HARD. For each state there must be a pair @@ -537,6 +559,8 @@ typedef struct system_variables ulonglong join_buff_space_limit; ulonglong log_slow_filter; ulonglong log_slow_verbosity; + ulonglong log_slow_disabled_statements; + ulonglong log_disabled_statements; ulonglong bulk_insert_buff_size; ulonglong join_buff_size; ulonglong sortbuff_size; @@ -555,6 +579,7 @@ typedef struct system_variables ha_rows max_join_size; ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; + ulong column_compression_zlib_strategy; ulong lock_wait_timeout; ulong join_cache_level; ulong max_allowed_packet; @@ -642,6 +667,7 @@ typedef struct system_variables my_bool sql_log_bin_off; my_bool binlog_annotate_row_events; my_bool binlog_direct_non_trans_update; + my_bool column_compression_zlib_wrap; plugin_ref table_plugin; plugin_ref tmp_table_plugin; @@ -691,6 +717,17 @@ typedef struct system_variables my_bool session_track_state_change; ulong threadpool_priority; + + uint idle_transaction_timeout; + uint idle_readonly_transaction_timeout; + uint idle_write_transaction_timeout; + uint column_compression_threshold; + uint column_compression_zlib_level; + uint in_subquery_conversion_threshold; + + vers_asof_timestamp_t vers_asof_timestamp; + my_bool vers_innodb_algorithm_simple; + ulong vers_alter_history; } SV; /** @@ -701,6 +738,8 @@ typedef struct system_variables typedef struct system_status_var { + ulong column_compressions; + ulong column_decompressions; ulong com_stat[(uint) SQLCOM_END]; ulong com_create_tmp_table; ulong com_drop_tmp_table; @@ -754,7 +793,6 @@ typedef struct system_status_var ulong ha_savepoint_rollback_count; ulong ha_external_lock_count; - ulong net_big_packet_count; ulong opened_tables; ulong opened_shares; ulong opened_views; /* +1 opening a view */ @@ -778,6 +816,7 @@ typedef struct system_status_var ulong feature_dynamic_columns; /* +1 when creating a dynamic column */ ulong feature_fulltext; /* +1 when MATCH is used */ ulong feature_gis; /* +1 opening a table with GIS features */ + ulong feature_invisible_columns; /* +1 opening a table with invisible column */ ulong feature_locale; /* +1 when LOCALE is set */ ulong feature_subquery; /* +1 when subqueries are used */ ulong feature_timezone; /* +1 when XPATH is used */ @@ -810,8 +849,12 @@ typedef struct system_status_var ulonglong rows_sent; ulonglong rows_tmp_read; ulonglong binlog_bytes_written; + ulonglong table_open_cache_hits; + ulonglong table_open_cache_misses; + ulonglong table_open_cache_overflows; double last_query_cost; double cpu_time, busy_time; + uint32 threads_running; /* Don't initialize */ /* Memory used for thread local storage */ int64 max_local_memory_used; @@ -892,7 +935,7 @@ void free_tmp_table(THD *thd, TABLE *entry); /* The following macro is to make init of Query_arena simpler */ -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS #define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE; #else #define INIT_ARENA_DBUG_INFO @@ -907,7 +950,7 @@ public: */ Item *free_list; MEM_ROOT *mem_root; // Pointer to current memroot -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool is_backup_arena; /* True if this arena is used for backup. */ bool is_reprepared; #endif @@ -926,6 +969,11 @@ public: enum_state state; +protected: + friend class sp_head; + bool is_stored_procedure; + +public: /* We build without RTTI, so dynamic_cast can't be used. */ enum Type { @@ -933,7 +981,8 @@ public: }; Query_arena(MEM_ROOT *mem_root_arg, enum enum_state state_arg) : - free_list(0), mem_root(mem_root_arg), state(state_arg) + free_list(0), mem_root(mem_root_arg), state(state_arg), + is_stored_procedure(state_arg == STMT_INITIALIZED_FOR_SP ? true : false) { INIT_ARENA_DBUG_INFO; } /* This constructor is used only when Query_arena is created as @@ -953,6 +1002,8 @@ public: { return state == STMT_PREPARED || state == STMT_EXECUTED; } inline bool is_conventional() const { return state == STMT_CONVENTIONAL_EXECUTION; } + inline bool is_sp_execute() const + { return is_stored_procedure; } inline void* alloc(size_t size) { return alloc_root(mem_root,size); } inline void* calloc(size_t size) @@ -968,7 +1019,7 @@ public: { return strmake_root(mem_root,str,size); } inline void *memdup(const void *str, size_t size) { return memdup_root(mem_root,str,size); } - inline void *memdup_w_gap(const void *str, size_t size, uint gap) + inline void *memdup_w_gap(const void *str, size_t size, size_t gap) { void *ptr; if ((ptr= alloc_root(mem_root,size+gap))) @@ -997,6 +1048,22 @@ public: }; +class Query_arena_stmt +{ + THD *thd; + Query_arena backup; + Query_arena *arena; + +public: + Query_arena_stmt(THD *_thd); + ~Query_arena_stmt(); + bool arena_replaced() + { + return arena != NULL; + } +}; + + class Server_side_cursor; /** @@ -1026,18 +1093,7 @@ public: */ ulong id; - /* - MARK_COLUMNS_NONE: Means mark_used_colums is not set and no indicator to - handler of fields used is set - MARK_COLUMNS_READ: Means a bit in read set is set to inform handler - that the field is to be read. If field list contains - duplicates, then thd->dup_field is set to point - to the last found duplicate. - MARK_COLUMNS_WRITE: Means a bit is set in write set to inform handler - that it needs to update this field in write_row - and update_row. - */ - enum enum_mark_columns mark_used_columns; + enum enum_column_usage column_usage; LEX_CSTRING name; /* name for named prepared statements */ LEX *lex; // parse tree descriptor @@ -1106,18 +1162,13 @@ public: /** Name of the current (default) database. - If there is the current (default) database, "db" contains its name. If - there is no current (default) database, "db" is NULL and "db_length" is - 0. In other words, "db", "db_length" must either be NULL, or contain a + If there is the current (default) database, "db.str" contains its name. If + there is no current (default) database, "db.str" is NULL and "db.length" is + 0. In other words, db must either be NULL, or contain a valid database name. - - @note this attribute is set and alloced by the slave SQL thread (for - the THD of that thread); that thread is (and must remain, for now) the - only responsible for freeing this member. */ - char *db; - size_t db_length; + LEX_CSTRING db; /* This is set to 1 of last call to send_result_to_client() was ok */ my_bool query_cache_is_applicable; @@ -1585,19 +1636,25 @@ public: class Sub_statement_state { public: + Discrete_interval auto_inc_interval_for_cur_row; + Discrete_intervals_list auto_inc_intervals_forced; + SAVEPOINT *savepoints; ulonglong option_bits; ulonglong first_successful_insert_id_in_prev_stmt; ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row; - Discrete_interval auto_inc_interval_for_cur_row; - Discrete_intervals_list auto_inc_intervals_forced; ulonglong limit_found_rows; - ha_rows cuted_fields, sent_row_count, examined_row_count; + ulonglong tmp_tables_size; ulonglong client_capabilities; + ulonglong cuted_fields, sent_row_count, examined_row_count; + ulonglong affected_rows; + ulonglong bytes_sent_old; + ulong tmp_tables_used; + ulong tmp_tables_disk_used; + ulong query_plan_fsort_passes; ulong query_plan_flags; uint in_sub_stmt; /* 0, SUB_STMT_TRIGGER or SUB_STMT_FUNCTION */ bool enable_slow_log; bool last_insert_id_used; - SAVEPOINT *savepoints; enum enum_check_fields count_cuted_fields; }; @@ -1613,7 +1670,8 @@ enum enum_thread_type SYSTEM_THREAD_EVENT_WORKER= 16, SYSTEM_THREAD_BINLOG_BACKGROUND= 32, SYSTEM_THREAD_SLAVE_BACKGROUND= 64, - SYSTEM_THREAD_GENERIC= 128 + SYSTEM_THREAD_GENERIC= 128, + SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND= 256 }; inline char const * @@ -1629,6 +1687,7 @@ show_system_thread(enum_thread_type thread) RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND); + RETURN_NAME_AS_STRING(SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND); default: sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread); return buf; @@ -1809,8 +1868,9 @@ private: class Locked_tables_list { -private: +public: MEM_ROOT m_locked_tables_root; +private: TABLE_LIST *m_locked_tables; TABLE_LIST **m_locked_tables_last; /** An auxiliary array used only in reopen_tables(). */ @@ -2089,13 +2149,27 @@ struct wait_for_commit void reinit(); }; +/* + Structure to store the start time for a query +*/ -extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); +struct QUERY_START_TIME_INFO +{ + my_time_t start_time; + ulong start_time_sec_part; + ulonglong start_utime, utime_after_lock; -class THD; -#ifndef DBUG_OFF -void dbug_serve_apcs(THD *thd, int n_calls); -#endif + void backup_query_start_time(QUERY_START_TIME_INFO *backup) + { + *backup= *this; + } + void restore_query_start_time(QUERY_START_TIME_INFO *backup) + { + *this= *backup; + } +}; + +extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); /** @class THD @@ -2113,7 +2187,8 @@ class THD :public Statement, */ public Item_change_list, public MDL_context_owner, - public Open_tables_state + public Open_tables_state, + public QUERY_START_TIME_INFO { private: inline bool is_stmt_prepare() const @@ -2206,7 +2281,7 @@ public: Protects THD data accessed from other threads: - thd->query and thd->query_length (used by SHOW ENGINE INNODB STATUS and SHOW PROCESSLIST - - thd->db and thd->db_length (used in SHOW PROCESSLIST) + - thd->db (used in SHOW PROCESSLIST) Is locked when THD is deleted. */ mysql_mutex_t LOCK_thd_data; @@ -2309,7 +2384,8 @@ public: /* Needed by MariaDB semi sync replication */ Trans_binlog_info *semisync_info; - + /* If this is a semisync slave connection. */ + bool semi_sync_slave; ulonglong client_capabilities; /* What the client supports */ ulong max_client_packet_length; @@ -2322,7 +2398,7 @@ public: HASH ull_hash; /* Hash of used seqeunces (for PREVIOUS value) */ HASH sequences; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; @@ -2337,12 +2413,12 @@ public: uint32 file_id; // for LOAD DATA INFILE /* remote (peer) port */ uint16 peer_port; - my_time_t start_time; // start_time and its sec_part - ulong start_time_sec_part; // are almost always used separately my_hrtime_t user_time; // track down slow pthread_create ulonglong prior_thr_create_utime, thr_create_utime; - ulonglong start_utime, utime_after_lock, utime_after_query; + ulonglong utime_after_query; + my_time_t system_time; + ulong system_time_sec_part; // Process indicator struct { @@ -2796,6 +2872,14 @@ public: { m_row_count_func= row_count_func; } + inline void set_affected_rows(longlong row_count_func) + { + /* + We have to add to affected_rows (used by slow log), as otherwise + information for 'call' will be wrong + */ + affected_rows+= (row_count_func >= 0 ? row_count_func : 0); + } ha_rows cuted_fields; @@ -2825,6 +2909,9 @@ public: ha_rows get_examined_row_count() const { return m_examined_row_count; } + ulonglong get_affected_rows() const + { return affected_rows; } + void set_sent_row_count(ha_rows count); void set_examined_row_count(ha_rows count); @@ -2902,8 +2989,16 @@ public: /* Statement id is thread-wide. This counter is used to generate ids */ ulong statement_id_counter; ulong rand_saved_seed1, rand_saved_seed2; + + /* The following variables are used when printing to slow log */ ulong query_plan_flags; ulong query_plan_fsort_passes; + ulong tmp_tables_used; + ulong tmp_tables_disk_used; + ulonglong tmp_tables_size; + ulonglong bytes_sent_old; + ulonglong affected_rows; /* Number of changed rows */ + pthread_t real_id; /* For debugging */ my_thread_id thread_id, thread_dbug_id; uint32 os_thread_id; @@ -2992,7 +3087,6 @@ public: uint8 failed_com_change_user; bool slave_thread; bool extra_port; /* If extra connection */ - bool no_errors; /** @@ -3036,7 +3130,7 @@ public: */ bool charset_is_system_charset, charset_is_collation_connection; bool charset_is_character_set_filesystem; - bool enable_slow_log; /* enable slow log for current statement */ + bool enable_slow_log; /* Enable slow log for current statement */ bool abort_on_warning; bool got_warning; /* Set on call to push_warning() */ /* set during loop of derived table processing */ @@ -3068,6 +3162,7 @@ public: the query. 0 if no error on the master. */ int slave_expected_error; + enum_sql_command last_sql_command; // Last sql_command exceuted in mysql_execute_command() sp_rcontext *spcont; // SP runtime context sp_cache *sp_proc_cache; @@ -3168,11 +3263,20 @@ public: /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - THD(my_thread_id id, bool is_wsrep_applier= false); + /** + @param id thread identifier + @param is_wsrep_applier thread type + @param skip_lock instruct whether @c LOCK_global_system_variables + is already locked, to not acquire it then. + */ + THD(my_thread_id id, bool is_wsrep_applier= false, bool skip_lock= false); ~THD(); - - void init(void); + /** + @param skip_lock instruct whether @c LOCK_global_system_variables + is already locked, to not acquire it then. + */ + void init(bool skip_lock= false); /* Initialize memory roots necessary for query processing and (!) pre-allocate memory for it. We can't do that in THD constructor because @@ -3323,27 +3427,50 @@ public: inline my_time_t query_start() { query_start_used=1; return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } - inline void set_current_time() + MYSQL_TIME query_start_TIME(); + +private: + bool system_time_ge(my_time_t secs, ulong usecs) + { + return (system_time == secs && system_time_sec_part >= usecs) || + system_time > secs; + } + + void set_system_time() { my_hrtime_t hrtime= my_hrtime(); - start_time= hrtime_to_my_time(hrtime); - start_time_sec_part= hrtime_sec_part(hrtime); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread_start_time)(start_time); -#endif + my_time_t secs= hrtime_to_my_time(hrtime); + ulong usecs= hrtime_sec_part(hrtime); + if (system_time_ge(secs, usecs)) + { + if (++system_time_sec_part == HRTIME_RESOLUTION) + { + ++system_time; + system_time_sec_part= 0; + } + } + else + { + system_time= secs; + system_time_sec_part= usecs; + } } + +public: inline void set_start_time() { + set_system_time(); if (user_time.val) { start_time= hrtime_to_my_time(user_time); start_time_sec_part= hrtime_sec_part(user_time); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread_start_time)(start_time); -#endif } else - set_current_time(); + { + start_time= system_time; + start_time_sec_part= system_time_sec_part; + } + PSI_CALL_set_thread_start_time(start_time); } inline void set_time() { @@ -3384,7 +3511,7 @@ public: void update_server_status() { set_time_for_next_stage(); - if (utime_after_query > utime_after_lock + variables.long_query_time) + if (utime_after_query >= utime_after_lock + variables.long_query_time) server_status|= SERVER_QUERY_WAS_SLOW; } inline ulonglong found_rows(void) @@ -3463,12 +3590,12 @@ public: { return !stmt_arena->is_stmt_prepare(); } - inline void* trans_alloc(unsigned int size) + inline void* trans_alloc(size_t size) { return alloc_root(&transaction.mem_root,size); } - LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length) + LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, size_t length) { if (!(lex_str->str= strmake_root(mem_root, str, length))) { @@ -3478,7 +3605,7 @@ public: lex_str->length= length; return lex_str; } - LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, const char* str, uint length) + LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, const char* str, size_t length) { if (!(lex_str->str= strmake_root(mem_root, str, length))) { @@ -3489,24 +3616,23 @@ public: return lex_str; } - LEX_STRING *make_lex_string(const char* str, uint length) - { - LEX_STRING *lex_str; - if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING)))) - return 0; - return make_lex_string(lex_str, str, length); - } - - LEX_CSTRING *make_clex_string(const char* str, uint length) + LEX_CSTRING *make_clex_string(const char* str, size_t length) { LEX_CSTRING *lex_str; - if (!(lex_str= (LEX_CSTRING *)alloc_root(mem_root, sizeof(LEX_CSTRING)))) + char *tmp; + if (!(lex_str= (LEX_CSTRING *)alloc_root(mem_root, sizeof(LEX_CSTRING) + + length+1))) return 0; - return make_lex_string(lex_str, str, length); + tmp= (char*) (lex_str+1); + lex_str->str= tmp; + memcpy(tmp, str, length); + tmp[length]= 0; + lex_str->length= length; + return lex_str; } // Allocate LEX_STRING for character set conversion - bool alloc_lex_string(LEX_STRING *dst, uint length) + bool alloc_lex_string(LEX_STRING *dst, size_t length) { if ((dst->str= (char*) alloc(length))) return false; @@ -3514,7 +3640,7 @@ public: return true; // EOM } bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, - const char *from, uint from_length, + const char *from, size_t from_length, CHARSET_INFO *from_cs); /* Convert a strings between character sets. @@ -3522,7 +3648,7 @@ public: dstcs and srccs cannot be &my_charset_bin. */ bool convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, - CHARSET_INFO *srccs, const char *src, uint src_length, + CHARSET_INFO *srccs, const char *src, size_t src_length, String_copier *status); /* @@ -3531,7 +3657,7 @@ public: */ bool convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, CHARSET_INFO *srccs, - const char *src, uint src_length); + const char *src, size_t src_length); /* If either "dstcs" or "srccs" is &my_charset_bin, @@ -3539,7 +3665,7 @@ public: Otherwise, performs Unicode conversion using convert_fix(). */ bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, - CHARSET_INFO *srccs, const char *src, uint src_length, + CHARSET_INFO *srccs, const char *src, size_t src_length, String_copier *status); /* @@ -3547,7 +3673,7 @@ public: in case of bad byte sequences or Unicode conversion problems. */ bool copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, - CHARSET_INFO *srccs, const char *src, uint src_length); + CHARSET_INFO *srccs, const char *src, size_t src_length); bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs); @@ -3569,8 +3695,8 @@ public: CHARSET_INFO *cs); Item *make_string_literal_concat(Item *item1, const LEX_CSTRING &str); void add_changed_table(TABLE *table); - void add_changed_table(const char *key, long key_length); - CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); + void add_changed_table(const char *key, size_t key_length); + CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length); int send_explain_fields(select_result *result, uint8 explain_flags, bool is_analyze); void make_explain_field_list(List<Item> &field_list, uint8 explain_flags, @@ -3775,20 +3901,7 @@ public: } } int killed_errno(); - inline void reset_killed() - { - /* - Resetting killed has to be done under a mutex to ensure - its not done during an awake() call. - */ - if (killed != NOT_KILLED) - { - mysql_mutex_lock(&LOCK_thd_kill); - killed= NOT_KILLED; - killed_err= 0; - mysql_mutex_unlock(&LOCK_thd_kill); - } - } + void reset_killed(); inline void reset_kill_query() { if (killed < KILL_CONNECTION) @@ -3818,6 +3931,9 @@ public: void restore_backup_open_tables_state(Open_tables_backup *backup); void reset_sub_statement_state(Sub_statement_state *backup, uint new_state); void restore_sub_statement_state(Sub_statement_state *backup); + void store_slow_query_state(Sub_statement_state *backup); + void reset_slow_query_state(); + void add_slow_query_state(Sub_statement_state *backup); void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); void restore_active_arena(Query_arena *set, Query_arena *backup); @@ -3946,74 +4062,33 @@ public: @retval FALSE Success @retval TRUE Out-of-memory error */ - bool set_db(const char *new_db, size_t new_db_len) - { - /* - Acquiring mutex LOCK_thd_data as we either free the memory allocated - for the database and reallocating the memory for the new db or memcpy - the new_db to the db. - */ - mysql_mutex_lock(&LOCK_thd_data); - /* Do not reallocate memory if current chunk is big enough. */ - if (db && new_db && db_length >= new_db_len) - memcpy(db, new_db, new_db_len+1); - else - { - my_free(db); - if (new_db) - db= my_strndup(new_db, new_db_len, MYF(MY_WME | ME_FATALERROR)); - else - db= NULL; - } - db_length= db ? new_db_len : 0; - bool result= new_db && !db; - mysql_mutex_unlock(&LOCK_thd_data); -#ifdef HAVE_PSI_THREAD_INTERFACE - if (result) - PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len); -#endif - return result; - } + bool set_db(const LEX_CSTRING *new_db); - /** - Set the current database; use shallow copy of C-string. + /** Set the current database, without copying */ + void reset_db(const LEX_CSTRING *new_db); - @param new_db a pointer to the new database name. - @param new_db_len length of the new database name. - - @note This operation just sets {db, db_length}. Switching the current - database usually involves other actions, like switching other database - attributes including security context. In the future, this operation - will be made private and more convenient interface will be provided. - */ - void reset_db(char *new_db, size_t new_db_len) - { - if (new_db != db || new_db_len != db_length) - { - mysql_mutex_lock(&LOCK_thd_data); - db= new_db; - db_length= new_db_len; - mysql_mutex_unlock(&LOCK_thd_data); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len); -#endif - } - } /* Copy the current database to the argument. Use the current arena to allocate memory for a deep copy: current database may be freed after a statement is parsed but before it's executed. + + Can only be called by owner of thd (no mutex protection) */ - bool copy_db_to(const char **p_db, size_t *p_db_length) + bool copy_db_to(LEX_CSTRING *to) { - if (db == NULL) + if (db.str == NULL) { my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); return TRUE; } - *p_db= strmake(db, db_length); - *p_db_length= db_length; - return FALSE; + to->str= strmake(db.str, db.length); + to->length= db.length; + return to->str == NULL; /* True on error */ + } + /* Get db name or "". Use for printing current db */ + const char *get_db() + { + return db.str ? db.str : ""; } thd_scheduler event_scheduler; @@ -4100,7 +4175,12 @@ public: Lex_input_stream *lip= &m_parser_state->m_lip; if (!yytext) { - if (!(yytext= lip->get_tok_start())) + if (lip->lookahead_token >= 0) + yytext= lip->get_tok_start_prev(); + else + yytext= lip->get_tok_start(); + + if (!yytext) yytext= ""; } /* Push an error into the error stack */ @@ -4116,6 +4196,11 @@ public: { parse_error(ER_SYNTAX_ERROR); } +#ifdef mysqld_error_find_printf_error_used + void parse_error(const char *t) + { + } +#endif private: /* Only the implementation of the SIGNAL and RESIGNAL statements @@ -4195,12 +4280,12 @@ public: Assign a new value to thd->query and thd->query_id and mysys_var. Protected with LOCK_thd_data mutex. */ - void set_query(char *query_arg, uint32 query_length_arg, + void set_query(char *query_arg, size_t query_length_arg, CHARSET_INFO *cs_arg) { set_query(CSET_STRING(query_arg, query_length_arg, cs_arg)); } - void set_query(char *query_arg, uint32 query_length_arg) /*Mutex protected*/ + void set_query(char *query_arg, size_t query_length_arg) /*Mutex protected*/ { set_query(CSET_STRING(query_arg, query_length_arg, charset())); } @@ -4210,9 +4295,7 @@ public: set_query_inner(string_arg); mysql_mutex_unlock(&LOCK_thd_data); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread_info)(query(), query_length()); -#endif + PSI_CALL_set_thread_info(query(), query_length()); } void reset_query() /* Mutex protected */ { set_query(CSET_STRING()); } @@ -4270,7 +4353,7 @@ public: { Security_context *sctx= &main_security_ctx; sql_print_warning(ER_THD(this, ER_NEW_ABORTING_CONNECTION), - thread_id, (db ? db : "unconnected"), + thread_id, (db.str ? db.str : "unconnected"), sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, reason); } @@ -4396,7 +4479,8 @@ public: const char *path, const char *db, const char *table_name, - bool open_in_engine); + bool open_in_engine, + bool open_internal_tables); TABLE *find_temporary_table(const char *db, const char *table_name); TABLE *find_temporary_table(const TABLE_LIST *tl); @@ -4406,14 +4490,14 @@ public: TMP_TABLE_SHARE *find_tmp_table_share(const char *db, const char *table_name); TMP_TABLE_SHARE *find_tmp_table_share(const TABLE_LIST *tl); - TMP_TABLE_SHARE *find_tmp_table_share(const char *key, uint key_length); + TMP_TABLE_SHARE *find_tmp_table_share(const char *key, size_t key_length); bool open_temporary_table(TABLE_LIST *tl); bool open_temporary_tables(TABLE_LIST *tl); bool close_temporary_tables(); - bool rename_temporary_table(TABLE *table, const char *db, - const char *table_name); + bool rename_temporary_table(TABLE *table, const LEX_CSTRING *db, + const LEX_CSTRING *table_name); bool drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table); bool rm_temporary_table(handlerton *hton, const char *path); void mark_tmp_tables_as_free_for_reuse(); @@ -4518,6 +4602,7 @@ public: /* Handling of timeouts for commands */ thr_timer_t query_timer; + public: void set_query_timer() { @@ -4572,6 +4657,29 @@ public: mysql_mutex_unlock(&LOCK_thread_count); } + + uint get_net_wait_timeout() + { + if (in_active_multi_stmt_transaction()) + { + if (transaction.all.is_trx_read_write()) + { + if (variables.idle_write_transaction_timeout > 0) + return variables.idle_write_transaction_timeout; + } + else + { + if (variables.idle_readonly_transaction_timeout > 0) + return variables.idle_readonly_transaction_timeout; + } + + if (variables.idle_transaction_timeout > 0) + return variables.idle_transaction_timeout; + } + + return variables.net_wait_timeout; + } + /** Switch to a sublex, to parse a substatement or an expression. */ @@ -4599,6 +4707,13 @@ public: Item *sp_fix_func_item(Item **it_addr); Item *sp_prepare_func_item(Item **it_addr, uint cols= 1); bool sp_eval_expr(Field *result_field, Item **expr_item_ptr); + + inline void prepare_logs_for_admin_command() + { + enable_slow_log&= !MY_TEST(variables.log_slow_disabled_statements & + LOG_SLOW_DISABLE_ADMIN); + query_plan_flags|= QPLAN_ADMIN; + } }; inline void add_to_active_threads(THD *thd) @@ -4625,11 +4740,12 @@ inline void unlink_not_visible_thd(THD *thd) /** A short cut for thd->get_stmt_da()->set_ok_status(). */ inline void -my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0, +my_ok(THD *thd, ulonglong affected_rows_arg= 0, ulonglong id= 0, const char *message= NULL) { - thd->set_row_count_func(affected_rows); - thd->get_stmt_da()->set_ok_status(affected_rows, id, message); + thd->set_row_count_func(affected_rows_arg); + thd->set_affected_rows(affected_rows_arg); + thd->get_stmt_da()->set_ok_status(affected_rows_arg, id, message); } @@ -4988,6 +5104,7 @@ class select_insert :public select_result_interceptor { ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; + bool versioned_write; select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par, List<Item> *fields_par, List<Item> *update_fields, List<Item> *update_values, @@ -5048,6 +5165,12 @@ public: const THD *get_thd(void) { return thd; } const HA_CREATE_INFO *get_create_info() { return create_info; }; int prepare2(void) { return 0; } + +private: + TABLE *create_table_from_items(THD *thd, + List<Item> *items, + MYSQL_LOCK **lock, + TABLEOP_HOOKS *hooks); }; #include <myisam.h> @@ -5210,7 +5333,7 @@ public: void cleanup(); virtual bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, - const char *alias, + const LEX_CSTRING *alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, @@ -5244,7 +5367,7 @@ class select_union_recursive :public select_unit int send_data(List<Item> &items); bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, - const char *alias, + const LEX_CSTRING *alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, @@ -5411,7 +5534,7 @@ public: { tmp_table_param.init(); } bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, - const char *alias, + const LEX_CSTRING *alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, @@ -5468,8 +5591,6 @@ public: }; - - /* Optimizer and executor structure for the materialized semi-join info. This structure contains @@ -5630,7 +5751,7 @@ class user_var_entry user_var_entry() {} /* Remove gcc warning */ LEX_CSTRING name; char *value; - ulong length; + size_t length; query_id_t update_query_id, used_query_id; Item_result type; bool unsigned_flag; @@ -5711,6 +5832,12 @@ class multi_update :public select_result_interceptor /* Need this to protect against multiple prepare() calls */ bool prepared; + + // For System Versioning (may need to insert new fields to a table). + ha_rows updated_sys_ver; + + bool has_vers_fields; + public: multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list, List<Item> *fields, List<Item> *values, @@ -6107,7 +6234,7 @@ void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage, #define THD_EXIT_COND(P1, P2) \ thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__) -inline bool binlog_should_compress(ulong len) +inline bool binlog_should_compress(size_t len) { return opt_bin_log_compress && len >= opt_bin_log_compress_min_len; @@ -6188,7 +6315,7 @@ public: (int) m_db.length, (m_db.length ? m_db.str : ""), dot, ".", (int) m_name.length, m_name.str); - DBUG_ASSERT(ok_for_lower_case_names(m_db.str)); + DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str)); return false; } }; @@ -6208,6 +6335,63 @@ public: } }; +class Type_holder: public Sql_alloc, + public Item_args, + public Type_handler_hybrid_field_type, + public Type_all_attributes, + public Type_geometry_attributes +{ + TYPELIB *m_typelib; + bool m_maybe_null; +public: + Type_holder() + :m_typelib(NULL), + m_maybe_null(false) + { } + + void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; } + bool get_maybe_null() const { return m_maybe_null; } + + uint decimal_precision() const + { + /* + Type_holder is not used directly to create fields, so + its virtual decimal_precision() is never called. + We should eventually extend create_result_table() to accept + an array of Type_holders directly, without having to allocate + Item_type_holder's and put them into List<Item>. + */ + DBUG_ASSERT(0); + return 0; + } + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } + uint uint_geometry_type() const + { + return Type_geometry_attributes::get_geometry_type(); + } + void set_typelib(TYPELIB *typelib) + { + m_typelib= typelib; + } + TYPELIB *get_typelib() const + { + return m_typelib; + } + + bool aggregate_attributes(THD *thd) + { + for (uint i= 0; i < arg_count; i++) + m_maybe_null|= args[i]->maybe_null; + return + type_handler()->Item_hybrid_func_fix_attributes(thd, + "UNION", this, this, + args, arg_count); + } +}; + /* A helper class to set THD flags to emit warnings/errors in case of @@ -6251,7 +6435,28 @@ public: }; +#ifndef DBUG_OFF +void dbug_serve_apcs(THD *thd, int n_calls); +#endif -#endif /* MYSQL_SERVER */ +class ScopedStatementReplication +{ +public: + ScopedStatementReplication(THD *thd) : thd(thd) + { + if (thd) + saved_binlog_format= thd->set_current_stmt_binlog_format_stmt(); + } + ~ScopedStatementReplication() + { + if (thd) + thd->restore_stmt_binlog_format(saved_binlog_format); + } + +private: + enum_binlog_format saved_binlog_format; + THD *thd; +}; +#endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_client.cc b/sql/sql_client.cc index efac01f9894..0e17360915c 100644 --- a/sql/sql_client.cc +++ b/sql/sql_client.cc @@ -18,7 +18,7 @@ This files defines some MySQL C API functions that are server specific */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // system_variables diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 0b6a79bae89..558d11eca8c 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -20,7 +20,8 @@ Functions to autenticate and handle reqests for a connection */ -#include <my_global.h> +#include "mariadb.h" +#include "mysqld.h" #include "sql_priv.h" #ifndef __WIN__ #include <netdb.h> // getservbyname, servent @@ -37,6 +38,7 @@ #include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL #include "sql_callback.h" #include "wsrep_mysqld.h" +#include "proxy_protocol.h" HASH global_user_stats, global_client_stats, global_table_stats; HASH global_index_stats; @@ -44,6 +46,7 @@ HASH global_index_stats; extern mysql_mutex_t LOCK_global_user_client_stats; extern mysql_mutex_t LOCK_global_table_stats; extern mysql_mutex_t LOCK_global_index_stats; +extern vio_keepalive_opts opt_vio_keepalive; /* Get structure for logging connection data for the current user @@ -83,7 +86,7 @@ int get_or_create_user_conn(THD *thd, const char *user, uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); uc->host= uc->user + user_len + 1; - uc->len= temp_len; + uc->len= (uint)temp_len; uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; uc->user_resources= *mqh; uc->reset_utime= thd->thr_create_utime; @@ -336,7 +339,7 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) if (lu) // for GRANT { USER_CONN *uc; - uint temp_len=lu->user.length+lu->host.length+2; + size_t temp_len=lu->user.length+lu->host.length+2; char temp_user[USER_HOST_BUFF_SIZE]; memcpy(temp_user,lu->user.str,lu->user.length); @@ -442,7 +445,7 @@ void init_user_stats(USER_STATS *user_stats, user_length= MY_MIN(user_length, sizeof(user_stats->user)-1); memcpy(user_stats->user, user, user_length); user_stats->user[user_length]= 0; - user_stats->user_name_length= user_length; + user_stats->user_name_length= (uint)user_length; strmake_buf(user_stats->priv_user, priv_user); user_stats->total_connections= total_connections; @@ -817,6 +820,113 @@ bool init_new_connection_handler_thread() return 0; } +/** + Set client address during authentication. + + Initializes THD::main_security_ctx and THD::peer_port. + Optionally does ip to hostname translation. + + @param thd current THD handle + @param addr peer address (can be NULL, if 'ip' is set) + @param ip peer address as string (can be NULL if 'addr' is set) + @param port peer port + @param check_proxy_networks if true, and host is in + 'proxy_protocol_networks' list, skip + "host not privileged" check + @param[out] host_errors - number of connect + errors for this host + + @retval 0 ok, 1 error +*/ +int thd_set_peer_addr(THD *thd, + sockaddr_storage *addr, + const char *ip, + uint port, + bool check_proxy_networks, + uint *host_errors) +{ + *host_errors= 0; + + thd->peer_port= port; + + char ip_string[128]; + if (!ip) + { + void *addr_data; + if (addr->ss_family == AF_UNIX) + { + /* local connection */ + my_free((void *)thd->main_security_ctx.ip); + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost; + thd->main_security_ctx.ip= 0; + return 0; + } + else if (addr->ss_family == AF_INET) + addr_data= &((struct sockaddr_in *)addr)->sin_addr; + else + addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr; + if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string))) + { + DBUG_ASSERT(0); + return 1; + } + ip= ip_string; + } + + my_free((void *)thd->main_security_ctx.ip); + if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME)))) + { + /* + No error accounting per IP in host_cache, + this is treated as a global server OOM error. + TODO: remove the need for my_strdup. + */ + statistic_increment(aborted_connects, &LOCK_status); + statistic_increment(connection_errors_internal, &LOCK_status); + return 1; /* The error is set by my_strdup(). */ + } + thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip; + if (!(specialflag & SPECIAL_NO_RESOLVE)) + { + int rc; + + rc = ip_to_hostname(addr, + thd->main_security_ctx.ip, + &thd->main_security_ctx.host, + host_errors); + + /* Cut very long hostnames to avoid possible overflows */ + if (thd->main_security_ctx.host) + { + if (thd->main_security_ctx.host != my_localhost) + ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host), + HOSTNAME_LENGTH)] = 0; + thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host; + } + + if (rc == RC_BLOCKED_HOST) + { + /* HOST_CACHE stats updated by ip_to_hostname(). */ + my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip); + return 1; + } + } + DBUG_PRINT("info", ("Host: %s ip: %s", + (thd->main_security_ctx.host ? + thd->main_security_ctx.host : "unknown host"), + (thd->main_security_ctx.ip ? + thd->main_security_ctx.ip : "unknown ip"))); + if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr)) + && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) + { + /* HOST_CACHE stats updated by acl_check_host(). */ + my_error(ER_HOST_NOT_PRIVILEGED, MYF(0), + thd->main_security_ctx.host_or_ip); + return 1; + } + return 0; +} + /* Perform handshake, authorize client and update thd ACL variables. @@ -846,8 +956,9 @@ static int check_connection(THD *thd) { my_bool peer_rc; char ip[NI_MAXHOST]; + uint16 peer_port; - peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST); + peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST); /* =========================================================================== @@ -868,7 +979,7 @@ static int check_connection(THD *thd) struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr; /* See RFC 5737, 192.0.2.0/24 is reserved. */ const char* fake= "192.0.2.4"; - ip4->s_addr= inet_addr(fake); + inet_pton(AF_INET,fake, ip4); strcpy(ip, fake); peer_rc= 0; } @@ -922,55 +1033,10 @@ static int check_connection(THD *thd) my_error(ER_BAD_HOST_ERROR, MYF(0)); return 1; } - if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME)))) - { - /* - No error accounting per IP in host_cache, - this is treated as a global server OOM error. - TODO: remove the need for my_strdup. - */ - statistic_increment(aborted_connects,&LOCK_status); - statistic_increment(connection_errors_internal, &LOCK_status); - return 1; /* The error is set by my_strdup(). */ - } - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; - if (!(specialflag & SPECIAL_NO_RESOLVE)) - { - int rc; - - rc= ip_to_hostname(&net->vio->remote, - thd->main_security_ctx.ip, - &thd->main_security_ctx.host, - &connect_errors); - - /* Cut very long hostnames to avoid possible overflows */ - if (thd->main_security_ctx.host) - { - if (thd->main_security_ctx.host != my_localhost) - ((char*) thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host), - HOSTNAME_LENGTH)]= 0; - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - } - - if (rc == RC_BLOCKED_HOST) - { - /* HOST_CACHE stats updated by ip_to_hostname(). */ - my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - } - DBUG_PRINT("info",("Host: %s ip: %s", - (thd->main_security_ctx.host ? - thd->main_security_ctx.host : "unknown host"), - (thd->main_security_ctx.ip ? - thd->main_security_ctx.ip : "unknown ip"))); - if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) - { - /* HOST_CACHE stats updated by acl_check_host(). */ - my_error(ER_HOST_NOT_PRIVILEGED, MYF(0), - thd->main_security_ctx.host_or_ip); + + if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port, + true, &connect_errors)) return 1; - } } else /* Hostname given means that the connection was on a socket */ { @@ -981,7 +1047,8 @@ static int check_connection(THD *thd) bzero((char*) &net->vio->remote, sizeof(net->vio->remote)); } vio_keepalive(net->vio, TRUE); - + vio_set_keepalive_options(net->vio, &opt_vio_keepalive); + if (thd->packet.alloc(thd->variables.net_buffer_length)) { /* @@ -1198,7 +1265,7 @@ void prepare_new_connection_state(THD* thd) if (packet_length != packet_error) my_error(ER_NEW_ABORTING_CONNECTION, MYF(0), thd->thread_id, - thd->db ? thd->db : "unconnected", + thd->db.str ? thd->db.str : "unconnected", sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, "init_connect command failed"); thd->server_status&= ~SERVER_STATUS_CLEAR_SET; diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 364be401944..67950061da8 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -16,7 +16,7 @@ #ifndef SQL_CONNECT_INCLUDED #define SQL_CONNECT_INCLUDED -#include "my_sys.h" /* pthread_handler_t */ +#include <my_sys.h> /* pthread_handler_t */ #include "mysql_com.h" /* enum_server_command */ #include "structs.h" #include <mysql/psi/mysql_socket.h> @@ -85,6 +85,10 @@ bool thd_init_client_charset(THD *thd, uint cs_number); bool setup_connection_thread_globals(THD *thd); bool thd_prepare_connection(THD *thd); bool thd_is_connection_alive(THD *thd); +int thd_set_peer_addr(THD *thd, sockaddr_storage *addr, + const char *ip, uint port, + bool check_proxy_networks, + uint *host_errors); bool login_connection(THD *thd); void prepare_new_connection_state(THD* thd); diff --git a/sql/sql_const.h b/sql/sql_const.h index 007b7faeebb..65742235bee 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -231,6 +231,7 @@ */ #define HEAP_TEMPTABLE_LOOKUP_COST 0.05 #define DISK_TEMPTABLE_LOOKUP_COST 1.0 +#define SORT_INDEX_CMP_COST 0.02 #define MY_CHARSET_BIN_MB_MAXLEN 1 diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 2460a16551d..19cd780e9c3 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_crypt.h" #include "password.h" diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index 3df554e9d31..e61776713b6 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -21,7 +21,7 @@ #pragma interface /* gcc class implementation */ #endif -#include "sql_list.h" /* Sql_alloc */ +#include "sql_alloc.h" /* Sql_alloc */ #include "my_rnd.h" /* rand_struct */ class SQL_CRYPT :public Sql_alloc diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 23a3eba1615..05d12998026 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -1,3 +1,20 @@ +/* + Copyright (c) 2016, 2017 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 */ + +#include "mariadb.h" #include "sql_class.h" #include "sql_lex.h" #include "sql_cte.h" @@ -112,8 +129,8 @@ bool With_clause::check_dependencies() elem != with_elem; elem= elem->next) { - if (my_strcasecmp(system_charset_info, with_elem->query_name->str, - elem->query_name->str) == 0) + if (lex_string_cmp(system_charset_info, with_elem->query_name, + elem->query_name) == 0) { my_error(ER_DUP_QUERY_NAME, MYF(0), with_elem->query_name->str); return true; @@ -223,7 +240,7 @@ With_element *With_clause::find_table_def(TABLE_LIST *table, with_elem= with_elem->next) { if (my_strcasecmp(system_charset_info, with_elem->query_name->str, - table->table_name) == 0 && + table->table_name.str) == 0 && !table->is_fqtn) { table->set_derived(); @@ -809,7 +826,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, TABLE_LIST *spec_tables_tail; st_select_lex *with_select; - if (parser_state.init(thd, (char*) unparsed_spec.str, unparsed_spec.length)) + if (parser_state.init(thd, (char*) unparsed_spec.str, (unsigned int)unparsed_spec.length)) goto err; lex_start(thd); with_select= &lex->select_lex; diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 5312e83a80f..13e9b83955c 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -1,3 +1,19 @@ +/* + Copyright (c) 2016, 2017 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_CTE_INCLUDED #define SQL_CTE_INCLUDED #include "sql_list.h" diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 99d95c86b2f..bb6bcd253f7 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -17,7 +17,7 @@ #pragma implementation /* gcc class implementation */ #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_cursor.h" @@ -132,7 +132,7 @@ int mysql_open_cursor(THD *thd, select_result *result, MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip, 2); @@ -440,7 +440,7 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags) if (create_result_table(unit->thd, unit->get_column_types(true), FALSE, thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS, - "", FALSE, TRUE, TRUE, 0)) + &empty_clex_str, FALSE, TRUE, TRUE, 0)) return TRUE; materialized_cursor= new (&table->mem_root) diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index 6fa72a2005d..740a658b7c0 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -62,6 +62,7 @@ public: virtual ~Server_side_cursor(); static void operator delete(void *ptr, size_t size); + static void operator delete(void *, MEM_ROOT *){} }; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 7860fa6d550..67910a3b618 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,7 +18,7 @@ /* create and drop of databases */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_db.h" @@ -62,7 +62,7 @@ static void mysql_change_db_impl(THD *thd, LEX_CSTRING *new_db_name, ulong new_db_access, CHARSET_INFO *new_db_charset); -static bool mysql_rm_db_internal(THD *thd, const char *db, +static bool mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silent); @@ -94,12 +94,12 @@ typedef struct my_dbopt_st */ static inline bool -cmp_db_names(const char *db1_name, - const char *db2_name) +cmp_db_names(LEX_CSTRING *db1_name, const LEX_CSTRING *db2_name) { - return ((!db1_name && !db2_name) || - (db1_name && db2_name && - my_strcasecmp(table_alias_charset, db1_name, db2_name) == 0)); + return (db1_name->length == db2_name->length && + (db1_name->length == 0 || + my_strcasecmp(table_alias_charset, + db1_name->str, db2_name->str) == 0)); } @@ -122,12 +122,12 @@ uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length, Helper function to write a query to binlog used by mysql_rm_db() */ -static inline int write_to_binlog(THD *thd, const char *query, uint q_len, - const char *db, uint db_len) +static inline int write_to_binlog(THD *thd, const char *query, size_t q_len, + const char *db, size_t db_len) { Query_log_event qinfo(thd, query, q_len, FALSE, TRUE, FALSE, 0); qinfo.db= db; - qinfo.db_len= db_len; + qinfo.db_len= (uint32)db_len; return mysql_bin_log.write(&qinfo); } @@ -388,7 +388,7 @@ bool load_db_opt(THD *thd, const char *path, Schema_specification_st *create) char buf[256]; DBUG_ENTER("load_db_opt"); bool error=1; - uint nbytes; + size_t nbytes; bzero((char*) create,sizeof(*create)); create->default_table_charset= thd->variables.collation_server; @@ -523,7 +523,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name) { Schema_specification_st db_info; - if (thd->db != NULL && strcmp(db_name, thd->db) == 0) + if (thd->db.str != NULL && strcmp(db_name, thd->db.str) == 0) return thd->db_charset; load_db_opt_by_name(thd, db_name, &db_info); @@ -566,7 +566,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name) */ static int -mysql_create_db_internal(THD *thd, const char *db, +mysql_create_db_internal(THD *thd, const LEX_CSTRING *db, const DDL_options_st &options, Schema_specification_st *create_info, bool silent) @@ -579,18 +579,18 @@ mysql_create_db_internal(THD *thd, const char *db, /* do not create 'information_schema' db */ if (is_infoschema_db(db)) { - my_error(ER_DB_CREATE_EXISTS, MYF(0), db); + my_error(ER_DB_CREATE_EXISTS, MYF(0), db->str); DBUG_RETURN(-1); } - char db_tmp[SAFE_NAME_LEN]; - const char *dbnorm= normalize_db_name(db, db_tmp, sizeof(db_tmp)); + char db_tmp[SAFE_NAME_LEN+1]; + const char *dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp)); if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(-1); /* Check directory */ - path_len= build_table_filename(path, sizeof(path) - 1, db, "", "", 0); + path_len= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0); path[path_len-1]= 0; // Remove last '/' from path long affected_rows= 1; @@ -618,20 +618,20 @@ mysql_create_db_internal(THD *thd, const char *db, { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_CREATE_EXISTS, ER_THD(thd, ER_DB_CREATE_EXISTS), - db); + db->str); affected_rows= 0; goto not_silent; } else { - my_error(ER_DB_CREATE_EXISTS, MYF(0), db); + my_error(ER_DB_CREATE_EXISTS, MYF(0), db->str); DBUG_RETURN(-1); } if (my_mkdir(path, 0777, MYF(0)) < 0) { - my_error(ER_CANT_CREATE_DB, MYF(0), db, my_errno); + my_error(ER_CANT_CREATE_DB, MYF(0), db->str, my_errno); DBUG_RETURN(-1); } @@ -687,8 +687,8 @@ not_silent: USE sisyfos; # Will give error on slave since # database does not exist. */ - qinfo.db = db; - qinfo.db_len = strlen(db); + qinfo.db = db->str; + qinfo.db_len = (uint32)db->length; /* These DDL methods and logging are protected with the exclusive @@ -707,7 +707,7 @@ not_silent: /* db-name is already validated when we come here */ static bool -mysql_alter_db_internal(THD *thd, const char *db, +mysql_alter_db_internal(THD *thd, const LEX_CSTRING *db, Schema_specification_st *create_info) { char path[FN_REFLEN+16]; @@ -715,7 +715,7 @@ mysql_alter_db_internal(THD *thd, const char *db, int error= 0; DBUG_ENTER("mysql_alter_db"); - if (lock_schema_name(thd, db)) + if (lock_schema_name(thd, db->str)) DBUG_RETURN(TRUE); /* @@ -723,13 +723,13 @@ mysql_alter_db_internal(THD *thd, const char *db, We pass MY_DB_OPT_FILE as "extension" to avoid "table name to file name" encoding. */ - build_table_filename(path, sizeof(path) - 1, db, "", MY_DB_OPT_FILE, 0); + build_table_filename(path, sizeof(path) - 1, db->str, "", MY_DB_OPT_FILE, 0); if ((error=write_db_opt(thd, path, create_info))) goto exit; /* Change options if current database is being altered. */ - if (thd->db && !strcmp(thd->db,db)) + if (thd->db.str && !cmp(&thd->db, db)) { thd->db_charset= create_info->default_table_charset ? create_info->default_table_charset : @@ -747,8 +747,8 @@ mysql_alter_db_internal(THD *thd, const char *db, database" and not the threads current database, which is the default. */ - qinfo.db = db; - qinfo.db_len = strlen(db); + qinfo.db= db->str; + qinfo.db_len= (uint)db->length; /* These DDL methods and logging are protected with the exclusive @@ -764,7 +764,7 @@ exit: } -int mysql_create_db(THD *thd, const char *db, +int mysql_create_db(THD *thd, const LEX_CSTRING *db, const DDL_options_st &options, const Schema_specification_st *create_info) { @@ -777,7 +777,7 @@ int mysql_create_db(THD *thd, const char *db, } -bool mysql_alter_db(THD *thd, const char *db, +bool mysql_alter_db(THD *thd, const LEX_CSTRING *db, const Schema_specification_st *create_info) { /* @@ -805,7 +805,7 @@ bool mysql_alter_db(THD *thd, const char *db, */ static bool -mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) +mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db, bool if_exists, bool silent) { ulong deleted_tables= 0; bool error= true, rm_mysql_schema; @@ -817,13 +817,13 @@ mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) Drop_table_error_handler err_handler; DBUG_ENTER("mysql_rm_db"); - char db_tmp[SAFE_NAME_LEN]; - const char *dbnorm= normalize_db_name(db, db_tmp, sizeof(db_tmp)); + char db_tmp[SAFE_NAME_LEN+1]; + const char *dbnorm= normalize_db_name(db->str, db_tmp, sizeof(db_tmp)); if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(true); - length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0); + length= build_table_filename(path, sizeof(path) - 1, db->str, "", "", 0); strmov(path+length, MY_DB_OPT_FILE); // Append db option file name del_dbopt(path); // Remove dboption hash entry /* @@ -846,14 +846,14 @@ mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) { if (!if_exists) { - my_error(ER_DB_DROP_EXISTS, MYF(0), db); + my_error(ER_DB_DROP_EXISTS, MYF(0), db->str); DBUG_RETURN(true); } else { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER_THD(thd, ER_DB_DROP_EXISTS), - db); + db->str); error= false; goto update_binlog; } @@ -867,7 +867,7 @@ mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) This check is only needed if we are dropping the "mysql" database. */ if ((rm_mysql_schema= - (my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0))) + (my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db->str) == 0))) { for (table= tables; table; table= table->next_local) if (check_if_log_table(table, TRUE, "DROP")) @@ -884,11 +884,9 @@ mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) { for (table= tables; table; table= table->next_local) { - LEX_CSTRING db_name= { table->db, table->db_length }; - LEX_CSTRING table_name= { table->table_name, table->table_name_length }; if (table->open_type == OT_BASE_ONLY || !thd->find_temporary_table(table)) - (void) delete_statistics_for_table(thd, &db_name, &table_name); + (void) delete_statistics_for_table(thd, &table->db, &table->table_name); } } @@ -959,8 +957,8 @@ update_binlog: database" and not the threads current database, which is the default. */ - qinfo.db = db; - qinfo.db_len = strlen(db); + qinfo.db = db->str; + qinfo.db_len = (uint32)db->length; /* These DDL methods and logging are protected with the exclusive @@ -980,33 +978,32 @@ update_binlog: { char *query, *query_pos, *query_end, *query_data_start; TABLE_LIST *tbl; - uint db_len; if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN))) goto exit; /* not much else we can do */ query_pos= query_data_start= strmov(query,"DROP TABLE IF EXISTS "); query_end= query + MAX_DROP_TABLE_Q_LEN; - db_len= strlen(db); for (tbl= tables; tbl; tbl= tbl->next_local) { - uint tbl_name_len; + size_t tbl_name_len; char quoted_name[FN_REFLEN+3]; // Only write drop table to the binlog for tables that no longer exist. - if (ha_table_exists(thd, tbl->db, tbl->table_name)) + if (ha_table_exists(thd, &tbl->db, &tbl->table_name)) continue; - my_snprintf(quoted_name, sizeof(quoted_name), quoted_string, - tbl->table_name); - tbl_name_len= strlen(quoted_name) + 1; /* +1 for the comma */ + tbl_name_len= my_snprintf(quoted_name, sizeof(quoted_name), + quoted_string, + tbl->table_name.str); + tbl_name_len++; /* +1 for the comma */ if (query_pos + tbl_name_len + 1 >= query_end) { /* These DDL methods and logging are protected with the exclusive metadata lock on the schema. */ - if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db, db_len)) + if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, db->length)) { error= true; goto exit; @@ -1024,7 +1021,7 @@ update_binlog: These DDL methods and logging are protected with the exclusive metadata lock on the schema. */ - if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db, db_len)) + if (write_to_binlog(thd, query, (uint)(query_pos -1 - query), db->str, db->length)) { error= true; goto exit; @@ -1039,7 +1036,7 @@ exit: SELECT DATABASE() in the future). For this we free() thd->db and set it to 0. */ - if (thd->db && cmp_db_names(thd->db, db) && !error) + if (thd->db.str && cmp_db_names(&thd->db, db) && !error) { mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL); @@ -1049,7 +1046,7 @@ exit: } -bool mysql_rm_db(THD *thd, const char *db, bool if_exists) +bool mysql_rm_db(THD *thd, const LEX_CSTRING *db, bool if_exists) { return mysql_rm_db_internal(thd, db, if_exists, false); } @@ -1084,20 +1081,18 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, if (!table_list) DBUG_RETURN(true); - table_list->db= db.str; - table_list->db_length= db.length; - table_list->table_name= table->str; - table_list->table_name_length= table->length; + table_list->db= db; + table_list->table_name= *table; table_list->open_type= OT_BASE_ONLY; /* To be able to correctly look up the table in the table cache. */ if (lower_case_table_names) - table_list->table_name_length= my_casedn_str(files_charset_info, - (char*) table_list->table_name); + table_list->table_name.length= my_casedn_str(files_charset_info, + (char*) table_list->table_name.str); table_list->alias= table_list->table_name; // If lower_case_table_names=2 - table_list->mdl_request.init(MDL_key::TABLE, table_list->db, - table_list->table_name, MDL_EXCLUSIVE, + table_list->mdl_request.init(MDL_key::TABLE, table_list->db.str, + table_list->table_name.str, MDL_EXCLUSIVE, MDL_TRANSACTION); /* Link into list */ (*tot_list_next_local)= table_list; @@ -1312,16 +1307,16 @@ static void mysql_change_db_impl(THD *thd, sets the new one. */ - thd->set_db(NULL, 0); + thd->set_db(&null_clex_str); } - else if (new_db_name == &INFORMATION_SCHEMA_NAME) + else if (new_db_name->str == INFORMATION_SCHEMA_NAME.str) { /* Here we must use THD::set_db(), because we want to copy INFORMATION_SCHEMA_NAME constant. */ - thd->set_db(INFORMATION_SCHEMA_NAME.str, INFORMATION_SCHEMA_NAME.length); + thd->set_db(&INFORMATION_SCHEMA_NAME); } else { @@ -1330,8 +1325,8 @@ static void mysql_change_db_impl(THD *thd, we just call THD::reset_db(). Since THD::reset_db() does not releases the previous database name, we should do it explicitly. */ - thd->set_db(NULL, 0); - thd->reset_db(const_cast<char*>(new_db_name->str), new_db_name->length); + thd->set_db(&null_clex_str); + thd->reset_db(new_db_name); } /* 2. Update security context. */ @@ -1366,17 +1361,17 @@ static void mysql_change_db_impl(THD *thd, static void backup_current_db_name(THD *thd, LEX_STRING *saved_db_name) { - if (!thd->db) + DBUG_ASSERT(saved_db_name->length >= SAFE_NAME_LEN +1); + if (!thd->db.str) { /* No current (default) database selected. */ - - saved_db_name->str= NULL; + saved_db_name->str= 0; saved_db_name->length= 0; } else { - strmake(saved_db_name->str, thd->db, saved_db_name->length - 1); - saved_db_name->length= thd->db_length; + memcpy(saved_db_name->str, thd->db.str, thd->db.length + 1); + saved_db_name->length= thd->db.length; } } @@ -1479,7 +1474,7 @@ bool mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name, } DBUG_PRINT("enter",("name: '%s'", new_db_name->str)); - if (is_infoschema_db(new_db_name->str, new_db_name->length)) + if (is_infoschema_db(new_db_name)) { /* Switch the current database to INFORMATION_SCHEMA. */ @@ -1630,7 +1625,7 @@ bool mysql_opt_change_db(THD *thd, bool force_switch, bool *cur_db_changed) { - *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str); + *cur_db_changed= !cmp_db_names(&thd->db, new_db_name); if (!*cur_db_changed) return FALSE; @@ -1656,9 +1651,9 @@ bool mysql_opt_change_db(THD *thd, @return 0 on success, 1 on error */ -bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) +bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db) { - int error= 0, change_to_newdb= 0; + bool error= 0, change_to_newdb= 0; char path[FN_REFLEN+16]; uint length; Schema_specification_st create_info; @@ -1691,7 +1686,7 @@ bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) Let's remember if we should do "USE newdb" afterwards. thd->db will be cleared in mysql_rename_db() */ - if (thd->db && !strcmp(thd->db, old_db->str)) + if (thd->db.str && !cmp(&thd->db, old_db)) change_to_newdb= 1; build_table_filename(path, sizeof(path)-1, @@ -1709,7 +1704,7 @@ bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) } /* Step1: Create the new database */ - if ((error= mysql_create_db_internal(thd, new_db.str, + if ((error= mysql_create_db_internal(thd, &new_db, DDL_options(), &create_info, 1))) goto exit; @@ -1725,8 +1720,7 @@ bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) DBUG_PRINT("info",("Examining: %s", file->name)); /* skiping non-FRM files */ - if (my_strcasecmp(files_charset_info, - (extension= fn_rext(file->name)), reg_ext)) + if (!(extension= (char*) fn_frm_ext(file->name))) continue; /* A frm file found, add the table info rename list */ @@ -1834,7 +1828,7 @@ bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) to execute them again. mysql_rm_db() also "unuses" if we drop the current database. */ - error= mysql_rm_db_internal(thd, old_db->str, 0, true); + error= mysql_rm_db_internal(thd, old_db, 0, true); /* Step8: logging */ if (mysql_bin_log.is_open()) diff --git a/sql/sql_db.h b/sql/sql_db.h index a4ada7de6a2..7de6c2a9c99 100644 --- a/sql/sql_db.h +++ b/sql/sql_db.h @@ -20,13 +20,13 @@ class THD; -int mysql_create_db(THD *thd, const char *db, +int mysql_create_db(THD *thd, const LEX_CSTRING *db, const DDL_options_st &options, const Schema_specification_st *create); -bool mysql_alter_db(THD *thd, const char *db, +bool mysql_alter_db(THD *thd, const LEX_CSTRING *db, const Schema_specification_st *create); -bool mysql_rm_db(THD *thd, const char *db, bool if_exists); -bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db); +bool mysql_rm_db(THD *thd, const LEX_CSTRING *db, bool if_exists); +bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db); bool mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name, bool force_switch); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e7a0168fb48..48619b9d4fc 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -21,7 +21,7 @@ Multi-table deletes were introduced by Monty and Sinisa */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_delete.h" @@ -61,6 +61,8 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t Explain_query *query= thd->lex->explain; Explain_delete *explain= new (mem_root) Explain_delete(mem_root, thd->lex->analyze_stmt); + if (!explain) + return 0; if (deleting_all_rows) { @@ -71,8 +73,9 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t else { explain->deleting_all_rows= false; - Update_plan::save_explain_data_intern(mem_root, explain, - thd->lex->analyze_stmt); + if (Update_plan::save_explain_data_intern(mem_root, explain, + thd->lex->analyze_stmt)) + return 0; } query->add_upd_del_plan(explain); @@ -86,18 +89,21 @@ Update_plan::save_explain_update_data(MEM_ROOT *mem_root, THD *thd) Explain_query *query= thd->lex->explain; Explain_update* explain= new (mem_root) Explain_update(mem_root, thd->lex->analyze_stmt); - save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt); + if (!explain) + return 0; + if (save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt)) + return 0; query->add_upd_del_plan(explain); return explain; } -void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, +bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *explain, bool is_analyze) { explain->select_type= "SIMPLE"; - explain->table_name.append(table->pos_in_table_list->alias); + explain->table_name.append(&table->pos_in_table_list->alias); explain->impossible_where= false; explain->no_partitions= false; @@ -105,13 +111,13 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, if (impossible_where) { explain->impossible_where= true; - return; + return 0; } if (no_partitions) { explain->no_partitions= true; - return; + return 0; } if (is_analyze) @@ -162,7 +168,8 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, explain->where_cond= select? select->cond: NULL; if (using_filesort) - explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze); + if (!(explain->filesort_tracker= new (mem_root) Filesort_tracker(is_analyze))) + return 1; explain->using_io_buffer= using_io_buffer; append_possible_keys(mem_root, explain->possible_keys, table, @@ -211,17 +218,26 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, if (!(unit->item && unit->item->eliminated)) explain->add_child(unit->first_select()->select_number); } + return 0; } static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, - Explain_delete *explain) + Explain_delete *explain, bool truncate_history) { + bool check_delete= true; + + if (table->versioned()) + { + bool historical= !table->vers_end_field()->is_max(); + check_delete= truncate_history ? historical : !historical; + } + explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (table->vfield) (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); - if (!sel || sel->skip_record(thd) > 0) + if (check_delete && (!sel || sel->skip_record(thd) > 0)) { explain->tracker.on_record_after_where(); return true; @@ -230,6 +246,18 @@ static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, } +inline +int TABLE::delete_row() +{ + if (!versioned(VERS_TIMESTAMP) || !vers_end_field()->is_max()) + return file->ha_delete_row(record[0]); + + store_record(this, record[1]); + vers_update_end(); + return file->ha_update_row(record[1], record[0]); +} + + /** Implement DELETE SQL word. @@ -242,7 +270,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_I_List<ORDER> *order_list, ha_rows limit, ulonglong options, select_result *result) { - bool will_batch; + bool will_batch= FALSE; int error, loc_error; TABLE *table; SQL_SELECT *select=0; @@ -254,11 +282,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool return_error= 0; ha_rows deleted= 0; bool reverse= FALSE; + bool has_triggers; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; + bool binlog_is_row; bool with_select= !select_lex->item_list.is_empty(); Explain_delete *explain; Delete_plan query_plan(thd->mem_root); @@ -274,6 +304,39 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); THD_STAGE_INFO(thd, stage_init_update); + + bool truncate_history= table_list->vers_conditions; + if (truncate_history) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_VERS_TRUNCATE_VIEW, MYF(0)); + DBUG_RETURN(true); + } + + TABLE *table= table_list->table; + DBUG_ASSERT(table); + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + { + my_error(ER_NOT_ALLOWED_COMMAND, MYF(0)); + DBUG_RETURN(true); + } +#endif + + DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); + if (select_lex->vers_setup_conds(thd, table_list, &conds)) + DBUG_RETURN(TRUE); + + // trx_sees() in InnoDB reads row_start + if (!table->versioned(VERS_TIMESTAMP)) + { + DBUG_ASSERT(table_list->vers_conditions.type == SYSTEM_TIME_BEFORE); + bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); + } + } + if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); if (mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) @@ -281,7 +344,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!table_list->single_table_updatable()) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } if (!(table= table_list->table) || !table->is_created()) @@ -363,9 +426,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - We should not be binlogging this statement in row-based, and - there should be no delete triggers associated with the table. */ + + has_triggers= (table->triggers && + table->triggers->has_delete_triggers()); if (!with_select && !using_limit && const_cond_result && (!thd->is_current_stmt_binlog_format_row() && - !(table->triggers && table->triggers->has_delete_triggers()))) + !has_triggers) + && !table->versioned(VERS_TIMESTAMP)) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -385,7 +452,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_type= THD::STMT_QUERY_TYPE; error= -1; deleted= maybe_deleted; - query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!query_plan.save_explain_delete_data(thd->mem_root, thd)) + error= 1; goto cleanup; } if (error != HA_ERR_WRONG_COMMAND) @@ -503,7 +571,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto produce_explain_and_leave; - explain= query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!(explain= query_plan.save_explain_delete_data(thd->mem_root, thd))) + goto got_error; ANALYZE_START_TRACKING(&explain->command_tracker); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", @@ -512,14 +581,59 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!(select && select->quick)) status_var_increment(thd->status_var.delete_scan_count); - if (query_plan.using_filesort) + binlog_is_row= thd->is_current_stmt_binlog_format_row(); + DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE")); + + /* + We can use direct delete (delete that is done silently in the handler) + if none of the following conditions are true: + - There are triggers + - There is binary logging + - There is a virtual not stored column in the WHERE clause + - ORDER BY or LIMIT + - As this requires the rows to be deleted in a specific order + - Note that Spider can handle ORDER BY and LIMIT in a cluster with + one data node. These conditions are therefore checked in + direct_delete_rows_init(). + + Direct delete does not require a WHERE clause + + Later we also ensure that we are only using one table (no sub queries) + */ + + if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && + !has_triggers && !binlog_is_row && !with_select) { + table->mark_columns_needed_for_delete(); + if (!table->check_virtual_columns_marked_for_read()) + { + DBUG_PRINT("info", ("Trying direct delete")); + if (select && select->cond && + (select->cond->used_tables() == table->map)) + { + DBUG_ASSERT(!table->file->pushed_cond); + if (!table->file->cond_push(select->cond)) + table->file->pushed_cond= select->cond; + } + if (!table->file->direct_delete_rows_init()) + { + /* Direct deleting is supported */ + DBUG_PRINT("info", ("Using direct delete")); + THD_STAGE_INFO(thd, stage_updating); + if (!(error= table->file->ha_direct_delete_rows(&deleted))) + error= -1; + goto terminate_delete; + } + } + } + if (query_plan.using_filesort) + { { Filesort fsort(order, HA_POS_ERROR, true, select); DBUG_ASSERT(query_plan.index == MAX_KEY); - Filesort_tracker *fs_tracker= + Filesort_tracker *fs_tracker= thd->lex->explain->get_upd_del_plan()->filesort_tracker; if (!(file_sort= filesort(thd, table, &fsort, fs_tracker))) @@ -555,17 +669,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (error) goto got_error; - init_ftfuncs(thd, select_lex, 1); - - if (table->prepare_triggers_for_delete_stmt_or_event()) - { - will_batch= FALSE; - } - else - will_batch= !table->file->start_bulk_delete(); + if (init_ftfuncs(thd, select_lex, 1)) + goto got_error; table->mark_columns_needed_for_delete(); + if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_DELETE) && + !table->prepare_triggers_for_delete_stmt_or_event()) + will_batch= !table->file->start_bulk_delete(); + if (with_select) { if (result->send_result_set_metadata(select_lex->item_list, @@ -589,10 +701,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, MEM_STRIP_BUF_SIZE); THD_STAGE_INFO(thd, stage_searching_rows_for_update); - while (!(error=info.read_record(&info)) && !thd->killed && + while (!(error=info.read_record()) && !thd->killed && ! thd->is_error()) { - if (record_should_be_deleted(thd, table, select, explain)) + if (record_should_be_deleted(thd, table, select, explain, truncate_history)) { table->file->position(table->record[0]); if ((error= deltempfile->unique_add((char*) table->file->ref))) @@ -615,14 +727,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } THD_STAGE_INFO(thd, stage_updating); - while (!(error=info.read_record(&info)) && !thd->killed && + while (!(error=info.read_record()) && !thd->killed && ! thd->is_error()) { if (delete_while_scanning) - delete_record= record_should_be_deleted(thd, table, select, explain); + delete_record= record_should_be_deleted(thd, table, select, explain, + truncate_history); if (delete_record) { - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) { @@ -636,10 +749,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, break; } - if (!(error= table->file->ha_delete_row(table->record[0]))) + error= table->delete_row(); + if (!error) { deleted++; - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE)) { @@ -672,6 +786,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else break; } + terminate_delete: killed_status= thd->killed; if (killed_status != NOT_KILLED || thd->is_error()) @@ -725,6 +840,8 @@ cleanup: else errcode= query_error_code(thd, killed_status == NOT_KILLED); + ScopedStatementReplication scoped_stmt_rpl( + table->versioned(VERS_TRX_ID) ? thd : NULL); /* [binlog]: If 'handler::delete_all_rows()' was called and the storage engine does not inject the rows itself, we replicate @@ -758,6 +875,8 @@ cleanup: } delete file_sort; free_underlaid_joins(thd, select_lex); + if (table->file->pushed_cond) + table->file->cond_pop(); DBUG_RETURN(error >= 0 || thd->is_error()); /* Special exits */ @@ -766,7 +885,8 @@ produce_explain_and_leave: We come here for various "degenerate" query plans: impossible WHERE, no-partitions-used, impossible-range, etc. */ - query_plan.save_explain_delete_data(thd->mem_root, thd); + if (!(query_plan.save_explain_delete_data(thd->mem_root, thd))) + goto got_error; send_nothing_and_leave: /* @@ -778,7 +898,8 @@ send_nothing_and_leave: delete select; delete file_sort; free_underlaid_joins(thd, select_lex); - //table->set_keyread(false); + if (table->file->pushed_cond) + table->file->cond_pop(); DBUG_ASSERT(!return_error || thd->is_error() || thd->killed); DBUG_RETURN((return_error || thd->is_error() || thd->killed) ? 1 : 0); @@ -821,6 +942,16 @@ l select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); + if (table_list->vers_conditions) + { + if (table_list->is_view()) + { + my_error(ER_VERS_TRUNCATE_VIEW, MYF(0)); + DBUG_RETURN(true); + } + if (select_lex->vers_setup_conds(thd, table_list, conds)) + DBUG_RETURN(true); + } if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num)) || setup_fields(thd, Ref_ptr_array(), field_list, MARK_COLUMNS_READ, NULL, NULL, 0) || @@ -830,7 +961,7 @@ l if (!table_list->single_table_updatable() || check_key_in_view(thd, table_list)) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); DBUG_RETURN(TRUE); } @@ -922,7 +1053,7 @@ int mysql_multi_delete_prepare(THD *thd) check_key_in_view(thd, target_tbl->correspondent_table)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - target_tbl->table_name, "DELETE"); + target_tbl->table_name.str, "DELETE"); DBUG_RETURN(TRUE); } /* @@ -1066,7 +1197,7 @@ multi_delete::initialize_tables(JOIN *join) MEM_STRIP_BUF_SIZE); } init_ftfuncs(thd, thd->lex->current_select, 1); - DBUG_RETURN(thd->is_fatal_error != 0); + DBUG_RETURN(thd->is_fatal_error); } @@ -1106,6 +1237,11 @@ int multi_delete::send_data(List<Item> &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; + if (table->versioned() && !table->vers_end_field()->is_max()) + { + continue; + } + table->file->position(table->record[0]); found++; @@ -1118,7 +1254,9 @@ int multi_delete::send_data(List<Item> &values) TRG_ACTION_BEFORE, FALSE)) DBUG_RETURN(1); table->status|= STATUS_DELETED; - if (!(error=table->file->ha_delete_row(table->record[0]))) + + error= table->delete_row(); + if (!error) { deleted++; if (!table->file->has_transactions()) @@ -1288,7 +1426,7 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, */ info.ignore_not_found_rows= 1; bool will_batch= !table->file->start_bulk_delete(); - while (!(local_error= info.read_record(&info)) && !thd->killed) + while (!(local_error= info.read_record()) && !thd->killed) { if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, @@ -1297,8 +1435,8 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, local_error= 1; break; } - - local_error= table->file->ha_delete_row(table->record[0]); + + local_error= table->delete_row(); if (local_error && !ignore) { table->file->print_error(local_error, MYF(0)); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 22bdab685e3..be5058b7c63 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -22,7 +22,7 @@ */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_derived.h" @@ -174,7 +174,7 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases) DBUG_ENTER("mysql_handle_single_derived"); DBUG_PRINT("enter", ("phases: 0x%x allowed: 0x%x alias: '%s'", phases, allowed_phases, - (derived->alias ? derived->alias : "<NULL>"))); + (derived->alias.str ? derived->alias.str : "<NULL>"))); if (!lex->derived_tables) DBUG_RETURN(FALSE); @@ -365,7 +365,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) Query_arena *arena, backup; DBUG_ENTER("mysql_derived_merge"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); if (derived->merged) @@ -518,7 +518,7 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_merge_for_insert"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); DBUG_PRINT("info", ("merged_for_insert: %d is_materialized_derived: %d " "is_multitable: %d single_table_updatable: %d " @@ -576,7 +576,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX_UNIT *unit= derived->get_unit(); DBUG_ENTER("mysql_derived_init"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); // Skip already prepared views/DT @@ -651,11 +651,10 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived) bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) { SELECT_LEX_UNIT *unit= derived->get_unit(); - DBUG_ENTER("mysql_derived_prepare"); bool res= FALSE; - DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), - unit)); + DBUG_ENTER("mysql_derived_prepare"); + DBUG_PRINT("enter", ("unit: %p table_list: %p alias: '%s'", + unit, derived, derived->alias.str)); if (!unit) DBUG_RETURN(FALSE); @@ -696,7 +695,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) (first_select->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), - derived->alias, FALSE, FALSE, FALSE, 0); + &derived->alias, FALSE, FALSE, FALSE, 0); thd->create_tmp_table_for_derived= FALSE; if (!res && !derived->table) @@ -800,7 +799,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) (first_select->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), - derived->alias, + &derived->alias, FALSE, FALSE, FALSE, 0)) { @@ -825,8 +824,8 @@ exit: thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST)) { thd->clear_error(); - my_error(ER_VIEW_INVALID, MYF(0), derived->db, - derived->table_name); + my_error(ER_VIEW_INVALID, MYF(0), derived->db.str, + derived->table_name.str); } } @@ -896,11 +895,10 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX_UNIT *unit= derived->get_unit(); SELECT_LEX *first_select= unit->first_select(); SELECT_LEX *save_current_select= lex->current_select; - bool res= FALSE; DBUG_ENTER("mysql_derived_optimize"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); if (derived->merged) { @@ -986,7 +984,7 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_create"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); TABLE *table= derived->table; SELECT_LEX_UNIT *unit= derived->get_unit(); @@ -1087,7 +1085,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) bool res= FALSE; DBUG_ENTER("mysql_derived_fill"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); if (unit->executed && !unit->uncacheable && !unit->describe && @@ -1209,7 +1207,7 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_reinit"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (derived->alias ? derived->alias : "<NULL>"), + (derived->alias.str ? derived->alias.str : "<NULL>"), derived->get_unit())); st_select_lex_unit *unit= derived->get_unit(); @@ -1321,7 +1319,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) continue; extracted_cond_copy= !sl->next_select() ? extracted_cond : - extracted_cond->build_clone(thd, thd->mem_root); + extracted_cond->build_clone(thd); if (!extracted_cond_copy) continue; @@ -1351,7 +1349,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) */ extracted_cond_copy= !sl->next_select() ? extracted_cond : - extracted_cond->build_clone(thd, thd->mem_root); + extracted_cond->build_clone(thd); if (!extracted_cond_copy) continue; diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc index 1b41f7dadfc..54ed1c3f225 100644 --- a/sql/sql_digest.cc +++ b/sql/sql_digest.cc @@ -18,7 +18,7 @@ This code needs extra visibility in the lexer structures */ -#include "my_global.h" +#include "mariadb.h" #include "my_md5.h" #include "unireg.h" @@ -31,11 +31,6 @@ #include "sql_get_diagnostics.h" -#ifdef NEVER -#include "my_sys.h" -#include "sql_signal.h" -#endif - /* Generated code */ #include "sql_yacc.h" #define LEX_TOKEN_WITH_DEFINITION @@ -154,7 +149,7 @@ inline void store_token_identifier(sql_digest_storage* digest_storage, /* Write the string data */ if (id_length > 0) memcpy((char *)(dest + 4), id_name, id_length); - digest_storage->m_byte_count+= bytes_needed; + digest_storage->m_byte_count+= (uint)bytes_needed; } else { diff --git a/sql/sql_digest.h b/sql/sql_digest.h index eaf74b9542e..81fe809b59d 100644 --- a/sql/sql_digest.h +++ b/sql/sql_digest.h @@ -54,10 +54,10 @@ struct sql_digest_storage reset(NULL, 0); } - inline void reset(unsigned char *token_array, uint length) + inline void reset(unsigned char *token_array, size_t length) { m_token_array= token_array; - m_token_array_length= length; + m_token_array_length= (uint)length; reset(); } diff --git a/sql/sql_do.cc b/sql/sql_do.cc index da7dfe0c137..20a7aa75590 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -16,7 +16,7 @@ /* Execute DO statement */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "transaction.h" #include "unireg.h" @@ -29,7 +29,7 @@ bool mysql_do(THD *thd, List<Item> &values) List_iterator<Item> li(values); Item *value; DBUG_ENTER("mysql_do"); - if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_NONE, 0, NULL, 0)) + if (setup_fields(thd, Ref_ptr_array(), values, COLUMNS_READ, 0, NULL, 0)) DBUG_RETURN(TRUE); while ((value = li++)) (void) value->is_null(); diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 5d70a543771..67440aeed33 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -41,7 +41,7 @@ This file contains the implementation of error and warnings related ***********************************************************************/ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_error.h" @@ -523,8 +523,7 @@ Warning_info::~Warning_info() } -bool Warning_info::has_sql_condition(const char *message_str, - ulong message_length) const +bool Warning_info::has_sql_condition(const char *message_str, size_t message_length) const { Diagnostics_area::Sql_condition_iterator it(m_warn_list); const Sql_condition *err; @@ -769,12 +768,12 @@ void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level, TRUE Error sending data to client */ -const LEX_STRING warning_level_names[]= +const LEX_CSTRING warning_level_names[]= { - { C_STRING_WITH_LEN("Note") }, - { C_STRING_WITH_LEN("Warning") }, - { C_STRING_WITH_LEN("Error") }, - { C_STRING_WITH_LEN("?") } + { STRING_WITH_LEN("Note") }, + { STRING_WITH_LEN("Warning") }, + { STRING_WITH_LEN("Error") }, + { STRING_WITH_LEN("?") } }; bool mysqld_show_warnings(THD *thd, ulong levels_to_show) @@ -918,11 +917,11 @@ char *err_conv(char *buff, uint to_length, const char *from, length of converted string */ -uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, - const char *from, uint32 from_length, +size_t convert_error_message(char *to, size_t to_length, CHARSET_INFO *to_cs, + const char *from, size_t from_length, CHARSET_INFO *from_cs, uint *errors) { - int cnvres; + int cnvres; my_wc_t wc; const uchar *from_end= (const uchar*) from+from_length; char *to_start= to; @@ -930,7 +929,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc; my_charset_conv_wc_mb wc_mb; uint error_count= 0; - uint length; + size_t length; DBUG_ASSERT(to_length > 0); /* Make room for the null terminator. */ @@ -969,7 +968,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/; if ((uchar*)(to + length) >= to_end) break; - cnvres= my_snprintf(to, 9, + cnvres= (int)my_snprintf(to, 9, (wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc); to+= cnvres; } @@ -979,7 +978,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, *to= 0; *errors= error_count; - return (uint32) (to - to_start); + return (size_t) (to - to_start); } diff --git a/sql/sql_error.h b/sql/sql_error.h index 263c5843a4a..67c6e50d283 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -17,12 +17,12 @@ #ifndef SQL_ERROR_H #define SQL_ERROR_H -#include "sql_list.h" /* Sql_alloc, MEM_ROOT */ -#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_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 "decimal.h" class THD; @@ -591,7 +591,7 @@ private: @return true if the Warning_info contains an SQL-condition with the given message. */ - bool has_sql_condition(const char *message_str, ulong message_length) const; + bool has_sql_condition(const char *message_str, size_t message_length) const; /** Reset the warning information. Clear all warnings, @@ -1089,7 +1089,7 @@ public: ulong current_statement_warn_count() const { return get_warning_info()->current_statement_warn_count(); } - bool has_sql_condition(const char *message_str, ulong message_length) const + bool has_sql_condition(const char *message_str, size_t message_length) const { return get_warning_info()->has_sql_condition(message_str, message_length); } void reset_for_next_command() @@ -1174,7 +1174,7 @@ public: void copy_non_errors_from_wi(THD *thd, const Warning_info *src_wi); -private: +protected: Warning_info *get_warning_info() { return m_wi_stack.front(); } const Warning_info *get_warning_info() const { return m_wi_stack.front(); } @@ -1240,12 +1240,12 @@ void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level, bool mysqld_show_warnings(THD *thd, ulong levels_to_show); -uint32 convert_error_message(char *to, uint32 to_length, +size_t convert_error_message(char *to, size_t to_length, CHARSET_INFO *to_cs, - const char *from, uint32 from_length, + const char *from, size_t from_length, CHARSET_INFO *from_cs, uint *errors); -extern const LEX_STRING warning_level_names[]; +extern const LEX_CSTRING warning_level_names[]; bool is_sqlstate_valid(const LEX_CSTRING *sqlstate); /** diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index c82948e52ae..5d977c6d5c2 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_select.h" #include "my_json_writer.h" @@ -445,13 +445,13 @@ uint Explain_union::make_union_table_name(char *buf) default: DBUG_ASSERT(0); } - memcpy(buf, type.str, (len= type.length)); + memcpy(buf, type.str, (len= (uint)type.length)); for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN; childno++) { len+= lastop; - lastop= my_snprintf(buf + len, NAME_LEN - len, + lastop= (uint)my_snprintf(buf + len, NAME_LEN - len, "%u,", union_members.at(childno)); } @@ -1149,33 +1149,37 @@ void Explain_table_access::fill_key_len_str(String *key_len_str) const } -void Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg) +bool Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg) { - set_pseudo_key(mem_root, key->name); + if (set_pseudo_key(mem_root, key->name.str)) + return 1; + key_len= key_len_arg; uint len= 0; for (uint i= 0; i < key->usable_key_parts; i++) { - key_parts_list.append_str(mem_root, - key->key_part[i].field->field_name.str); + if (!key_parts_list.append_str(mem_root, + key->key_part[i].field->field_name.str)) + return 1; len += key->key_part[i].store_length; if (len >= key_len_arg) break; } + return 0; } -void Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg) +bool Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg) { if (key_name_arg) { - size_t name_len= strlen(key_name_arg); - if ((key_name= (char*)alloc_root(root, name_len+1))) - memcpy(key_name, key_name_arg, name_len+1); + if (!(key_name= strdup_root(root, key_name_arg))) + return 1; } else key_name= NULL; key_len= ~(uint) 0; + return 0; } @@ -1815,9 +1819,9 @@ const char * extra_tag_text[]= "Using join buffer", // special handling - "const row not found", - "unique row not found", - "Impossible ON condition" + "Const row not found", + "Unique row not found", + "Impossible ON condition", }; @@ -2456,7 +2460,11 @@ int Explain_range_checked_fer::append_possible_keys_stat(MEM_ROOT *alloc, for (j= 0; j < table->s->keys; j++) { if (possible_keys.is_set(j)) - keys_stat_names[j]= key_set.append_str(alloc, table->key_info[j].name); + { + if (!(keys_stat_names[j]= key_set.append_str(alloc, + table->key_info[j].name.str))) + return 1; + } else keys_stat_names[j]= NULL; } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 16781adf0f4..38250cc40ce 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -596,8 +596,8 @@ public: key_name= NULL; key_len= (uint)-1; } - void set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); - void set_pseudo_key(MEM_ROOT *root, const char *key_name); + bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); + bool set_pseudo_key(MEM_ROOT *root, const char *key_name); inline const char *get_key_name() const { return key_name; } inline uint get_key_len() const { return key_len; } diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc index c79783bf561..15c6cc57efb 100644 --- a/sql/sql_expression_cache.cc +++ b/sql/sql_expression_cache.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "sql_select.h" #include "sql_expression_cache.h" @@ -97,6 +97,7 @@ void Expression_cache_tmptable::init() List_iterator<Item> li(items); Item_iterator_list it(li); uint field_counter; + LEX_CSTRING cache_table_name= { STRING_WITH_LEN("subquery-cache-table") }; DBUG_ENTER("Expression_cache_tmptable::init"); DBUG_ASSERT(!inited); inited= TRUE; @@ -124,7 +125,7 @@ void Expression_cache_tmptable::init() TMP_TABLE_ALL_COLUMNS) & ~TMP_TABLE_FORCE_MYISAM), HA_POS_ERROR, - (char *)"subquery-cache-table", + &cache_table_name, TRUE))) { DBUG_PRINT("error", ("create_tmp_table failed, caching switched off")); diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc index d21fa7f4752..e7ab6cc3c75 100644 --- a/sql/sql_get_diagnostics.cc +++ b/sql/sql_get_diagnostics.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ +#include "mariadb.h" #include "sql_list.h" // Sql_alloc, List, List_iterator #include "sql_cmd.h" // Sql_cmd #include "sql_class.h" // Diagnostics_area diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index a2b53045815..ddc9c4a99d7 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -52,7 +52,7 @@ cursor points at the first record). */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_handler.h" #include "sql_base.h" // close_thread_tables @@ -116,7 +116,7 @@ static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len, my_bool first __attribute__((unused))) { *key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */ - return table->handler_name.str; + return (char*) table->handler_name.str; } @@ -139,6 +139,48 @@ static void mysql_ha_hash_free(SQL_HANDLER *table) delete table; } +static void mysql_ha_close_childs(THD *thd, TABLE_LIST *current_table_list, + TABLE_LIST **next_global) +{ + TABLE_LIST *table_list; + DBUG_ENTER("mysql_ha_close_childs"); + DBUG_PRINT("info",("current_table_list: %p", current_table_list)); + DBUG_PRINT("info",("next_global: %p", *next_global)); + for (table_list = *next_global; table_list; table_list = *next_global) + { + *next_global = table_list->next_global; + DBUG_PRINT("info",("table_name: %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + DBUG_PRINT("info",("parent_l: %p", table_list->parent_l)); + if (table_list->parent_l == current_table_list) + { + DBUG_PRINT("info",("found child")); + TABLE *table = table_list->table; + if (table) + { + table->open_by_handler= 0; + if (!table->s->tmp_table) + { + (void) close_thread_table(thd, &table); + thd->mdl_context.release_lock(table_list->mdl_request.ticket); + } + else + { + thd->mark_tmp_table_as_free_for_reuse(table); + } + } + mysql_ha_close_childs(thd, table_list, next_global); + } + else + { + /* the end of child tables */ + *next_global = table_list; + break; + } + } + DBUG_VOID_RETURN; +} + /** Close a HANDLER table. @@ -155,11 +197,16 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) { THD *thd= handler->thd; TABLE *table= handler->table; + TABLE_LIST *current_table_list= NULL, *next_global; /* check if table was already closed */ if (!table) return; + if ((next_global= table->file->get_next_global_for_child())) + current_table_list= next_global->parent_l; + + table->open_by_handler= 0; if (!table->s->tmp_table) { /* Non temporary table. */ @@ -170,15 +217,17 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) } table->file->ha_index_or_rnd_end(); - table->open_by_handler= 0; close_thread_table(thd, &table); + if (current_table_list) + mysql_ha_close_childs(thd, current_table_list, &next_global); thd->mdl_context.release_lock(handler->mdl_request.ticket); } else { /* Must be a temporary table */ table->file->ha_index_or_rnd_end(); - table->open_by_handler= 0; + if (current_table_list) + mysql_ha_close_childs(thd, current_table_list, &next_global); thd->mark_tmp_table_as_free_for_reuse(table); } my_free(handler->lock); @@ -217,7 +266,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) Query_arena backup_arena; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", - tables->db, tables->table_name, tables->alias, + tables->db.str, tables->table_name.str, tables->alias.str, reopen != 0)); if (thd->locked_tables_mode) @@ -249,12 +298,12 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) } else if (! reopen) /* Otherwise we have 'tables' already. */ { - if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias, - strlen(tables->alias) + 1)) + if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias.str, + tables->alias.length + 1)) { - DBUG_PRINT("info",("duplicate '%s'", tables->alias)); + DBUG_PRINT("info",("duplicate '%s'", tables->alias.str)); DBUG_PRINT("exit",("ERROR")); - my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias); + my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias.str); DBUG_RETURN(TRUE); } } @@ -281,7 +330,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) right from the start as open_tables() can't handle properly back-off for such locks. */ - tables->mdl_request.init(MDL_key::TABLE, tables->db, tables->table_name, + tables->mdl_request.init(MDL_key::TABLE, tables->db.str, tables->table_name.str, MDL_SHARED_READ, MDL_TRANSACTION); mdl_savepoint= thd->mdl_context.mdl_savepoint(); @@ -309,15 +358,24 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) goto err; } - if (tables->mdl_request.ticket && - thd->mdl_context.has_lock(mdl_savepoint, tables->mdl_request.ticket)) + DBUG_PRINT("info",("clone_tickets start")); + for (TABLE_LIST *table_list= tables; table_list; + table_list= table_list->next_global) + { + DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + if (table_list->mdl_request.ticket && + thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket)) { + DBUG_PRINT("info",("clone_tickets")); /* The ticket returned is within a savepoint. Make a copy. */ - error= thd->mdl_context.clone_ticket(&tables->mdl_request); - tables->table->mdl_ticket= tables->mdl_request.ticket; + error= thd->mdl_context.clone_ticket(&table_list->mdl_request); + table_list->table->mdl_ticket= table_list->mdl_request.ticket; if (error) goto err; } + } + DBUG_PRINT("info",("clone_tickets end")); if (! reopen) { @@ -327,12 +385,12 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) init_alloc_root(&sql_handler->mem_root, "sql_handler", 1024, 0, MYF(MY_THREAD_SPECIFIC)); - sql_handler->db.length= strlen(tables->db); - sql_handler->table_name.length= strlen(tables->table_name); - sql_handler->handler_name.length= strlen(tables->alias); + sql_handler->db.length= tables->db.length; + sql_handler->table_name.length= tables->table_name.length; + sql_handler->handler_name.length= tables->alias.length; if (!(my_multi_malloc(MY_WME, - &sql_handler->db.str, + &sql_handler->base_data, (uint) sql_handler->db.length + 1, &sql_handler->table_name.str, (uint) sql_handler->table_name.length + 1, @@ -340,12 +398,12 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) (uint) sql_handler->handler_name.length + 1, NullS))) goto err; - sql_handler->base_data= sql_handler->db.str; // Free this - memcpy(sql_handler->db.str, tables->db, sql_handler->db.length +1); - memcpy(sql_handler->table_name.str, tables->table_name, - sql_handler->table_name.length+1); - memcpy(sql_handler->handler_name.str, tables->alias, - sql_handler->handler_name.length +1); + sql_handler->db.str= sql_handler->base_data; + memcpy((char*) sql_handler->db.str, tables->db.str, tables->db.length +1); + memcpy((char*) sql_handler->table_name.str, tables->table_name.str, + tables->table_name.length+1); + memcpy((char*) sql_handler->handler_name.str, tables->alias.str, + tables->alias.length +1); /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler)) @@ -379,26 +437,37 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) /* Restore the state. */ thd->set_open_tables(backup_open_tables); + DBUG_PRINT("info",("set_lock_duration start")); if (sql_handler->mdl_request.ticket) { thd->mdl_context.set_lock_duration(sql_handler->mdl_request.ticket, MDL_EXPLICIT); thd->mdl_context.set_needs_thr_lock_abort(TRUE); } + for (TABLE_LIST *table_list= tables->next_global; table_list; + table_list= table_list->next_global) + { + DBUG_PRINT("info",("table_list %s.%s", table_list->table->s->db.str, + table_list->table->s->table_name.str)); + if (table_list->mdl_request.ticket) + { + thd->mdl_context.set_lock_duration(table_list->mdl_request.ticket, + MDL_EXPLICIT); + thd->mdl_context.set_needs_thr_lock_abort(TRUE); + } + } + DBUG_PRINT("info",("set_lock_duration end")); /* - Assert that the above check prevents opening of views and merge tables. - For temporary tables, TABLE::next can be set even if only one table - was opened for HANDLER as it is used to link them together. - */ - DBUG_ASSERT(sql_handler->table->next == NULL || - sql_handler->table->s->tmp_table); - /* If it's a temp table, don't reset table->query_id as the table is being used by this handler. For non-temp tables we use this flag in asserts. */ - table->open_by_handler= 1; + for (TABLE_LIST *table_list= tables; table_list; + table_list= table_list->next_global) + { + table_list->table->open_by_handler= 1; + } /* Safety, cleanup the pointer to satisfy MDL assertions. */ tables->mdl_request.ticket= NULL; @@ -452,7 +521,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) SQL_HANDLER *handler; DBUG_ENTER("mysql_ha_close"); DBUG_PRINT("enter",("'%s'.'%s' as '%s'", - tables->db, tables->table_name, tables->alias)); + tables->db.str, tables->table_name.str, tables->alias.str)); if (thd->locked_tables_mode) { @@ -461,15 +530,15 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) } if ((my_hash_inited(&thd->handler_tables_hash)) && (handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash, - (uchar*) tables->alias, - strlen(tables->alias) + 1))) + (const uchar*) tables->alias.str, + tables->alias.length + 1))) { mysql_ha_close_table(handler); my_hash_delete(&thd->handler_tables_hash, (uchar*) handler); } else { - my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER"); + my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias.str, "HANDLER"); DBUG_PRINT("exit",("ERROR")); DBUG_RETURN(TRUE); } @@ -496,13 +565,13 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) @return handler */ -SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name) +static SQL_HANDLER *mysql_ha_find_handler(THD *thd, const LEX_CSTRING *name) { SQL_HANDLER *handler; if ((my_hash_inited(&thd->handler_tables_hash)) && (handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash, - (uchar*) name, - strlen(name) + 1))) + (const uchar*) name->str, + name->length + 1))) { DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p", handler->db.str, @@ -512,9 +581,8 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name) { /* The handler table has been closed. Re-open it. */ TABLE_LIST tmp; - tmp.init_one_table(handler->db.str, handler->db.length, - handler->table_name.str, handler->table_name.length, - handler->handler_name.str, TL_READ); + tmp.init_one_table(&handler->db, &handler->table_name, + &handler->handler_name, TL_READ); if (mysql_ha_open(thd, &tmp, handler)) { @@ -525,7 +593,7 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name) } else { - my_error(ER_UNKNOWN_TABLE, MYF(0), name, "HANDLER"); + my_error(ER_UNKNOWN_TABLE, MYF(0), name->str, "HANDLER"); return 0; } return handler; @@ -572,7 +640,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, if (handler->keyno < 0 || my_strcasecmp(&my_charset_latin1, keyname, - table->s->key_info[handler->keyno].name)) + table->s->key_info[handler->keyno].name.str)) { if ((handler->keyno= find_type(keyname, &table->s->keynames, FIND_TYPE_NO_PREFIX) - 1) < 0) @@ -688,7 +756,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error; DBUG_ENTER("mysql_ha_read"); DBUG_PRINT("enter",("'%s'.'%s' as '%s'", - tables->db, tables->table_name, tables->alias)); + tables->db.str, tables->table_name.str, tables->alias.str)); if (thd->locked_tables_mode) { @@ -697,7 +765,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, } retry: - if (!(handler= mysql_ha_find_handler(thd, tables->alias))) + if (!(handler= mysql_ha_find_handler(thd, &tables->alias))) goto err0; table= handler->table; @@ -708,8 +776,14 @@ retry: { int lock_error; - if (handler->lock->lock_count > 0) - handler->lock->locks[0]->type= handler->lock->locks[0]->org_type; + THR_LOCK_DATA **pos,**end; + for (pos= handler->lock->locks, + end= handler->lock->locks + handler->lock->lock_count; + pos < end; + pos++) + { + pos[0]->type= pos[0]->org_type; + } /* save open_tables state */ TABLE* backup_open_tables= thd->open_tables; @@ -876,7 +950,7 @@ retry: if (error != HA_ERR_RECORD_CHANGED && error != HA_ERR_WRONG_COMMAND) sql_print_error("mysql_ha_read: Got error %d when reading " "table '%s'", - error, tables->table_name); + error, tables->table_name.str); table->file->print_error(error,MYF(0)); table->file->ha_index_or_rnd_end(); goto err; @@ -933,7 +1007,7 @@ SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, { SQL_HANDLER *handler; DBUG_ENTER("mysql_ha_read_prepare"); - if (!(handler= mysql_ha_find_handler(thd, tables->alias))) + if (!(handler= mysql_ha_find_handler(thd, &tables->alias))) DBUG_RETURN(0); tables->table= handler->table; // This is used by fix_fields if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1)) @@ -969,7 +1043,7 @@ static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables) { if (tables->is_anonymous_derived_table()) continue; - if ((! *tables->db || + if ((! tables->db.str[0] || ! my_strcasecmp(&my_charset_latin1, hash_tables->db.str, tables->get_db_name())) && ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str, diff --git a/sql/sql_handler.h b/sql/sql_handler.h index cb1aff1d727..ffefec91fad 100644 --- a/sql/sql_handler.h +++ b/sql/sql_handler.h @@ -31,9 +31,9 @@ public: TABLE *table; List<Item> fields; /* Fields, set on open */ THD *thd; - LEX_STRING handler_name; - LEX_STRING db; - LEX_STRING table_name; + LEX_CSTRING handler_name; + LEX_CSTRING db; + LEX_CSTRING table_name; MEM_ROOT mem_root; MYSQL_LOCK *lock; MDL_request mdl_request; diff --git a/sql/sql_help.cc b/sql/sql_help.cc index bd11397e2aa..da38a2caf94 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_help.h" @@ -202,7 +202,7 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (!select->cond->val_int()) // Doesn't match like continue; @@ -246,7 +246,7 @@ int search_keyword(THD *thd, TABLE *keywords, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info) && count<2) + while (!read_record_info.read_record() && count<2) { if (!select->cond->val_int()) // Dosn't match like continue; @@ -380,7 +380,7 @@ int search_categories(THD *thd, TABLE *categories, if (init_read_record(&read_record_info, thd, categories, select, NULL, 1, 0, FALSE)) DBUG_RETURN(0); - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (select && !select->cond->val_int()) continue; @@ -418,7 +418,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname, FALSE)) DBUG_VOID_RETURN; - while (!read_record_info.read_record(&read_record_info)) + while (!read_record_info.read_record()) { if (!select->cond->val_int()) continue; @@ -645,7 +645,7 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, # created SQL_SELECT */ -SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen, +SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, size_t mlen, TABLE_LIST *tables, TABLE *table, Field *pfname, int *error) { @@ -654,7 +654,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen, Item_func_like(thd, new (mem_root) Item_field(thd, pfname), - new (mem_root) Item_string(thd, mask, mlen, + new (mem_root) Item_string(thd, mask, (uint)mlen, pfname->charset()), new (mem_root) Item_string_ascii(thd, "\\"), FALSE); @@ -686,23 +686,19 @@ static bool mysqld_help_internal(THD *thd, const char *mask) List<String> topics_list, categories_list, subcategories_list; String name, description, example; int count_topics, count_categories, error; - uint mlen= strlen(mask); + size_t mlen= strlen(mask); size_t i; MEM_ROOT *mem_root= thd->mem_root; + LEX_CSTRING MYSQL_HELP_TOPIC_NAME= {STRING_WITH_LEN("help_topic") }; + LEX_CSTRING MYSQL_HELP_CATEGORY_NAME= {STRING_WITH_LEN("help_category") }; + LEX_CSTRING MYSQL_HELP_RELATION_NAME= {STRING_WITH_LEN("help_relation") }; + LEX_CSTRING MYSQL_HELP_KEYWORD_NAME= {STRING_WITH_LEN("help_keyword") }; DBUG_ENTER("mysqld_help"); - tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("help_topic"), - "help_topic", TL_READ); - tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("help_category"), - "help_category", TL_READ); - tables[2].init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("help_relation"), - "help_relation", TL_READ); - tables[3].init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("help_keyword"), - "help_keyword", TL_READ); + tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_TOPIC_NAME, 0, TL_READ); + tables[1].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_CATEGORY_NAME, 0, TL_READ); + tables[2].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_RELATION_NAME, 0, TL_READ); + tables[3].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_KEYWORD_NAME, 0, TL_READ); tables[0].next_global= tables[0].next_local= tables[0].next_name_resolution_table= &tables[1]; tables[1].next_global= tables[1].next_local= diff --git a/sql/sql_hset.h b/sql/sql_hset.h index 4dfddf898f0..dfaf17cf55c 100644 --- a/sql/sql_hset.h +++ b/sql/sql_hset.h @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "my_global.h" #include "hash.h" diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e5d2f88a857..84ec1513cf4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -56,7 +56,7 @@ */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_insert.h" #include "sql_update.h" // compare_record @@ -168,9 +168,9 @@ static bool check_view_single_update(List<Item> &fields, List<Item> *values, if (!tbl->single_table_updatable()) { if (insert) - my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias.str, "INSERT"); else - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "UPDATE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias.str, "UPDATE"); return TRUE; } *map= tables; @@ -210,7 +210,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (!table_list->single_table_updatable()) { - my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT"); DBUG_RETURN(-1); } @@ -222,7 +222,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(-1); } - if (values.elements != table->s->fields) + if (values.elements != table->s->visible_fields) { my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); @@ -304,7 +304,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, (table_list->view && check_view_insertability(thd, table_list))) { - my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT"); DBUG_RETURN(-1); } @@ -676,6 +676,12 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list) } +Field **TABLE::field_to_fill() +{ + return triggers && triggers->nullable_fields() ? triggers->nullable_fields() : field; +} + + /** INSERT statement implementation @@ -736,11 +742,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, */ if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables_mode && - find_locked_table(thd->open_tables, table_list->db, - table_list->table_name)) + find_locked_table(thd->open_tables, table_list->db.str, + table_list->table_name.str)) { my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0), - table_list->table_name); + table_list->table_name.str); DBUG_RETURN(TRUE); } /* @@ -980,7 +986,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, No field list, all fields are set explicitly: INSERT INTO t1 VALUES (values) */ - if (thd->lex->used_tables) // Column used in values() + if (thd->lex->used_tables || // Column used in values() + table->s->visible_fields != table->s->fields) restore_record(table,s->default_values); // Get empty record else { @@ -1138,8 +1145,10 @@ values_loop_end: } else errcode= query_error_code(thd, thd->killed == NOT_KILLED); - - /* bug#22725: + + ScopedStatementReplication scoped_stmt_rpl( + table->versioned(VERS_TRX_ID) ? thd : NULL); + /* bug#22725: A query which per-row-loop can not be interrupted with KILLED, like INSERT, and that does not invoke stored @@ -1290,7 +1299,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) uint used_fields_buff_size= bitmap_buffer_size(table->s->fields); uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size); MY_BITMAP used_fields; - enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + enum_column_usage saved_column_usage= thd->column_usage; DBUG_ENTER("check_key_in_view"); if (!used_fields_buff) @@ -1306,20 +1315,20 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) we must not set query_id for fields as they're not really used in this context */ - thd->mark_used_columns= MARK_COLUMNS_NONE; + thd->column_usage= COLUMNS_WRITE; /* check simplicity and prepare unique test of view */ for (trans= trans_start; trans != trans_end; trans++) { if (!trans->item->fixed && trans->item->fix_fields(thd, &trans->item)) { - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; DBUG_RETURN(TRUE); } Item_field *field; /* simple SELECT list entry (field without expression) */ if (!(field= trans->item->field_for_view_update())) { - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; DBUG_RETURN(TRUE); } if (field->field->unireg_check == Field::NEXT_NUMBER) @@ -1331,7 +1340,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) */ trans->item= field; } - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; /* unique test */ for (trans= trans_start; trans != trans_end; trans++) { @@ -1372,7 +1381,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, if (!table_list->single_table_updatable()) { - my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT"); DBUG_RETURN(TRUE); } /* @@ -1559,6 +1568,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!table) table= table_list->table; + if (table->versioned(VERS_TIMESTAMP) && duplic == DUP_REPLACE) + { + // Additional memory may be required to create historical items. + if (table_list->set_insert_values(thd->mem_root)) + DBUG_RETURN(TRUE); + } + if (!select_insert) { Item *fake_conds= 0; @@ -1610,6 +1626,24 @@ static int last_uniq_key(TABLE *table,uint keynr) /* + Inserts one historical row to a table. + + Copies content of the row from table->record[1] to table->record[0], + sets Sys_end to now() and calls ha_write_row() . +*/ + +int vers_insert_history_row(TABLE *table) +{ + DBUG_ASSERT(table->versioned(VERS_TIMESTAMP)); + restore_record(table,record[1]); + + // Set Sys_end to now() + table->vers_update_end(); + + return table->file->ha_write_row(table->record[0]); +} + +/* Write a record to table with optional deleting of conflicting records, invoke proper triggers if needed. @@ -1813,7 +1847,26 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } if (error != HA_ERR_RECORD_IS_THE_SAME) + { info->updated++; + if (table->versioned()) + { + if (table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + if ((error= vers_insert_history_row(table))) + { + info->last_errno= error; + table->file->print_error(error, MYF(0)); + trg_error= 1; + restore_record(table, record[2]); + goto ok_or_after_trg_err; + } + restore_record(table, record[2]); + } + info->copied++; + } + } else error= 0; /* @@ -1865,17 +1918,35 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) tables which have ON UPDATE but have no ON DELETE triggers, we just should not expose this fact to users by invoking ON UPDATE triggers. - */ - if (last_uniq_key(table,key_nr) && - !table->file->referenced_by_foreign_key() && + For system versioning wa also use path through delete since we would + save nothing through this cheating. + */ + if (last_uniq_key(table,key_nr) && + !table->file->referenced_by_foreign_key() && (!table->triggers || !table->triggers->has_delete_triggers())) { + if (table->versioned(VERS_TRX_ID)) + { + bitmap_set_bit(table->write_set, table->vers_start_field()->field_index); + table->vers_start_field()->set_notnull(); + table->vers_start_field()->store(0, false); + } if ((error=table->file->ha_update_row(table->record[1], - table->record[0])) && + table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME) goto err; if (error != HA_ERR_RECORD_IS_THE_SAME) + { info->deleted++; + if (table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + error= vers_insert_history_row(table); + restore_record(table, record[2]); + if (error) + goto err; + } + } else error= 0; thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row); @@ -1891,9 +1962,25 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE)) goto before_trg_err; - if ((error=table->file->ha_delete_row(table->record[1]))) + + if (!table->versioned(VERS_TIMESTAMP)) + error= table->file->ha_delete_row(table->record[1]); + else + { + DBUG_ASSERT(table->insert_values); + store_record(table,insert_values); + restore_record(table,record[1]); + table->vers_update_end(); + error= table->file->ha_update_row(table->record[1], + table->record[0]); + restore_record(table,insert_values); + } + if (error) goto err; - info->deleted++; + if (!table->versioned(VERS_TIMESTAMP)) + info->deleted++; + else + info->updated++; if (!table->file->has_transactions()) thd->transaction.stmt.modified_non_trans_table= TRUE; if (table->triggers && @@ -1981,7 +2068,9 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *t for (Field **field=entry->field ; *field ; field++) { if (!bitmap_is_set(write_set, (*field)->field_index) && - has_no_default_value(thd, *field, table_list)) + !(*field)->vers_sys_field() && + has_no_default_value(thd, *field, table_list) && + ((*field)->real_type() != MYSQL_TYPE_ENUM)) err=1; } return thd->abort_on_warning ? err : 0; @@ -2173,8 +2262,8 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) Delayed_insert *di; while ((di= it++)) { - if (!strcmp(table_list->db, di->table_list.db) && - !strcmp(table_list->table_name, di->table_list.table_name)) + if (!cmp(&table_list->db, &di->table_list.db) && + !cmp(&table_list->table_name, &di->table_list.table_name)) { di->lock(); break; @@ -2238,7 +2327,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, DBUG_ENTER("delayed_get_table"); /* Must be set in the parser */ - DBUG_ASSERT(table_list->db); + DBUG_ASSERT(table_list->db.str); /* Find the thread which handles this table. */ if (!(di= find_handler(thd, table_list))) @@ -2265,11 +2354,12 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, */ di->thd.variables.binlog_annotate_row_events= 0; - di->thd.set_db(table_list->db, (uint) strlen(table_list->db)); - di->thd.set_query(my_strdup(table_list->table_name, - MYF(MY_WME | ME_FATALERROR)), - 0, system_charset_info); - if (di->thd.db == NULL || di->thd.query() == NULL) + di->thd.set_db(&table_list->db); + di->thd.set_query(my_strndup(table_list->table_name.str, + table_list->table_name.length, + MYF(MY_WME | ME_FATALERROR)), + table_list->table_name.length, system_charset_info); + if (di->thd.db.str == NULL || di->thd.query() == NULL) { /* The error is reported */ delete di; @@ -2277,7 +2367,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, } di->table_list= *table_list; // Needed to open table /* Replace volatile strings with local copies */ - di->table_list.alias= di->table_list.table_name= di->thd.query(); + di->table_list.alias.str= di->table_list.table_name.str= di->thd.query(); + di->table_list.alias.length= di->table_list.table_name.length= di->thd.query_length(); di->table_list.db= di->thd.db; /* We need the tickets so that they can be cloned in handle_delayed_insert */ di->grl_protection.init(MDL_key::GLOBAL, "", "", @@ -2423,8 +2514,7 @@ 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 mysql_notify_thread_having_shared_lock() or - kill_delayed_threads_for_table(). + killed using kill_delayed_threads_for_table(). */ if (!thd.is_error()) my_message(ER_QUERY_INTERRUPTED, ER_THD(&thd, ER_QUERY_INTERRUPTED), @@ -2760,7 +2850,7 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, if (!(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) { - my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name); + my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name.str); return TRUE; } return FALSE; @@ -2845,7 +2935,7 @@ pthread_handler_t handle_delayed_insert(void *arg) pthread_detach_this_thread(); /* Add thread to THD list so that's it's visible in 'show processlist' */ - thd->set_current_time(); + thd->set_start_time(); add_to_active_threads(thd); if (abort_loop) thd->set_killed(KILL_CONNECTION); @@ -3499,7 +3589,8 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par, select_result_interceptor(thd_arg), table_list(table_list_par), table(table_par), fields(fields_par), autoinc_value_of_last_inserted_row(0), - insert_into_view(table_list_par && table_list_par->view != 0) + insert_into_view(table_list_par && table_list_par->view != 0), + versioned_write(false) { bzero((char*) &info,sizeof(info)); info.handle_duplicates= duplic; @@ -3534,6 +3625,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) check_insert_fields(thd, table_list, *fields, values, !insert_into_view, 1, &map)); + versioned_write= table_list->table->versioned(); + if (!res && fields->elements) { bool saved_abort_on_warning= thd->abort_on_warning; @@ -3746,6 +3839,7 @@ int select_insert::send_data(List<Item> &values) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(1); } + table->vers_write= versioned_write; if (table_list) // Not CREATE ... SELECT { switch (table_list->view_check_option(thd, info.ignore)) { @@ -3757,6 +3851,7 @@ int select_insert::send_data(List<Item> &values) } error= write_record(thd, table, &info); + table->vers_write= table->versioned(); table->auto_increment_field_not_null= FALSE; if (!error) @@ -3795,12 +3890,16 @@ int select_insert::send_data(List<Item> &values) void select_insert::store_values(List<Item> &values) { + DBUG_ENTER("select_insert::store_values"); + if (fields->elements) fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1, TRG_EVENT_INSERT); else fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(), values, 1, TRG_EVENT_INSERT); + + DBUG_VOID_RETURN; } bool select_insert::prepare_eof() @@ -4026,10 +4125,7 @@ Field *Item::create_field_for_create_select(TABLE *table) @retval 0 Error */ -static TABLE *create_table_from_items(THD *thd, - Table_specification_st *create_info, - TABLE_LIST *create_table, - Alter_info *alter_info, +TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items, MYSQL_LOCK **lock, TABLEOP_HOOKS *hooks) @@ -4090,6 +4186,12 @@ static TABLE *create_table_from_items(THD *thd, alter_info->create_list.push_back(cr_field, thd->mem_root); } + if (create_info->vers_fix_system_fields(thd, alter_info, *create_table, + select_tables, items, &versioned_write)) + { + DBUG_RETURN(NULL); + } + DEBUG_SYNC(thd,"create_table_select_before_create"); /* Check if LOCK TABLES + CREATE OR REPLACE of existing normal table*/ @@ -4119,8 +4221,8 @@ static TABLE *create_table_from_items(THD *thd, open_table(). */ - if (!mysql_create_table_no_lock(thd, create_table->db, - create_table->table_name, + if (!mysql_create_table_no_lock(thd, &create_table->db, + &create_table->table_name, create_info, alter_info, NULL, select_field_count, create_table)) { @@ -4146,8 +4248,8 @@ static TABLE *create_table_from_items(THD *thd, */ if (open_table(thd, create_table, &ot_ctx)) { - quick_rm_table(thd, create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name), + quick_rm_table(thd, create_info->db_type, &create_table->db, + table_case_name(create_info, &create_table->table_name), 0); } /* Restore */ @@ -4204,7 +4306,7 @@ static TABLE *create_table_from_items(THD *thd, mysql_unlock_tables(thd, *lock); *lock= 0; } - drop_open_table(thd, table, create_table->db, create_table->table_name); + drop_open_table(thd, table, &create_table->db, &create_table->table_name); DBUG_RETURN(0); /* purecov: end */ } @@ -4213,8 +4315,9 @@ static TABLE *create_table_from_items(THD *thd, int -select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) +select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u) { + List<Item> values(_values, thd->mem_root); MYSQL_LOCK *extra_lock= NULL; DBUG_ENTER("select_create::prepare"); @@ -4296,10 +4399,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DEBUG_SYNC(thd,"create_table_select_before_check_if_exists"); - if (!(table= create_table_from_items(thd, create_info, - create_table, - alter_info, &values, - &extra_lock, hook_ptr))) + if (!(table= create_table_from_items(thd, &values, &extra_lock, hook_ptr))) /* abort() deletes table */ DBUG_RETURN(-1); @@ -4594,16 +4694,14 @@ void select_create::abort_result_set() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); table->auto_increment_field_not_null= FALSE; - drop_open_table(thd, table, create_table->db, create_table->table_name); + drop_open_table(thd, table, &create_table->db, &create_table->table_name); table=0; // Safety if (thd->log_current_statement && mysql_bin_log.is_open()) { /* Remove logging of drop, create + insert rows */ binlog_reset_cache(thd); /* Original table was deleted. We have to log it */ - log_drop_table(thd, create_table->db, create_table->db_length, - create_table->table_name, create_table->table_name_length, - tmp_table); + log_drop_table(thd, &create_table->db, &create_table->table_name, tmp_table); } } DBUG_VOID_RETURN; diff --git a/sql/sql_insert.h b/sql/sql_insert.h index aea0dac6b0d..6efd680d188 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -37,6 +37,7 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type, bool is_multi_insert); int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); +int vers_insert_history_row(TABLE *table); int write_record(THD *thd, TABLE *table, COPY_INFO *info); void kill_delayed_threads(void); diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 3612cb6cc32..fb8660aa79d 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -27,6 +27,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "key.h" #include "sql_base.h" #include "sql_select.h" @@ -764,7 +765,7 @@ uint JOIN_CACHE::get_record_max_affix_length() The minimal possible size of the join buffer of this cache */ -ulong JOIN_CACHE::get_min_join_buffer_size() +size_t JOIN_CACHE::get_min_join_buffer_size() { if (!min_buff_size) { @@ -823,7 +824,7 @@ ulong JOIN_CACHE::get_min_join_buffer_size() The maximum possible size of the join buffer of this cache */ -ulong JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size) +size_t JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size) { if (!max_buff_size) { @@ -932,9 +933,9 @@ int JOIN_CACHE::alloc_buffer() if (for_explain_only) return 0; - for (ulong buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; ) + for (size_t buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; ) { - ulong next_buff_size; + size_t next_buff_size; if ((buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC)))) break; @@ -1700,7 +1701,7 @@ enum JOIN_CACHE::Match_flag JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr) the number of bytes in the increment */ -uint JOIN_CACHE::aux_buffer_incr(ulong recno) +uint JOIN_CACHE::aux_buffer_incr(size_t recno) { return join_tab_scan->aux_buffer_incr(recno); } @@ -2570,10 +2571,11 @@ finish: BNLH, BKA or BKAH) to the data structure RETURN VALUE - none + 0 ok + 1 error */ -void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) +bool JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) { explain->incremental= MY_TEST(prev_cache); @@ -2595,6 +2597,7 @@ void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain) default: DBUG_ASSERT(0); } + return 0; } /** @@ -2607,7 +2610,7 @@ THD *JOIN_CACHE::thd() } -static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file) +static bool add_mrr_explain_info(String *str, uint mrr_mode, handler *file) { char mrr_str_buf[128]={0}; int len; @@ -2616,22 +2619,30 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file) if (len > 0) { if (str->length()) - str->append(STRING_WITH_LEN("; ")); - str->append(mrr_str_buf, len); + { + if (str->append(STRING_WITH_LEN("; "))) + return 1; + } + if (str->append(mrr_str_buf, len)) + return 1; } + return 0; } -void JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain) + +bool JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain) { - JOIN_CACHE::save_explain_data(explain); - add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); + if (JOIN_CACHE::save_explain_data(explain)) + return 1; + return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); } -void JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain) +bool JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain) { - JOIN_CACHE::save_explain_data(explain); - add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); + if (JOIN_CACHE::save_explain_data(explain)) + return 1; + return add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); } @@ -2748,22 +2759,22 @@ int JOIN_CACHE_HASHED::init_hash_table() size_of_key_ofs + // reference to the next key (use_emb_key ? get_size_of_rec_offset() : key_length); - ulong space_per_rec= avg_record_length + + size_t space_per_rec= avg_record_length + avg_aux_buffer_incr + key_entry_length+size_of_key_ofs; - uint n= buff_size / space_per_rec; + size_t n= buff_size / space_per_rec; /* TODO: Make a better estimate for this upper bound of the number of records in in the join buffer. */ - uint max_n= buff_size / (pack_length-length+ + size_t max_n= buff_size / (pack_length-length+ key_entry_length+size_of_key_ofs); hash_entries= (uint) (n / 0.7); set_if_bigger(hash_entries, 1); - if (offset_size(max_n*key_entry_length) <= + if (offset_size((uint)(max_n*key_entry_length)) <= size_of_key_ofs) break; } @@ -3372,7 +3383,7 @@ int JOIN_TAB_SCAN::next() if (is_first_record) is_first_record= FALSE; else - err= info->read_record(info); + err= info->read_record(); if (!err) { @@ -3387,7 +3398,7 @@ int JOIN_TAB_SCAN::next() Move to the next record if the last retrieved record does not meet the condition pushed to the table join_tab. */ - err= info->read_record(info); + err= info->read_record(); if (!err) { join_tab->tracker->r_rows++; @@ -3490,7 +3501,7 @@ bool JOIN_CACHE_BNL::prepare_look_for_matches(bool skip_last) if (!records) return TRUE; reset(FALSE); - rem_records= records - MY_TEST(skip_last); + rem_records= (uint)records - MY_TEST(skip_last); return rem_records == 0; } @@ -3818,7 +3829,7 @@ int JOIN_CACHE_BNLH::init(bool for_explain) the increment of the size of the MRR buffer for the next record */ -uint JOIN_TAB_SCAN_MRR::aux_buffer_incr(ulong recno) +uint JOIN_TAB_SCAN_MRR::aux_buffer_incr(size_t recno) { uint incr= 0; TABLE_REF *ref= &join_tab->ref; diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h index 4ae843ebfc2..c4ba08496d0 100644 --- a/sql/sql_join_cache.h +++ b/sql/sql_join_cache.h @@ -107,7 +107,7 @@ protected: /* 3 functions below actually do not use the hidden parameter 'this' */ /* Calculate the number of bytes used to store an offset value */ - uint offset_size(uint len) + uint offset_size(size_t len) { return (len < 256 ? 1 : len < 256*256 ? 2 : 4); } /* Get the offset value that takes ofs_sz bytes at the position ptr */ @@ -420,7 +420,7 @@ protected: incremented when a new record is added to the join buffer. If no auxiliary buffer is needed the function should return 0. */ - virtual uint aux_buffer_incr(ulong recno); + virtual uint aux_buffer_incr(size_t recno); /* Shall calculate how much space is remaining in the join buffer */ virtual size_t rem_space() @@ -606,9 +606,9 @@ public: void set_join_buffer_size(size_t sz) { buff_size= sz; } /* Get the minimum possible size of the cache join buffer */ - virtual ulong get_min_join_buffer_size(); + virtual size_t get_min_join_buffer_size(); /* Get the maximum possible size of the cache join buffer */ - virtual ulong get_max_join_buffer_size(bool optimize_buff_size); + virtual size_t get_max_join_buffer_size(bool optimize_buff_size); /* Shrink the size if the cache join buffer in a given ratio */ bool shrink_join_buffer_in_ratio(ulonglong n, ulonglong d); @@ -662,7 +662,7 @@ public: enum_nested_loop_state join_records(bool skip_last); /* Add a comment on the join algorithm employed by the join cache */ - virtual void save_explain_data(EXPLAIN_BKA_TYPE *explain); + virtual bool save_explain_data(EXPLAIN_BKA_TYPE *explain); THD *thd(); @@ -1069,7 +1069,7 @@ public: Shall calculate the increment of the auxiliary buffer for a record write if such a buffer is used by the table scan object */ - virtual uint aux_buffer_incr(ulong recno) { return 0; } + virtual uint aux_buffer_incr(size_t recno) { return 0; } /* Initiate the process of iteration over the joined table */ virtual int open(); @@ -1244,7 +1244,7 @@ public: JOIN_TAB_SCAN_MRR(JOIN *j, JOIN_TAB *tab, uint flags, RANGE_SEQ_IF rs_funcs) :JOIN_TAB_SCAN(j, tab), range_seq_funcs(rs_funcs), mrr_mode(flags) {} - uint aux_buffer_incr(ulong recno); + uint aux_buffer_incr(size_t recno); int open(); @@ -1325,6 +1325,10 @@ public: JOIN_CACHE_BKA(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev) :JOIN_CACHE(j, tab, prev), mrr_mode(flags) {} + JOIN_CACHE_BKA(JOIN_CACHE_BKA *bka) + :JOIN_CACHE(bka->join, bka->join_tab, bka->prev_cache), + mrr_mode(bka->mrr_mode) {} + uchar **get_curr_association_ptr() { return &curr_association; } /* Initialize the BKA cache */ @@ -1340,7 +1344,7 @@ public: /* Check index condition of the joined table for a record from BKA cache */ bool skip_index_tuple(range_id_t range_info); - void save_explain_data(EXPLAIN_BKA_TYPE *explain); + bool save_explain_data(EXPLAIN_BKA_TYPE *explain); }; @@ -1421,6 +1425,10 @@ public: JOIN_CACHE_BKAH(JOIN *j, JOIN_TAB *tab, uint flags, JOIN_CACHE *prev) :JOIN_CACHE_BNLH(j, tab, prev), mrr_mode(flags) {} + JOIN_CACHE_BKAH(JOIN_CACHE_BKAH *bkah) + :JOIN_CACHE_BNLH(bkah->join, bkah->join_tab, bkah->prev_cache), + mrr_mode(bkah->mrr_mode) {} + uchar **get_curr_association_ptr() { return &curr_matching_chain; } /* Initialize the BKAH cache */ @@ -1431,5 +1439,5 @@ public: /* Check index condition of the joined table for a record from BKAH cache */ bool skip_index_tuple(range_id_t range_info); - void save_explain_data(EXPLAIN_BKA_TYPE *explain); + bool save_explain_data(EXPLAIN_BKA_TYPE *explain); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b958a0b2e16..a2a9e5b2f16 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -18,7 +18,7 @@ /* A lexical scanner on a temporary buffer with a yacc interface */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_class.h" // sql_lex.h: SQLCOM_END #include "sql_lex.h" @@ -33,9 +33,9 @@ #include "sql_signal.h" -void LEX::parse_error() +void LEX::parse_error(uint err_number) { - thd->parse_error(); + thd->parse_error(err_number); } @@ -196,8 +196,9 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) lex_start(thd); context->init(); if ((!(table_ident= new Table_ident(thd, + &table->s->db, &table->s->table_name, - &table->s->db, TRUE))) || + TRUE))) || (!(table_list= select_lex->add_table_to_list(thd, table_ident, NULL, @@ -257,7 +258,7 @@ st_parsing_options::reset() bool Lex_input_stream::init(THD *thd, char* buff, - unsigned int length) + size_t length) { DBUG_EXECUTE_IF("bug42064_simulate_oom", DBUG_SET("+d,simulate_out_of_memory");); @@ -268,12 +269,12 @@ bool Lex_input_stream::init(THD *thd, DBUG_SET("-d,bug42064_simulate_oom");); if (m_cpp_buf == NULL) - return TRUE; + return true; m_thd= thd; reset(buff, length); - return FALSE; + return false; } @@ -286,7 +287,7 @@ bool Lex_input_stream::init(THD *thd, */ void -Lex_input_stream::reset(char *buffer, unsigned int length) +Lex_input_stream::reset(char *buffer, size_t length) { yylineno= 1; yylval= NULL; @@ -333,7 +334,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr) DBUG_ASSERT(begin_ptr); DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length); - uint body_utf8_length= get_body_utf8_maximum_length(thd); + size_t body_utf8_length= get_body_utf8_maximum_length(thd); m_body_utf8= (char *) thd->alloc(body_utf8_length + 1); m_body_utf8_ptr= m_body_utf8; @@ -343,7 +344,7 @@ void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr) } -uint Lex_input_stream::get_body_utf8_maximum_length(THD *thd) +size_t Lex_input_stream::get_body_utf8_maximum_length(THD *thd) { /* String literals can grow during escaping: @@ -683,7 +684,6 @@ void LEX::start(THD *thd_arg) context_stack.empty(); unit.init_query(); - unit.init_select(); current_select_number= 1; select_lex.linkage= UNSPECIFIED_TYPE; /* 'parent_lex' is used in init_query() so it must be before it. */ @@ -741,6 +741,7 @@ void LEX::start(THD *thd_arg) spcont= NULL; proc_list.first= 0; escape_used= FALSE; + default_used= FALSE; query_tables= 0; reset_query_tables_list(FALSE); expr_allows_subselect= TRUE; @@ -775,6 +776,8 @@ void LEX::start(THD *thd_arg) frame_bottom_bound= NULL; win_spec= NULL; + vers_conditions.empty(); + is_lex_started= TRUE; DBUG_VOID_RETURN; } @@ -1311,7 +1314,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) { Lex_input_stream *lip= & thd->m_parser_state->m_lip; int token; - + if (lip->lookahead_token >= 0) { /* @@ -1344,6 +1347,8 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return WITH_CUBE_SYM; case ROLLUP_SYM: return WITH_ROLLUP_SYM; + case SYSTEM: + return WITH_SYSTEM_SYM; default: /* Save the token following 'WITH' @@ -1354,6 +1359,45 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return WITH; } break; + case FOR_SYM: + /* + * Additional look-ahead to resolve doubtful cases like: + * SELECT ... FOR UPDATE + * SELECT ... FOR SYSTEM_TIME ... . + */ + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + switch(token) { + case SYSTEM_TIME_SYM: + return FOR_SYSTEM_TIME_SYM; + default: + /* + Save the token following 'FOR_SYM' + */ + lip->lookahead_yylval= lip->yylval; + lip->yylval= NULL; + lip->lookahead_token= token; + return FOR_SYM; + } + break; + case VALUES: + if (thd->lex->current_select->parsing_place == IN_UPDATE_ON_DUP_KEY || + thd->lex->current_select->parsing_place == IN_PART_FUNC) + return VALUE_SYM; + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + switch(token) { + case LESS_SYM: + return VALUES_LESS_SYM; + case IN_SYM: + return VALUES_IN_SYM; + default: + lip->lookahead_yylval= lip->yylval; + lip->yylval= NULL; + lip->lookahead_token= token; + return VALUES; + } + break; default: break; } @@ -2110,7 +2154,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) } -void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_length) +void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length) { /* TODO: @@ -2118,14 +2162,15 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_length) that can be considered white-space. */ - *prefix_length= 0; + size_t plen= 0; while ((str->length > 0) && (my_isspace(cs, str->str[0]))) { - (*prefix_length)++; + plen++; str->length --; str->str ++; } - + if (prefix_length) + *prefix_length= plen; /* FIXME: Also, parsing backward is not safe with multi bytes characters @@ -2141,7 +2186,7 @@ void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_length) st_select_lex structures initialisations */ -void st_select_lex_node::init_query() +void st_select_lex_node::init_query_common() { options= 0; sql_cache= SQL_CACHE_UNSPECIFIED; @@ -2150,13 +2195,9 @@ void st_select_lex_node::init_query() uncacheable= 0; } -void st_select_lex_node::init_select() -{ -} - void st_select_lex_unit::init_query() { - st_select_lex_node::init_query(); + init_query_common(); linkage= GLOBAL_OPTIONS_TYPE; select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; @@ -2182,7 +2223,7 @@ void st_select_lex_unit::init_query() void st_select_lex::init_query() { - st_select_lex_node::init_query(); + init_query_common(); table_list.empty(); top_join_list.empty(); join_list= &top_join_list; @@ -2233,17 +2274,20 @@ void st_select_lex::init_query() m_agg_func_used= false; window_specs.empty(); window_funcs.empty(); + tvc= 0; + in_tvc= false; + versioned_tables= 0; } void st_select_lex::init_select() { - st_select_lex_node::init_select(); sj_nests.empty(); sj_subselects.empty(); group_list.empty(); if (group_list_ptrs) group_list_ptrs->clear(); - type= db= 0; + type= 0; + db= null_clex_str; having= 0; table_join_options= 0; in_sum_expr= with_wild= 0; @@ -2271,6 +2315,11 @@ void st_select_lex::init_select() with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; + tvc= 0; + in_funcs.empty(); + curr_tvc_name= 0; + in_tvc= false; + versioned_tables= 0; } /* @@ -2582,26 +2631,6 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, return FALSE; } -bool st_select_lex_node::inc_in_sum_expr() { return 1; } -uint st_select_lex_node::get_in_sum_expr() { return 0; } -TABLE_LIST* st_select_lex_node::get_table_list() { return 0; } -List<Item>* st_select_lex_node::get_item_list() { return 0; } -TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, - LEX_CSTRING *alias, - ulong table_join_options, - thr_lock_type flags, - enum_mdl_type mdl_type, - List<Index_hint> *hints, - List<String> *partition_names, - LEX_STRING *option) -{ - return 0; -} -ulong st_select_lex_node::get_table_join_options() -{ - return 0; -} - /* prohibit using LIMIT clause */ @@ -3025,10 +3054,9 @@ void Query_tables_list::destroy_query_tables_list() */ LEX::LEX() - : explain(NULL), - result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), + : explain(NULL), result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0), - is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) + default_used(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) { init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer, @@ -3265,8 +3293,7 @@ uint8 LEX::get_effective_with_check(TABLE_LIST *view) case of success */ -bool -LEX::copy_db_to(const char **p_db, size_t *p_db_length) const +bool LEX::copy_db_to(LEX_CSTRING *to) { if (sphead && sphead->m_name.str) { @@ -3275,12 +3302,10 @@ LEX::copy_db_to(const char **p_db, size_t *p_db_length) const It is safe to assign the string by-pointer, both sphead and its statements reside in the same memory root. */ - *p_db= sphead->m_db.str; - if (p_db_length) - *p_db_length= sphead->m_db.length; + *to= sphead->m_db; return FALSE; } - return thd->copy_db_to(p_db, p_db_length); + return thd->copy_db_to(to); } /** @@ -3756,6 +3781,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds, DBUG_ENTER("st_select_lex::fix_prepare_information"); if (!thd->stmt_arena->is_conventional() && first_execution) { + Query_arena_stmt on_stmt_arena(thd); first_execution= 0; if (group_list.first) { @@ -3849,7 +3875,7 @@ void st_select_lex::alloc_index_hints (THD *thd) RETURN VALUE 0 on success, non-zero otherwise */ -bool st_select_lex::add_index_hint (THD *thd, const char *str, uint length) +bool st_select_lex::add_index_hint (THD *thd, const char *str, size_t length) { return index_hints->push_front(new (thd->mem_root) Index_hint(current_index_hint_type, @@ -4212,6 +4238,22 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, if (in_subq->emb_on_expr_nest == NO_JOIN_NEST) in_subq->emb_on_expr_nest= derived; } + + uint cnt= sizeof(expr_cache_may_be_used)/sizeof(bool); + for (uint i= 0; i < cnt; i++) + { + if (subq_select->expr_cache_may_be_used[i]) + expr_cache_may_be_used[i]= true; + } + + List_iterator_fast<Item_func_in> it(subq_select->in_funcs); + Item_func_in *in_func; + while ((in_func= it++)) + { + in_funcs.push_back(in_func, thd->mem_root); + if (in_func->emb_on_expr_nest == NO_JOIN_NEST) + in_func->emb_on_expr_nest= derived; + } } /* Walk through child's tables and adjust table map, tablenr, @@ -5658,7 +5700,7 @@ bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop) Item_splocal(thd, &src->name, src->offset, src->type_handler()); if (args[i] == NULL) return true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS args[i]->m_sp= sphead; #endif } @@ -5792,7 +5834,7 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop) loop.m_index->type_handler()); if (splocal == NULL) return true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS splocal->m_sp= sphead; #endif Item_int *inc= new (thd->mem_root) Item_int(thd, loop.m_direction); @@ -5978,8 +6020,8 @@ bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock, if (sp_block_finalize(thd, spblock, &splabel)) return true; if (end_label->str && - my_strcasecmp(system_charset_info, - end_label->str, splabel->name.str) != 0) + lex_string_cmp(system_charset_info, + end_label, &splabel->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), end_label->str); return true; @@ -5993,7 +6035,7 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name) sp_name *res; LEX_CSTRING db; if (check_routine_name(name) || - copy_db_to(&db.str, &db.length) || + copy_db_to(&db) || (!(res= new (thd->mem_root) sp_name(&db, name, false)))) return NULL; return res; @@ -6386,8 +6428,8 @@ bool LEX::sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name) sp_label *lab= spcont->pop_label(); sphead->backpatch(lab); if (label_name->str && - my_strcasecmp(system_charset_info, label_name->str, - lab->name.str) != 0) + lex_string_cmp(system_charset_info, label_name, + &lab->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), label_name->str); return true; @@ -6583,7 +6625,7 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd, pos.pos(), pos.length()))) return NULL; } -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS item->m_sp= sphead; #endif safe_to_cache_query=0; @@ -6751,7 +6793,7 @@ Item *LEX::create_item_limit(THD *thd, spv->offset, spv->type_handler(), pos.pos(), pos.length()))) return NULL; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS item->m_sp= sphead; #endif safe_to_cache_query= 0; @@ -6872,7 +6914,7 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name, pos.pos(), pos.length()); if (splocal == NULL) return NULL; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS splocal->m_sp= sphead; #endif safe_to_cache_query= 0; @@ -7207,7 +7249,7 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, if (no_top_clones) return cond; cond->clear_extraction_flag(); - return cond->build_clone(thd, thd->mem_root); + return cond->build_clone(thd); } if (cond->type() == Item::COND_ITEM) { @@ -7263,6 +7305,50 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } +int set_statement_var_if_exists(THD *thd, const char *var_name, + size_t var_name_length, ulonglong value) +{ + sys_var *sysvar; + if (thd->lex->sql_command == SQLCOM_CREATE_VIEW) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "[NO]WAIT"); + return 1; + } + if (thd->lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "[NO]WAIT"); + return 1; + } + if ((sysvar= find_sys_var_ex(thd, var_name, var_name_length, true, false))) + { + Item *item= new (thd->mem_root) Item_uint(thd, value); + set_var *var= new (thd->mem_root) set_var(thd, OPT_SESSION, sysvar, + &null_clex_str, item); + + if (!item || !var || thd->lex->stmt_var_list.push_back(var, thd->mem_root)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return 1; + } + } + return 0; +} + + +Query_tables_backup::Query_tables_backup(THD* _thd) : + thd(_thd) +{ + thd->lex->reset_n_backup_query_tables_list(&backup); + thd->lex->sql_command= backup.sql_command; +} + + +Query_tables_backup::~Query_tables_backup() +{ + thd->lex->restore_backup_query_tables_list(&backup); +} + + bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name) { uint offset; @@ -7394,3 +7480,24 @@ Item *LEX::make_item_func_replace(THD *thd, new (thd->mem_root) Item_func_replace_oracle(thd, org, find, replace) : new (thd->mem_root) Item_func_replace(thd, org, find, replace); } + + +bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name) +{ + DBUG_ASSERT(field_name.str); + Item_field *fld= new (thd->mem_root) Item_field(thd, &context, + table->db.str, table->alias.str, &field_name); + if (!fld || item_list.push_back(fld)) + return true; + + if (thd->lex->view_list.elements) + { + LEX_CSTRING *l; + if (!(l= thd->make_clex_string(field_name.str, field_name.length))) + return true; + thd->lex->view_list.push_back(l); + } + + return false; +} + diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a0b109d590d..45fbe38e974 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -23,7 +23,6 @@ #include "violite.h" /* SSL_type */ #include "sql_trigger.h" -#include "item.h" /* From item_subselect.h: subselect_union_engine */ #include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */ #include "mem_root_array.h" #include "sql_cmd.h" @@ -31,7 +30,8 @@ #include "sql_window.h" #include "sql_trigger.h" #include "sp.h" // enum stored_procedure_type - +#include "sql_tvc.h" +#include "item.h" /* YACC and LEX Definitions */ @@ -206,7 +206,7 @@ struct LEX_TYPE #ifdef MYSQL_SERVER extern const LEX_STRING empty_lex_str; -extern const LEX_CSTRING empty_clex_str; +extern MYSQL_PLUGIN_IMPORT const LEX_CSTRING empty_clex_str; extern const LEX_CSTRING star_clex_str; extern const LEX_CSTRING param_clex_str; @@ -226,6 +226,13 @@ enum enum_sp_data_access SP_MODIFIES_SQL_DATA }; +enum enum_sp_aggregate_type +{ + DEFAULT_AGGREGATE= 0, + NOT_AGGREGATE, + GROUP_AGGREGATE +}; + const LEX_STRING sp_data_access_name[]= { { C_STRING_WITH_LEN("") }, @@ -413,7 +420,7 @@ public: LEX_CSTRING key_name; Index_hint (enum index_hint_type type_arg, index_clause_map clause_arg, - const char *str, uint length) : + const char *str, size_t length) : type(type_arg), clause(clause_arg) { key_name.str= str; @@ -551,6 +558,8 @@ protected: st_select_lex_node *next, **prev, /* neighbor list */ *master, *slave, /* vertical links */ *link_next, **link_prev; /* list of whole SELECT_LEX */ + + void init_query_common(); public: ulonglong options; @@ -573,6 +582,10 @@ public: */ uint8 uncacheable; enum sub_select_type linkage; + bool is_linkage_set() const + { + return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || linkage == EXCEPT_TYPE; + } bool no_table_names_allowed; /* used for global order by */ static void *operator new(size_t size, MEM_ROOT *mem_root) throw () @@ -588,11 +601,8 @@ public: linkage(UNSPECIFIED_TYPE) { } - virtual ~st_select_lex_node() {} inline st_select_lex_node* get_master() { return master; } - virtual void init_query(); - virtual void init_select(); void include_down(st_select_lex_node *upper); void add_slave(st_select_lex_node *slave_arg); void include_neighbour(st_select_lex_node *before); @@ -601,23 +611,6 @@ public: void exclude(); void exclude_from_tree(); - virtual st_select_lex* outer_select()= 0; - virtual st_select_lex* return_after_parsing()= 0; - - virtual bool inc_in_sum_expr(); - virtual uint get_in_sum_expr(); - virtual TABLE_LIST* get_table_list(); - virtual List<Item>* get_item_list(); - virtual ulong get_table_join_options(); - virtual TABLE_LIST *add_table_to_list(THD *thd, Table_ident *table, - LEX_CSTRING *alias, - ulong table_options, - thr_lock_type flags= TL_UNLOCK, - enum_mdl_type mdl_type= MDL_SHARED_READ, - List<Index_hint> *hints= 0, - List<String> *partition_names= 0, - LEX_STRING *option= 0); - virtual void set_lock_for_tables(thr_lock_type lock_type) {} void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; } void move_node(st_select_lex_node *where_to_move) { @@ -753,7 +746,7 @@ public: /* thread handler */ THD *thd; /* - SELECT_LEX for hidden SELECT in onion which process global + SELECT_LEX for hidden SELECT in union which process global ORDER BY and LIMIT */ st_select_lex *fake_select_lex; @@ -775,7 +768,7 @@ public: { return reinterpret_cast<st_select_lex*>(slave); } - void set_with_clause(With_clause *with_cl); + inline void set_with_clause(With_clause *with_cl); st_select_lex_unit* next_unit() { return reinterpret_cast<st_select_lex_unit*>(next); @@ -844,7 +837,7 @@ class st_select_lex: public st_select_lex_node { public: Name_resolution_context context; - const char *db; + LEX_CSTRING db; Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ Item *prep_having;/* saved HAVING clause for prepared statement processing */ @@ -906,6 +899,17 @@ public: those converted to jtbm nests. The list is emptied when conversion is done. */ List<Item_in_subselect> sj_subselects; + /* + List of IN-predicates in this st_select_lex that + can be transformed into IN-subselect defined with TVC. + */ + List<Item_func_in> in_funcs; + /* + Number of current derived table made with TVC during the + transformation of IN-predicate into IN-subquery for this + st_select_lex. + */ + uint curr_tvc_name; /* Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column @@ -1042,6 +1046,16 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; + + table_value_constr *tvc; + bool in_tvc; + + /** System Versioning */ +public: + uint versioned_tables; + int vers_setup_conds(THD *thd, TABLE_LIST *tables, COND **where_expr); + /* push new Item_field into item_list */ + bool vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name); void init_query(); void init_select(); @@ -1161,7 +1175,7 @@ public: Add a index hint to the tagged list of hints. The type and clause of the hint will be the current ones (set by set_index_hint()) */ - bool add_index_hint (THD *thd, const char *str, uint length); + bool add_index_hint (THD *thd, const char *str, size_t length); /* make a list to hold index hints */ void alloc_index_hints (THD *thd); @@ -1219,7 +1233,7 @@ public: void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; } void set_agg_func_used(bool val) { m_agg_func_used= val; } - void set_with_clause(With_clause *with_clause); + inline void set_with_clause(With_clause *with_clause); With_clause *get_with_clause() { return master_unit()->with_clause; @@ -1257,7 +1271,7 @@ public: ORDER *find_common_window_func_partition_fields(THD *thd); bool cond_pushdown_is_allowed() const - { return !olap && !explicit_limit; } + { return !olap && !explicit_limit && !tvc; } private: bool m_non_agg_field_used; @@ -1281,7 +1295,12 @@ typedef class st_select_lex SELECT_LEX; inline bool st_select_lex_unit::is_unit_op () { if (!first_select()->next_select()) - return 0; + { + if (first_select()->tvc) + return 1; + else + return 0; + } enum sub_select_type linkage= first_select()->next_select()->linkage; return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || @@ -1295,6 +1314,7 @@ struct st_sp_chistics enum enum_sp_suid_behaviour suid; bool detistic; enum enum_sp_data_access daccess; + enum enum_sp_aggregate_type agg_type; void init() { bzero(this, sizeof(*this)); } void set(const st_sp_chistics &other) { *this= other; } bool read_from_mysql_proc_row(THD *thd, TABLE *table); @@ -1971,6 +1991,18 @@ private: }; +class Query_tables_backup +{ + THD *thd; + Query_tables_list backup; + +public: + Query_tables_backup(THD *_thd); + ~Query_tables_backup(); + const Query_tables_list& get() const { return backup; } +}; + + /* st_parsing_options contains the flags for constructions that are allowed in the current statement. @@ -2043,9 +2075,9 @@ public: @retval FALSE OK @retval TRUE Error */ - bool init(THD *thd, char *buff, unsigned int length); + bool init(THD *thd, char *buff, size_t length); - void reset(char *buff, unsigned int length); + void reset(char *buff, size_t length); /** Set the echo mode. @@ -2318,16 +2350,16 @@ public: } /** Get the utf8-body length. */ - uint get_body_utf8_length() + size_t get_body_utf8_length() { - return (uint) (m_body_utf8_ptr - m_body_utf8); + return (size_t) (m_body_utf8_ptr - m_body_utf8); } /** Get the maximum length of the utf8-body buffer. The utf8 body can grow because of the character set conversion and escaping. */ - uint get_body_utf8_maximum_length(THD *thd); + size_t get_body_utf8_maximum_length(THD *thd); void body_utf8_start(THD *thd, const char *begin_ptr); void body_utf8_append(const char *ptr); @@ -2387,7 +2419,7 @@ private: const char *m_buf; /** Length of the raw buffer. */ - uint m_buf_length; + size_t m_buf_length; /** Echo the parsed stream to the pre-processed buffer. */ bool m_echo; @@ -2594,7 +2626,7 @@ public: Explain_update* save_explain_update_data(MEM_ROOT *mem_root, THD *thd); protected: - void save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze); + bool save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze); public: virtual ~Update_plan() {} @@ -2710,7 +2742,6 @@ struct LEX: public Query_tables_list private: Query_arena_memroot *arena_for_set_stmt; MEM_ROOT *mem_root_for_set_stmt; - void parse_error(); bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock, class sp_label **splabel); bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); @@ -2724,6 +2755,7 @@ private: bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); public: + void parse_error(uint err_number= ER_SYNTAX_ERROR); inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} bool set_arena_for_set_stmt(Query_arena *backup); void reset_arena_for_set_stmt(Query_arena *backup); @@ -2731,7 +2763,7 @@ public: List<Item_func_set_user_var> set_var_list; // in-query assignment list List<Item_param> param_list; - List<LEX_STRING> view_list; // view list (list of field names in view) + List<LEX_CSTRING> view_list; // view list (list of field names in view) List<LEX_CSTRING> with_column_list; // list of column names in with_list_element List<LEX_STRING> *column_list; // list of column names (in ANALYZE) List<LEX_STRING> *index_list; // list of index names (in ANALYZE) @@ -2937,6 +2969,7 @@ public: st_alter_tablespace *alter_tablespace_info; bool escape_used; + bool default_used; /* using default() function */ bool is_lex_started; /* If lex_start() did run. For debugging. */ /* @@ -2981,6 +3014,9 @@ public: Window_frame_bound *frame_bottom_bound; Window_spec *win_spec; + /* System Versioning */ + vers_select_conds_t vers_conditions; + inline void free_set_stmt_mem_root() { DBUG_ASSERT(!is_arena_for_set_stmt()); @@ -3097,7 +3133,7 @@ public: context_stack.pop(); } - bool copy_db_to(const char **p_db, size_t *p_db_length) const; + bool copy_db_to(LEX_CSTRING *to); Name_resolution_context *current_context() { @@ -3618,6 +3654,8 @@ public: alter_info.check_constraint_list.push_back(constr); return false; } + bool add_alter_list(const char *par_name, Virtual_column_info *expr, + bool par_exists); void set_command(enum_sql_command command, DDL_options_st options) { @@ -3685,6 +3723,11 @@ public: bool add_grant_command(THD *thd, enum_sql_command sql_command_arg, stored_procedure_type type_arg); + + Vers_parse_info &vers_get_info() + { + return create_info.vers_info; + } }; @@ -3831,7 +3874,7 @@ public: @retval FALSE OK @retval TRUE Error */ - bool init(THD *thd, char *buff, unsigned int length) + bool init(THD *thd, char *buff, size_t length) { return m_lip.init(thd, buff, length); } @@ -3963,10 +4006,9 @@ int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex); extern int MYSQLlex(union YYSTYPE *yylval, THD *thd); extern int ORAlex(union YYSTYPE *yylval, THD *thd); -extern void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, - uint *prefix_removed); +extern void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, size_t * prefix_length = 0); -extern bool is_lex_native_function(const LEX_CSTRING *name); +extern bool is_lex_native_function(const LEX_CSTRING *name); extern bool is_native_function(THD *thd, const LEX_CSTRING *name); extern bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name); @@ -3976,6 +4018,8 @@ extern bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name); void my_missing_function_error(const LEX_CSTRING &token, const char *name); bool is_keyword(const char *name, uint len); +int set_statement_var_if_exists(THD *thd, const char *var_name, + size_t var_name_length, ulonglong value); Virtual_column_info *add_virtual_expression(THD *thd, Item *expr); Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, diff --git a/sql/sql_list.cc b/sql/sql_list.cc index c63c83f0645..3512c7fc2ef 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -18,6 +18,7 @@ #pragma implementation // gcc: Class implementation #endif +#include "mariadb.h" #include "sql_list.h" list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index f3e6d80dc87..311e601490b 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -19,48 +19,7 @@ #pragma interface /* gcc class implementation */ #endif -#include "my_sys.h" /* alloc_root, TRASH, MY_WME, - MY_FAE, MY_ALLOW_ZERO_PTR */ -#include "m_string.h" /* bfill */ - -THD *thd_get_current_thd(); - -/* mysql standard class memory allocator */ - -class Sql_alloc -{ -public: - static void *operator new(size_t size) throw () - { - DBUG_ASSERT(size < UINT_MAX32); - return thd_alloc(thd_get_current_thd(), (uint) size); - } - static void *operator new[](size_t size) throw () - { - DBUG_ASSERT(size < UINT_MAX32); - return thd_alloc(thd_get_current_thd(), (uint) size); - } - static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () - { return alloc_root(mem_root, size); } - static void *operator new(size_t size, MEM_ROOT *mem_root) throw () - { return alloc_root(mem_root, size); } - static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); } - static void operator delete(void *ptr, MEM_ROOT *mem_root) - { /* never called */ } - static void operator delete[](void *ptr, MEM_ROOT *mem_root) - { /* never called */ } - static void operator delete[](void *ptr, size_t size) { TRASH_FREE(ptr, size); } -#ifdef HAVE_valgrind - bool dummy_for_valgrind; - inline Sql_alloc() :dummy_for_valgrind(0) {} - inline ~Sql_alloc() {} -#else - inline Sql_alloc() {} - inline ~Sql_alloc() {} -#endif - -}; - +#include "sql_alloc.h" /** Simple intrusive linked list. @@ -69,6 +28,7 @@ public: a pointer to the first element in the list and a indirect reference to the last element. */ + template <typename T> class SQL_I_List :public Sql_alloc { @@ -665,6 +625,10 @@ struct ilink { DBUG_ASSERT(prev != 0 && next != 0); } + inline void assert_not_linked() + { + DBUG_ASSERT(prev == 0 && next == 0); + } virtual ~ilink() { unlink(); } /*lint -e1740 */ }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 2f061b8377d..17e2aca79e4 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -19,7 +19,7 @@ /* Copy data from a textfile to table */ /* 2006-12 Erik Wetterberg : LOAD XML added */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_load.h" @@ -322,13 +322,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, killed_state killed_status; bool is_concurrent; #endif - const char *db = table_list->db; // This is never null + const char *db= table_list->db.str; // This is never null /* If path for file is not defined, we will use the current database. If this is not set, we will use the directory where the table to be loaded is located */ - const char *tdb= thd->db ? thd->db : db; // Result is never null + const char *tdb= thd->db.str ? thd->db.str : db; // Result is never null ulong skip_lines= ex->skip_lines; bool transactional_table __attribute__((unused)); DBUG_ENTER("mysql_load"); @@ -379,7 +379,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, !table_list->single_table_updatable() || // and derived tables check_key_in_view(thd, table_list)) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "LOAD"); DBUG_RETURN(TRUE); } if (table_list->prepare_where(thd, 0, TRUE) || @@ -398,7 +398,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, */ if (unique_table(thd, table_list, table_list->next_global, 0)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name, + my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name.str, "LOAD DATA"); DBUG_RETURN(TRUE); } @@ -710,8 +710,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, writing binary log will be ignored */ if (thd->transaction.stmt.modified_non_trans_table) (void) write_execute_load_query_log_event(thd, ex, - table_list->db, - table_list->table_name, + table_list->db.str, + table_list->table_name.str, is_concurrent, handle_duplicates, ignore, transactional_table, @@ -761,7 +761,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { int errcode= query_error_code(thd, killed_status == NOT_KILLED); error= write_execute_load_query_log_event(thd, ex, - table_list->db, table_list->table_name, + table_list->db.str, + table_list->table_name.str, is_concurrent, handle_duplicates, ignore, transactional_table, @@ -810,7 +811,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, List<Item> fv; Item *item, *val; int n; - const char *tdb= (thd->db != NULL ? thd->db : db_arg); + const char *tdb= (thd->db.str != NULL ? thd->db.str : db_arg); const char *qualify_db= NULL; char command_buffer[1024]; String query_str(command_buffer, sizeof(command_buffer), @@ -826,7 +827,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, lle.set_fname_outside_temp_buf(ex->file_name, strlen(ex->file_name)); query_str.length(0); - if (!thd->db || strcmp(db_arg, thd->db)) + if (!thd->db.str || strcmp(db_arg, thd->db.str)) { /* If used database differs from table's database, @@ -853,7 +854,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, if (n++) query_str.append(", "); if (item->real_type() == Item::FIELD_ITEM) - append_identifier(thd, &query_str, item->name.str, item->name.length); + append_identifier(thd, &query_str, &item->name); else { /* Actually Item_user_var_as_out_param despite claiming STRING_ITEM. */ @@ -877,8 +878,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, val= lv++; if (n++) query_str.append(STRING_WITH_LEN(", ")); - append_identifier(thd, &query_str, item->name.str, item->name.length); - query_str.append(val->name.str, val->name.length); + append_identifier(thd, &query_str, &item->name); + query_str.append(&val->name); } } @@ -966,7 +967,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, */ while ((sql_field= (Item_field*) it++)) { - Field *field= sql_field->field; + Field *field= sql_field->field; table->auto_increment_field_not_null= auto_increment_field_not_null; /* No fields specified in fields_vars list can be null in this format. diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index a2efa5e072c..f8b96279378 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -20,12 +20,11 @@ !! This file is built from my_locale.pl !! */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_locale.h" #include "sql_class.h" // THD -#include "my_sys.h" // MY_*, NullS, NULL enum err_msgs_index diff --git a/sql/sql_locale.h b/sql/sql_locale.h index ec2f3d29e15..d1009832e0c 100644 --- a/sql/sql_locale.h +++ b/sql/sql_locale.h @@ -22,7 +22,6 @@ typedef struct my_locale_errmsgs const char ***errmsgs; } MY_LOCALE_ERRMSGS; -#include "my_global.h" /* uint */ typedef struct st_typelib TYPELIB; diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 8c8aee0cb03..f787d39b774 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -21,7 +21,7 @@ * o Berkeley DB: removing unneeded log files. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_manager.h" #include "sql_base.h" // flush_tables diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f8c09d82bf5..67181c6eb5e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -15,7 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" // sql_kill, *_precheck, *_prepare #include "lock.h" // try_transactional_lock, @@ -82,7 +82,6 @@ #include <m_ctype.h> #include <myisam.h> #include <my_dir.h> -#include "rpl_handler.h" #include "rpl_mi.h" #include "sql_digest.h" @@ -99,7 +98,6 @@ #include "debug_sync.h" #include "probes_mysql.h" #include "set_var.h" -#include "log_slow.h" #include "sql_bootstrap.h" #include "sql_sequence.h" @@ -113,6 +111,7 @@ #include "wsrep_mysqld.h" #include "wsrep_thd.h" +#include "vtmd.h" static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state, @@ -133,39 +132,39 @@ static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *); const char *any_db="*any*"; // Special symbol for check_access -const LEX_STRING command_name[257]={ - { C_STRING_WITH_LEN("Sleep") }, //0 - { C_STRING_WITH_LEN("Quit") }, //1 - { C_STRING_WITH_LEN("Init DB") }, //2 - { C_STRING_WITH_LEN("Query") }, //3 - { C_STRING_WITH_LEN("Field List") }, //4 - { C_STRING_WITH_LEN("Create DB") }, //5 - { C_STRING_WITH_LEN("Drop DB") }, //6 - { C_STRING_WITH_LEN("Refresh") }, //7 - { C_STRING_WITH_LEN("Shutdown") }, //8 - { C_STRING_WITH_LEN("Statistics") }, //9 - { C_STRING_WITH_LEN("Processlist") }, //10 - { C_STRING_WITH_LEN("Connect") }, //11 - { C_STRING_WITH_LEN("Kill") }, //12 - { C_STRING_WITH_LEN("Debug") }, //13 - { C_STRING_WITH_LEN("Ping") }, //14 - { C_STRING_WITH_LEN("Time") }, //15 - { C_STRING_WITH_LEN("Delayed insert") }, //16 - { C_STRING_WITH_LEN("Change user") }, //17 - { C_STRING_WITH_LEN("Binlog Dump") }, //18 - { C_STRING_WITH_LEN("Table Dump") }, //19 - { C_STRING_WITH_LEN("Connect Out") }, //20 - { C_STRING_WITH_LEN("Register Slave") }, //21 - { C_STRING_WITH_LEN("Prepare") }, //22 - { C_STRING_WITH_LEN("Execute") }, //23 - { C_STRING_WITH_LEN("Long Data") }, //24 - { C_STRING_WITH_LEN("Close stmt") }, //25 - { C_STRING_WITH_LEN("Reset stmt") }, //26 - { C_STRING_WITH_LEN("Set option") }, //27 - { C_STRING_WITH_LEN("Fetch") }, //28 - { C_STRING_WITH_LEN("Daemon") }, //29 - { C_STRING_WITH_LEN("Unimpl get tid") }, //30 - { C_STRING_WITH_LEN("Reset connection") },//31 +const LEX_CSTRING command_name[257]={ + { STRING_WITH_LEN("Sleep") }, //0 + { STRING_WITH_LEN("Quit") }, //1 + { STRING_WITH_LEN("Init DB") }, //2 + { STRING_WITH_LEN("Query") }, //3 + { STRING_WITH_LEN("Field List") }, //4 + { STRING_WITH_LEN("Create DB") }, //5 + { STRING_WITH_LEN("Drop DB") }, //6 + { STRING_WITH_LEN("Refresh") }, //7 + { STRING_WITH_LEN("Shutdown") }, //8 + { STRING_WITH_LEN("Statistics") }, //9 + { STRING_WITH_LEN("Processlist") }, //10 + { STRING_WITH_LEN("Connect") }, //11 + { STRING_WITH_LEN("Kill") }, //12 + { STRING_WITH_LEN("Debug") }, //13 + { STRING_WITH_LEN("Ping") }, //14 + { STRING_WITH_LEN("Time") }, //15 + { STRING_WITH_LEN("Delayed insert") }, //16 + { STRING_WITH_LEN("Change user") }, //17 + { STRING_WITH_LEN("Binlog Dump") }, //18 + { STRING_WITH_LEN("Table Dump") }, //19 + { STRING_WITH_LEN("Connect Out") }, //20 + { STRING_WITH_LEN("Register Slave") }, //21 + { STRING_WITH_LEN("Prepare") }, //22 + { STRING_WITH_LEN("Execute") }, //23 + { STRING_WITH_LEN("Long Data") }, //24 + { STRING_WITH_LEN("Close stmt") }, //25 + { STRING_WITH_LEN("Reset stmt") }, //26 + { STRING_WITH_LEN("Set option") }, //27 + { STRING_WITH_LEN("Fetch") }, //28 + { STRING_WITH_LEN("Daemon") }, //29 + { STRING_WITH_LEN("Unimpl get tid") }, //30 + { STRING_WITH_LEN("Reset connection") },//31 { 0, 0 }, //32 { 0, 0 }, //33 { 0, 0 }, //34 @@ -384,12 +383,12 @@ const LEX_STRING command_name[257]={ { 0, 0 }, //247 { 0, 0 }, //248 { 0, 0 }, //249 - { C_STRING_WITH_LEN("Bulk_execute") }, //250 - { C_STRING_WITH_LEN("Slave_worker") }, //251 - { C_STRING_WITH_LEN("Slave_IO") }, //252 - { C_STRING_WITH_LEN("Slave_SQL") }, //253 - { C_STRING_WITH_LEN("Com_multi") }, //254 - { C_STRING_WITH_LEN("Error") } // Last command number 255 + { STRING_WITH_LEN("Bulk_execute") }, //250 + { STRING_WITH_LEN("Slave_worker") }, //251 + { STRING_WITH_LEN("Slave_IO") }, //252 + { STRING_WITH_LEN("Slave_SQL") }, //253 + { STRING_WITH_LEN("Com_multi") }, //254 + { STRING_WITH_LEN("Error") } // Last command number 255 }; const char *xa_state_names[]={ @@ -404,7 +403,7 @@ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) { Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; return rpl_filter->is_on() && tables && !thd->spcont && - !rpl_filter->tables_ok(thd->db, tables); + !rpl_filter->tables_ok(thd->db.str, tables); } #endif @@ -413,7 +412,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) { for (TABLE_LIST *table= tables; table; table= table->next_global) { - DBUG_ASSERT(table->db && table->table_name); + DBUG_ASSERT(table->db.str && table->table_name.str); if (table->updating && !thd->find_tmp_table_share(table)) return 1; } @@ -601,7 +600,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED;; + CF_CAN_BE_EXPLAINED; sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -924,7 +923,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, save_vio= thd->net.vio; thd->net.vio= 0; thd->clear_error(1); - dispatch_command(COM_QUERY, thd, buf, len, FALSE, FALSE); + dispatch_command(COM_QUERY, thd, buf, (uint)len, FALSE, FALSE); thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; @@ -937,7 +936,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error) { MYSQL_FILE *in= static_cast<MYSQL_FILE*> (input); - char *line= mysql_file_fgets(buffer, size, in); + char *line= mysql_file_fgets(buffer, (int)size, in); if (error) *error= (line == NULL) ? ferror(in->m_file) : 0; return line; @@ -1016,7 +1015,7 @@ static void handle_bootstrap_impl(THD *thd) } query= (char *) thd->memdup_w_gap(buffer, length + 1, - thd->db_length + 1 + + thd->db.length + 1 + QUERY_CACHE_DB_LENGTH_SIZE + QUERY_CACHE_FLAGS_SIZE); size_t db_len= 0; @@ -1222,8 +1221,8 @@ bool do_command(THD *thd) the client, the connection is closed or "net_wait_timeout" number of seconds has passed. */ - if(!thd->skip_wait_timeout) - my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + if (!thd->skip_wait_timeout) + my_net_set_read_timeout(net, thd->get_net_wait_timeout()); /* Errors and diagnostics are cleared once here before query */ thd->clear_error(1); @@ -1286,7 +1285,8 @@ bool do_command(THD *thd) mysql_mutex_lock(&thd->LOCK_wsrep_thd); if (thd->wsrep_conflict_state == MUST_ABORT) { - DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", thd->real_id)); + DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", + (ulong) thd->real_id)); wsrep_client_rollback(thd); } mysql_mutex_unlock(&thd->LOCK_wsrep_thd); @@ -1483,7 +1483,7 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables) @retval # - Number of commands in the batch */ -uint maria_multi_check(THD *thd, char *packet, uint packet_length) +uint maria_multi_check(THD *thd, char *packet, size_t packet_length) { uint counter= 0; DBUG_ENTER("maria_multi_check"); @@ -1550,9 +1550,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, "<?>"))); bool drop_more_results= 0; - if (!is_com_multi) - inc_thread_running(); - /* keep it withing 1 byte */ compile_time_assert(COM_END == 255); @@ -1600,17 +1597,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_EXECUTE_IF("crash_dispatch_command_before", { DBUG_PRINT("crash_dispatch_command_before", ("now")); - DBUG_ABORT(); }); + DBUG_SUICIDE(); }); /* Performance Schema Interface instrumentation, begin */ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi, com_statement_info[command]. m_key); + /* + We should always call reset_for_next_command() before a query. + mysql_parse() will do this for queries. Ensure it's also done + for other commands. + */ + if (command != COM_QUERY) + thd->reset_for_next_command(); thd->set_command(command); /* - Commands which always take a long time are logged into - the slow log only if opt_log_slow_admin_statements is set. + thd->variables.log_slow_disabled_statements defines which statements + are logged to slow log */ thd->enable_slow_log= thd->variables.sql_log_slow; thd->query_plan_flags= QPLAN_INIT; @@ -1666,7 +1670,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; if (!mysql_change_db(thd, &tmp, FALSE)) { - general_log_write(thd, command, thd->db, thd->db_length); + general_log_write(thd, command, thd->db.str, thd->db.length); my_ok(thd); } break; @@ -1699,8 +1703,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* acl_authenticate() takes the data from net->read_pos */ net->read_pos= (uchar*)packet; - uint save_db_length= thd->db_length; - char *save_db= thd->db; + LEX_CSTRING save_db= thd->db; USER_CONN *save_user_connect= thd->user_connect; Security_context save_security_ctx= *thd->security_ctx; CHARSET_INFO *save_character_set_client= @@ -1736,7 +1739,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->user_connect) decrease_user_connections(thd->user_connect); thd->user_connect= save_user_connect; - thd->reset_db(save_db, save_db_length); + thd->reset_db(&save_db); thd->update_charset(save_character_set_client, save_collation_connection, save_character_set_results); thd->failed_com_change_user++; @@ -1749,7 +1752,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (save_user_connect) decrease_user_connections(save_user_connect); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - my_free(save_db); + my_free((char*) save_db.str); my_free(const_cast<char*>(save_security_ctx.user)); } break; @@ -1798,7 +1801,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (alloc_query(thd, packet, packet_length)) break; // fatal error is set MYSQL_QUERY_START(thd->query(), thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip); char *packet_end= thd->query() + thd->query_length(); @@ -1876,7 +1879,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* DTRACE begin */ MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip); @@ -1885,7 +1888,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, com_statement_info[command].m_key, - thd->db, thd->db_length, + thd->db.str, thd->db.length, thd->charset()); THD_STAGE_INFO(thd, stage_init); MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt, @@ -1893,6 +1896,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_query_and_id(beginning_of_next_stmt, length, thd->charset(), next_query_id()); + /* Count each statement from the client. */ @@ -1902,7 +1906,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); /* Reset the query start time. */ parser_state.reset(beginning_of_next_stmt, length); - /* TODO: set thd->lex->sql_command to SQLCOM_END here */ if (WSREP_ON) wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state, @@ -1935,7 +1938,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]); - if (thd->copy_db_to(&db.str, &db.length)) + if (thd->copy_db_to(&db)) break; /* We have name + wildcard in packet, separated by endzero @@ -1960,7 +1963,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } packet= arg_end + 1; - thd->reset_for_next_command(0); // Don't clear errors // thd->reset_for_next_command reset state => restore it if (is_next_command) { @@ -1976,8 +1978,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, table_name.length= my_casedn_str(files_charset_info, table_name.str); db.length= my_casedn_str(files_charset_info, (char*) db.str); } - table_list.init_one_table(db.str, db.length, table_name.str, - table_name.length, table_name.str, TL_READ); + table_list.init_one_table(&db, (LEX_CSTRING*) &table_name, 0, TL_READ); /* Init TABLE_LIST members necessary when the undelrying table is view. @@ -1988,9 +1989,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, &table_list.next_local); thd->lex->add_to_query_tables(&table_list); - if (is_infoschema_db(table_list.db, table_list.db_length)) + if (is_infoschema_db(&table_list.db)) { - ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias); + ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, &table_list.alias); if (schema_table) table_list.schema_table= schema_table; } @@ -1999,7 +2000,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (!(fields= (char *) thd->memdup(packet, query_length + 1))) break; thd->set_query(fields, query_length); - general_log_print(thd, command, "%s %s", table_list.table_name, fields); + general_log_print(thd, command, "%s %s", table_list.table_name.str, + fields); if (thd->open_temporary_tables(&table_list)) break; @@ -2055,8 +2057,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, status_var_increment(thd->status_var.com_other); - thd->enable_slow_log&= opt_log_slow_admin_statements; - thd->query_plan_flags|= QPLAN_ADMIN; + thd->prepare_logs_for_admin_command(); if (check_global_access(thd, REPL_SLAVE_ACL)) break; @@ -2165,7 +2166,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { STATUS_VAR *current_global_status_var; // Big; Don't allocate on stack ulong uptime; - uint length __attribute__((unused)); ulonglong queries_per_second1000; char buff[250]; uint buff_len= sizeof(buff); @@ -2180,8 +2180,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, queries_per_second1000= 0; else queries_per_second1000= thd->query_id * 1000 / uptime; - - length= my_snprintf(buff, buff_len - 1, +#ifndef EMBEDDED_LIBRARY + size_t length= +#endif + my_snprintf(buff, buff_len - 1, "Uptime: %lu Threads: %d Questions: %lu " "Slow queries: %lu Opens: %lu Flush tables: %lld " "Open tables: %u Queries per second avg: %u.%03u", @@ -2308,7 +2310,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } if (dispatch_command(subcommand, thd, packet + (1 + length_length), - subpacket_length - (1 + length_length), TRUE, + (uint)(subpacket_length - (1 + length_length)), TRUE, (current_com != counter))) { DBUG_ASSERT(thd->is_error()); @@ -2317,7 +2319,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_ASSERT(subpacket_length <= packet_length); packet+= subpacket_length; - packet_length-= subpacket_length; + packet_length-= (uint)subpacket_length; } com_multi_end: @@ -2407,10 +2409,8 @@ com_multi_end: thd->m_digest= NULL; if (!is_com_multi) - { - dec_thread_running(); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory - } + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); @@ -2437,9 +2437,12 @@ com_multi_end: /* + Log query to slow queries, if it passes filtering + @note This function must call delete_explain_query(). */ + void log_slow_statement(THD *thd) { DBUG_ENTER("log_slow_statement"); @@ -2451,19 +2454,26 @@ void log_slow_statement(THD *thd) */ if (unlikely(thd->in_sub_stmt)) goto end; // Don't set time for sub stmt + if (!thd->enable_slow_log || !global_system_variables.sql_log_slow) + goto end; + if ((thd->server_status & + (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && + !(sql_command_flags[thd->last_sql_command] & CF_STATUS_COMMAND) && + (!thd->variables.log_slow_filter || + (thd->variables.log_slow_filter & QPLAN_NOT_USING_INDEX))) + { + thd->query_plan_flags|= QPLAN_NOT_USING_INDEX; + /* We are always logging no index queries if enabled in filter */ + thd->server_status|= SERVER_QUERY_WAS_SLOW; + } /* Follow the slow log filter configuration. */ - if (!thd->enable_slow_log || !global_system_variables.sql_log_slow || - (thd->variables.log_slow_filter - && !(thd->variables.log_slow_filter & thd->query_plan_flags))) + if (thd->variables.log_slow_filter && + !(thd->variables.log_slow_filter & thd->query_plan_flags)) goto end; - - if (((thd->server_status & SERVER_QUERY_WAS_SLOW) || - ((thd->server_status & - (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && - opt_log_queries_not_using_indexes && - !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) && + + if ((thd->server_status & SERVER_QUERY_WAS_SLOW) && thd->get_examined_row_count() >= thd->variables.min_examined_row_limit) { thd->status_var.long_query_count++; @@ -2539,27 +2549,23 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); #else { - LEX_STRING db; - size_t dummy; - if (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, &dummy)) + if (lex->select_lex.db.str == NULL && + lex->copy_db_to(&lex->select_lex.db)) { DBUG_RETURN(1); } schema_select_lex= new (thd->mem_root) SELECT_LEX(); schema_select_lex->table_list.first= NULL; if (lower_case_table_names == 1) - lex->select_lex.db= thd->strdup(lex->select_lex.db); + lex->select_lex.db.str= thd->strdup(lex->select_lex.db.str); schema_select_lex->db= lex->select_lex.db; /* check_db_name() may change db.str if lower_case_table_names == 1, but that's ok as the db is allocted above in this case. */ - db.str= (char*) lex->select_lex.db; - db.length= strlen(db.str); - if (check_db_name(&db)) + if (check_db_name((LEX_STRING*) &lex->select_lex.db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db.str); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->select_lex.db.str); DBUG_RETURN(1); } break; @@ -2622,7 +2628,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, TRUE error; In this case thd->fatal_error is set */ -bool alloc_query(THD *thd, const char *packet, uint packet_length) +bool alloc_query(THD *thd, const char *packet, size_t packet_length) { char *query; /* Remove garbage at start and end of query */ @@ -2650,7 +2656,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) */ if (! (query= (char*) thd->memdup_w_gap(packet, packet_length, - 1 + thd->db_length + + 1 + thd->db.length + QUERY_CACHE_DB_LENGTH_SIZE + QUERY_CACHE_FLAGS_SIZE))) return TRUE; @@ -2660,7 +2666,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) also store this length, in case current database is changed during execution. We might need to reallocate the 'query' buffer */ - int2store(query + packet_length + 1, thd->db_length); + int2store(query + packet_length + 1, thd->db.length); thd->set_query(query, packet_length); @@ -2878,6 +2884,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp) { /* bits that should be cleared in thd->server_status */ uint bits_to_be_cleared= 0; + ulonglong affected_rows; if (sp->m_flags & sp_head::MULTI_RESULTS) { if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS)) @@ -2924,7 +2931,9 @@ static bool do_execute_sp(THD *thd, sp_head *sp) return 1; // Substatement should already have sent error } - my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func()); + affected_rows= thd->affected_rows; // Affected rows for all sub statements + thd->affected_rows= 0; // Reset total, as my_ok() adds to it + my_ok(thd, affected_rows); return 0; } @@ -2950,8 +2959,8 @@ static int mysql_create_routine(THD *thd, LEX *lex) /* Checking the drop permissions if CREATE OR REPLACE is used */ if (lex->create_info.or_replace()) { - if (check_routine_access(thd, ALTER_PROC_ACL, lex->sphead->m_db.str, - lex->sphead->m_name.str, + if (check_routine_access(thd, ALTER_PROC_ACL, &lex->sphead->m_db, + &lex->sphead->m_name, Sp_handler::handler(lex->sql_command), 0)) return true; } @@ -3020,7 +3029,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, - lex->sphead->m_db.str, name->str, + &lex->sphead->m_db, name, Sp_handler::handler(lex->sql_command), 1)) { if (sp_grant_privileges(thd, lex->sphead->m_db.str, name->str, @@ -3121,8 +3130,8 @@ bool Sql_cmd_call::execute(THD *thd) If the routine is not found, let's still check EXECUTE_ACL to decide whether to return "Access denied" or "Routine does not exist". */ - if (check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, - m_name->m_name.str, + if (check_routine_access(thd, EXECUTE_ACL, &m_name->m_db, + &m_name->m_name, &sp_handler_procedure, false)) return true; @@ -3155,6 +3164,18 @@ bool Sql_cmd_call::execute(THD *thd) if (do_execute_sp(thd, sp)) return true; + + if (sp->sp_cache_version() == 0) + { + sp_cache_flush(thd->sp_proc_cache, sp); + } + + /* + Disable slow log for the above call(), if calls are disabled. + Instead we will log the executed statements to the slow log. + */ + if (thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_CALL) + thd->enable_slow_log= 0; } return false; } @@ -3236,6 +3257,12 @@ mysql_execute_command(THD *thd) table_list.first); /* + Remember last commmand executed, so that we can use it in functions called by + dispatch_command() + */ + thd->last_sql_command= lex->sql_command; + + /* Reset warning count for each query that uses tables A better approach would be to reset this for any commands that is not a SHOW command or a select that only access local @@ -3838,7 +3865,7 @@ mysql_execute_command(THD *thd) case SQLCOM_ASSIGN_TO_KEYCACHE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_access(thd, INDEX_ACL, first_table->db, + if (check_access(thd, INDEX_ACL, first_table->db.str, &first_table->grant.privilege, &first_table->grant.m_internal, 0, 0)) @@ -3849,7 +3876,7 @@ mysql_execute_command(THD *thd) case SQLCOM_PRELOAD_KEYS: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_access(thd, INDEX_ACL, first_table->db, + if (check_access(thd, INDEX_ACL, first_table->db.str, &first_table->grant.privilege, &first_table->grant.m_internal, 0, 0)) @@ -3996,7 +4023,6 @@ mysql_execute_command(THD *thd) copy. */ Alter_info alter_info(lex->alter_info, thd->mem_root); - if (thd->is_fatal_error) { /* If out of memory when creating a copy of alter_info. */ @@ -4013,9 +4039,9 @@ mysql_execute_command(THD *thd) /* Fix names if symlinked or relocated tables */ if (append_file_to_dir(thd, &create_info.data_file_name, - create_table->table_name) || + &create_table->table_name) || append_file_to_dir(thd, &create_info.index_file_name, - create_table->table_name)) + &create_table->table_name)) goto end_with_restore_list; /* @@ -4024,6 +4050,7 @@ mysql_execute_command(THD *thd) */ if (!(create_info.used_fields & HA_CREATE_USED_ENGINE)) create_info.use_default_db_type(thd); + /* If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). @@ -4127,8 +4154,8 @@ mysql_execute_command(THD *thd) */ if (create_info.used_fields & HA_CREATE_USED_UNION) { - my_error(ER_WRONG_OBJECT, MYF(0), create_table->db, - create_table->table_name, "BASE TABLE"); + my_error(ER_WRONG_OBJECT, MYF(0), create_table->db.str, + create_table->table_name.str, "BASE TABLE"); res= 1; goto end_with_restore_list; } @@ -4209,18 +4236,22 @@ mysql_execute_command(THD *thd) } else { + if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table)) + { + goto end_with_restore_list; + } /* In STATEMENT format, we probably have to replicate also temporary tables, like mysql replication does. Also check if the requested engine is allowed/supported. */ if (WSREP(thd) && - !check_engine(thd, create_table->db, create_table->table_name, + !check_engine(thd, create_table->db.str, create_table->table_name.str, &create_info) && (!thd->is_current_stmt_binlog_format_row() || !create_info.tmp_table())) { - WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name, NULL) + WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL); } /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); @@ -4265,21 +4296,20 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL) + WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL); /* Currently CREATE INDEX or DROP INDEX cause a full table rebuild and thus classify as slow administrative statements just like ALTER TABLE. */ - thd->enable_slow_log&= opt_log_slow_admin_statements; - thd->query_plan_flags|= QPLAN_ADMIN; + thd->prepare_logs_for_admin_command(); bzero((char*) &create_info, sizeof(create_info)); create_info.db_type= 0; create_info.row_type= ROW_TYPE_NOT_USED; create_info.default_table_charset= thd->variables.collation_database; - res= mysql_alter_table(thd, first_table->db, first_table->table_name, + res= mysql_alter_table(thd, &first_table->db, &first_table->table_name, &create_info, first_table, &alter_info, 0, (ORDER*) 0, 0); break; @@ -4391,7 +4421,7 @@ end_with_restore_list: if (check_rename_table(thd, first_table, all_tables)) goto error; - WSREP_TO_ISOLATION_BEGIN(0, 0, first_table) + WSREP_TO_ISOLATION_BEGIN(0, 0, first_table); if (mysql_rename_tables(thd, first_table, 0)) goto error; @@ -4434,7 +4464,7 @@ end_with_restore_list: DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s", lex->table_type == TABLE_TYPE_VIEW, - first_table->db, first_table->table_name)); + first_table->db.str, first_table->table_name.str)); if (lex->table_type == TABLE_TYPE_VIEW) { if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) @@ -4442,7 +4472,7 @@ end_with_restore_list: DBUG_PRINT("debug", ("check_table_access failed")); my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "SHOW", thd->security_ctx->priv_user, - thd->security_ctx->host_or_ip, first_table->alias); + thd->security_ctx->host_or_ip, first_table->alias.str); goto error; } DBUG_PRINT("debug", ("check_table_access succeeded")); @@ -4471,7 +4501,7 @@ end_with_restore_list: { my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "SHOW", thd->security_ctx->priv_user, - thd->security_ctx->host_or_ip, first_table->alias); + thd->security_ctx->host_or_ip, first_table->alias.str); goto error; } } @@ -4719,7 +4749,7 @@ end_with_restore_list: if (WSREP(thd) && thd->wsrep_consistency_check == CONSISTENCY_CHECK_DECLARED) { thd->wsrep_consistency_check = CONSISTENCY_CHECK_RUNNING; - WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL); + WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL); } #endif /* WITH_WSREP */ @@ -4934,7 +4964,7 @@ end_with_restore_list: SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT, result, unit, select_lex); - res|= thd->is_error(); + res|= (int)(thd->is_error()); MYSQL_MULTI_DELETE_DONE(res, result->num_deleted()); if (res) @@ -5043,9 +5073,7 @@ end_with_restore_list: #endif case SQLCOM_CHANGE_DB: { - LEX_CSTRING db_str= { (char *) select_lex->db, strlen(select_lex->db) }; - - if (!mysql_change_db(thd, &db_str, FALSE)) + if (!mysql_change_db(thd, &select_lex->db, FALSE)) my_ok(thd); break; @@ -5171,8 +5199,8 @@ end_with_restore_list: (CREATE_ACL | DROP_ACL) : CREATE_ACL, &lex->name)) break; - WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) - res= mysql_create_db(thd, lex->name.str, + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); + res= mysql_create_db(thd, &lex->name, lex->create_info, &lex->create_info); break; } @@ -5180,8 +5208,8 @@ end_with_restore_list: { if (prepare_db_action(thd, DROP_ACL, &lex->name)) break; - WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) - res= mysql_rm_db(thd, lex->name.str, lex->if_exists()); + WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL); + res= mysql_rm_db(thd, &lex->name, lex->if_exists()); break; } case SQLCOM_ALTER_DB_UPGRADE: @@ -5212,7 +5240,7 @@ end_with_restore_list: res= 1; break; } - WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); res= mysql_upgrade_db(thd, db); if (!res) my_ok(thd); @@ -5223,8 +5251,8 @@ end_with_restore_list: LEX_CSTRING *db= &lex->name; if (prepare_db_action(thd, ALTER_ACL, db)) break; - WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL) - res= mysql_alter_db(thd, db->str, &lex->create_info); + WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL); + res= mysql_alter_db(thd, db, &lex->create_info); break; } case SQLCOM_SHOW_CREATE_DB: @@ -5313,7 +5341,7 @@ end_with_restore_list: "mysql", NULL, NULL, 1, 0)) break; #ifdef HAVE_DLOPEN - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res = mysql_create_function(thd, &lex->udf))) my_ok(thd); #else @@ -5331,7 +5359,7 @@ end_with_restore_list: "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list, lex->sql_command == SQLCOM_CREATE_ROLE))) @@ -5345,7 +5373,7 @@ end_with_restore_list: check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res= mysql_drop_user(thd, lex->users_list, lex->sql_command == SQLCOM_DROP_ROLE))) my_ok(thd); @@ -5358,7 +5386,7 @@ end_with_restore_list: check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (lex->sql_command == SQLCOM_ALTER_USER) res= mysql_alter_user(thd, lex->users_list); else @@ -5374,7 +5402,7 @@ end_with_restore_list: break; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res = mysql_revoke_all(thd, lex->users_list))) my_ok(thd); break; @@ -5384,7 +5412,7 @@ end_with_restore_list: { if (lex->type != TYPE_ENUM_PROXY && check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, - first_table ? first_table->db : select_lex->db, + first_table ? first_table->db.str : select_lex->db.str, first_table ? &first_table->grant.privilege : NULL, first_table ? &first_table->grant.m_internal : NULL, first_table ? 0 : 1, 0)) @@ -5434,7 +5462,7 @@ end_with_restore_list: if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0)) goto error; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_routine_grant(thd, all_tables, sph, lex->users_list, grants, lex->sql_command == SQLCOM_REVOKE, TRUE); @@ -5447,7 +5475,7 @@ end_with_restore_list: all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_table_grant(thd, all_tables, lex->users_list, lex->columns, lex->grant, lex->sql_command == SQLCOM_REVOKE); @@ -5463,9 +5491,9 @@ end_with_restore_list: } else { - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ - res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + res= mysql_grant(thd, select_lex->db.str, lex->users_list, lex->grant, lex->sql_command == SQLCOM_REVOKE, lex->type == TYPE_ENUM_PROXY); } @@ -5489,7 +5517,7 @@ end_with_restore_list: case SQLCOM_REVOKE_ROLE: case SQLCOM_GRANT_ROLE: { - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if (!(res= mysql_grant_role(thd, lex->users_list, lex->sql_command != SQLCOM_GRANT_ROLE))) my_ok(thd); @@ -5547,7 +5575,7 @@ end_with_restore_list: REFRESH_STATUS | REFRESH_USER_RESOURCES)) { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); } #endif /* WITH_WSREP*/ @@ -5581,11 +5609,11 @@ end_with_restore_list: */ if (first_table) { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); + WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); } else { - WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); + WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL); } } #endif /* WITH_WSREP */ @@ -5850,8 +5878,8 @@ end_with_restore_list: { int sp_result; const Sp_handler *sph= Sp_handler::handler(lex->sql_command); - if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, sph, 0)) + if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db, + &lex->spname->m_name, sph, 0)) goto error; /* @@ -5924,13 +5952,11 @@ end_with_restore_list: int sp_result; const Sp_handler *sph= Sp_handler::handler(lex->sql_command); - const char *db= lex->spname->m_db.str; - const char *name= lex->spname->m_name.str; - if (check_routine_access(thd, ALTER_PROC_ACL, db, name, + if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db, &lex->spname->m_name, Sp_handler::handler(lex->sql_command), 0)) goto error; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); /* Conditionally writes to binlog */ sp_result= sph->sp_drop_routine(thd, lex->spname); @@ -5956,7 +5982,7 @@ end_with_restore_list: if (sp_result != SP_KEY_NOT_FOUND && sp_automatic_privileges && !opt_noacl && - sp_revoke_privileges(thd, db, name, + sp_revoke_privileges(thd, lex->spname->m_db.str, lex->spname->m_name.str, Sp_handler::handler(lex->sql_command))) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, @@ -6051,7 +6077,7 @@ end_with_restore_list: if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog. */ - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); break; } @@ -6154,7 +6180,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); res= create_server(thd, &lex->server_options); break; @@ -6167,7 +6193,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if ((error= alter_server(thd, &lex->server_options))) { @@ -6187,7 +6213,7 @@ end_with_restore_list: if (check_global_access(thd, SUPER_ACL)) break; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); if ((err_code= drop_server(thd, &lex->server_options))) { @@ -6400,6 +6426,21 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) (ulonglong) thd->variables.select_limit); } + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + for (TABLE_LIST *table= all_tables; table; table= table->next_local) + { + if (table->vers_conditions) + { + VTMD_exists vtmd(*table); + if (vtmd.check_exists(thd)) + return 1; + if (vtmd.exists && vtmd.setup_select(thd)) + return 1; + } + } + } + if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) { if (lex->describe) @@ -6538,11 +6579,11 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table, TABLE_LIST *table; for (table= first_table; table; table= table->next_local->next_local) { - if (check_access(thd, ALTER_ACL | DROP_ACL, table->db, + if (check_access(thd, ALTER_ACL | DROP_ACL, table->db.str, &table->grant.privilege, &table->grant.m_internal, 0, 0) || - check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db, + check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db.str, &table->next_local->grant.privilege, &table->next_local->grant.m_internal, 0, 0)) @@ -6632,7 +6673,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, /* check access may be called twice in a row. Don't change to same stage */ if (thd->proc_info != stage_checking_permissions.m_name) THD_STAGE_INFO(thd, stage_checking_permissions); - if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) + if ((!db || !db[0]) && !thd->db.str && !dont_check_global_grants) { DBUG_PRINT("error",("No database")); if (!no_errors) @@ -6688,7 +6729,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, */ if (!(sctx->master_access & SELECT_ACL)) { - if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db))) + if (db && (!thd->db.str || db_is_pattern || strcmp(db, thd->db.str))) { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); @@ -6737,7 +6778,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_RETURN(FALSE); } - if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) + if (db && (!thd->db.str || db_is_pattern || strcmp(db, thd->db.str))) { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); @@ -6792,8 +6833,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, - (db ? db : (thd->db ? - thd->db : + (db ? db : (thd->db.str ? + thd->db.str : "unknown"))); } DBUG_RETURN(TRUE); @@ -6831,7 +6872,7 @@ bool check_single_table_access(THD *thd, ulong privilege, !all_tables->schema_table) db_name= all_tables->view_db.str; else - db_name= all_tables->db; + db_name= all_tables->db.str; if (check_access(thd, privilege, db_name, &all_tables->grant.privilege, @@ -6920,7 +6961,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) case SCH_TRIGGERS: case SCH_EVENTS: { - const char *dst_db_name= table->schema_select_lex->db; + const char *dst_db_name= table->schema_select_lex->db.str; DBUG_ASSERT(dst_db_name); @@ -6955,7 +6996,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) if (thd->open_temporary_tables(dst_table)) return TRUE; - if (check_access(thd, SELECT_ACL, dst_table->db, + if (check_access(thd, SELECT_ACL, dst_table->db.str, &dst_table->grant.privilege, &dst_table->grant.m_internal, FALSE, FALSE)) @@ -7091,15 +7132,15 @@ deny: bool -check_routine_access(THD *thd, ulong want_access, const char *db, - const char *name, +check_routine_access(THD *thd, ulong want_access, const LEX_CSTRING *db, + const LEX_CSTRING *name, const Sp_handler *sph, bool no_errors) { TABLE_LIST tables[1]; bzero((char *)tables, sizeof(TABLE_LIST)); - tables->db= db; - tables->table_name= tables->alias= name; + tables->db= *db; + tables->table_name= tables->alias= *name; /* The following test is just a shortcut for check_access() (to avoid @@ -7116,7 +7157,7 @@ check_routine_access(THD *thd, ulong want_access, const char *db, DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0); if ((thd->security_ctx->master_access & want_access) == want_access) tables->grant.privilege= want_access; - else if (check_access(thd, want_access, db, + else if (check_access(thd, want_access, db->str, &tables->grant.privilege, &tables->grant.m_internal, 0, no_errors)) @@ -7183,7 +7224,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { if (access & want_access) { - if (!check_access(thd, access, table->db, + if (!check_access(thd, access, table->db.str, &table->grant.privilege, &table->grant.m_internal, 0, 1) && @@ -7296,7 +7337,7 @@ bool check_fk_parent_table_access(THD *thd, } else { - if (!thd->db) + if (!thd->db.str) { DBUG_ASSERT(create_db); db_name.length= strlen(create_db); @@ -7313,7 +7354,7 @@ bool check_fk_parent_table_access(THD *thd, } else { - if (thd->lex->copy_db_to(&db_name.str, &db_name.length)) + if (thd->lex->copy_db_to(&db_name)) return true; else is_qualified_table_name= false; @@ -7330,9 +7371,7 @@ bool check_fk_parent_table_access(THD *thd, db_name.length= my_casedn_str(files_charset_info, (char*) db_name.str); } - parent_table.init_one_table(db_name.str, db_name.length, - table_name.str, table_name.length, - table_name.str, TL_IGNORE); + parent_table.init_one_table(&db_name, &table_name, 0, TL_IGNORE); /* Check if user has any of the "privileges" at table level on @@ -7417,16 +7456,16 @@ bool check_stack_overrun(THD *thd, long margin, #define MY_YACC_INIT 1000 // Start with big alloc #define MY_YACC_MAX 32000 // Because of 'short' -bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) +bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, size_t *yystacksize) { Yacc_state *state= & current_thd->m_parser_state->m_yacc; - ulong old_info=0; + size_t old_info=0; DBUG_ASSERT(state); - if ((uint) *yystacksize >= MY_YACC_MAX) + if ( *yystacksize >= MY_YACC_MAX) return 1; if (!state->yacc_yyvs) old_info= *yystacksize; - *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX); + *yystacksize= set_zone((int)(*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX); if (!(state->yacc_yyvs= (uchar*) my_realloc(state->yacc_yyvs, *yystacksize*sizeof(**yyvs), @@ -7467,27 +7506,26 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) void THD::reset_for_next_command(bool do_clear_error) { - THD *thd= this; DBUG_ENTER("THD::reset_for_next_command"); - DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ - DBUG_ASSERT(! thd->in_sub_stmt); + DBUG_ASSERT(!spcont); /* not for substatements of routines */ + DBUG_ASSERT(!in_sub_stmt); if (do_clear_error) clear_error(1); - thd->free_list= 0; + free_list= 0; /* - We also assign thd->stmt_lex in lex_start(), but during bootstrap this + We also assign stmt_lex in lex_start(), but during bootstrap this code is executed first. */ - thd->stmt_lex= &main_lex; thd->stmt_lex->current_select_number= 1; - DBUG_PRINT("info", ("Lex %p stmt_lex: %p", thd->lex, thd->stmt_lex)); + stmt_lex= &main_lex; stmt_lex->current_select_number= 1; + DBUG_PRINT("info", ("Lex %p stmt_lex: %p", lex, stmt_lex)); /* Those two lines below are theoretically unneeded as THD::cleanup_after_query() should take care of this already. */ - thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty(); - thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + auto_inc_intervals_in_cur_stmt_for_binlog.empty(); + stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; #ifdef WITH_WSREP /* @@ -7498,58 +7536,57 @@ void THD::reset_for_next_command(bool do_clear_error) use autoinc values passed in binlog events, not the values forced by the cluster. */ - if (WSREP(thd) && thd->wsrep_exec_mode == LOCAL_STATE && - !thd->slave_thread && wsrep_auto_increment_control) + if (WSREP(this) && wsrep_exec_mode == LOCAL_STATE && + !slave_thread && wsrep_auto_increment_control) { - thd->variables.auto_increment_offset= + variables.auto_increment_offset= global_system_variables.auto_increment_offset; - thd->variables.auto_increment_increment= + variables.auto_increment_increment= global_system_variables.auto_increment_increment; } #endif /* WITH_WSREP */ - thd->query_start_used= 0; - thd->query_start_sec_part_used= 0; - thd->is_fatal_error= thd->time_zone_used= 0; - thd->log_current_statement= 0; + query_start_used= 0; + query_start_sec_part_used= 0; + is_fatal_error= time_zone_used= 0; + log_current_statement= 0; /* Clear the status flag that are expected to be cleared at the beginning of each SQL statement. */ - thd->server_status&= ~SERVER_STATUS_CLEAR_SET; + server_status&= ~SERVER_STATUS_CLEAR_SET; /* If in autocommit mode and not in a transaction, reset OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings in ha_rollback_trans() about some tables couldn't be rolled back. */ - if (!thd->in_multi_stmt_transaction_mode()) + if (!in_multi_stmt_transaction_mode()) { - thd->variables.option_bits&= ~OPTION_KEEP_LOG; - thd->transaction.all.reset(); + variables.option_bits&= ~OPTION_KEEP_LOG; + transaction.all.reset(); } - DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); - thd->thread_specific_used= FALSE; + DBUG_ASSERT(security_ctx== &main_security_ctx); + thread_specific_used= FALSE; if (opt_bin_log) - reset_dynamic(&thd->user_var_events); - DBUG_ASSERT(thd->user_var_events_alloc == &thd->main_mem_root); - - thd->get_stmt_da()->reset_for_next_command(); - thd->rand_used= 0; - thd->m_sent_row_count= thd->m_examined_row_count= 0; - thd->accessed_rows_and_keys= 0; + reset_dynamic(&user_var_events); + DBUG_ASSERT(user_var_events_alloc == &main_mem_root); + enable_slow_log= variables.sql_log_slow; + get_stmt_da()->reset_for_next_command(); + rand_used= 0; + m_sent_row_count= m_examined_row_count= 0; + accessed_rows_and_keys= 0; - thd->query_plan_flags= QPLAN_INIT; - thd->query_plan_fsort_passes= 0; + reset_slow_query_state(); - thd->reset_current_stmt_binlog_format_row(); - thd->binlog_unsafe_warning_flags= 0; + reset_current_stmt_binlog_format_row(); + binlog_unsafe_warning_flags= 0; - thd->save_prep_leaf_list= false; + save_prep_leaf_list= false; DBUG_PRINT("debug", ("is_current_stmt_binlog_format_row(): %d", - thd->is_current_stmt_binlog_format_row())); + is_current_stmt_binlog_format_row())); DBUG_VOID_RETURN; } @@ -7622,7 +7659,6 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) DBUG_RETURN(1); unit->init_query(); - unit->init_select(); unit->thd= thd; unit->include_down(lex->current_select); unit->link_next= 0; @@ -7696,28 +7732,23 @@ mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) @param var_name Variable name */ -void create_select_for_variable(const char *var_name) +void create_select_for_variable(THD *thd, LEX_CSTRING *var_name) { - THD *thd; LEX *lex; - LEX_CSTRING tmp; Item *var; char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end; DBUG_ENTER("create_select_for_variable"); - thd= current_thd; lex= thd->lex; mysql_init_select(lex); lex->sql_command= SQLCOM_SELECT; - tmp.str= var_name; - tmp.length=strlen(var_name); /* We set the name of Item to @@session.var_name because that then is used as the column name in the output. */ - if ((var= get_system_var(thd, OPT_SESSION, tmp, null_clex_str))) + if ((var= get_system_var(thd, OPT_SESSION, var_name, &null_clex_str))) { - end= strxmov(buff, "@@session.", var_name, NullS); + end= strxmov(buff, "@@session.", var_name->str, NullS); var->set_name(thd, buff, (uint)(end-buff), system_charset_info); add_item_to_list(thd, var); } @@ -7941,7 +7972,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, lex->set_trg_event_type_for_tables(); MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip, 0); @@ -8093,13 +8124,14 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { register TABLE_LIST *ptr; TABLE_LIST *UNINIT_VAR(previous_table_ref); /* The table preceding the current one. */ - const char *alias_str; + LEX_CSTRING alias_str; LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); if (!table) DBUG_RETURN(0); // End of memory - alias_str= alias ? alias->str : table->table.str; + alias_str= alias ? *alias : table->table; + DBUG_ASSERT(alias_str.str); if (!MY_TEST(table_options & TL_OPTION_ALIAS) && check_table_name(table->table.str, table->table.length, FALSE)) { @@ -8114,7 +8146,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, DBUG_RETURN(0); } - if (!alias) /* Alias is case sensitive */ + if (!alias) /* Alias is case sensitive */ { if (table->sel) { @@ -8122,7 +8154,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ER_THD(thd, ER_DERIVED_MUST_HAVE_ALIAS), MYF(0)); DBUG_RETURN(0); } - if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1))) + /* alias_str points to table->table; Let's make a copy */ + if (!(alias_str.str= (char*) thd->memdup(alias_str.str, alias_str.length+1))) DBUG_RETURN(0); } if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) @@ -8130,10 +8163,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (table->db.str) { ptr->is_fqtn= TRUE; - ptr->db= table->db.str; - ptr->db_length= table->db.length; + ptr->db= table->db; } - else if (lex->copy_db_to(&ptr->db, &ptr->db_length)) + else if (lex->copy_db_to(&ptr->db)) DBUG_RETURN(0); else ptr->is_fqtn= FALSE; @@ -8145,12 +8177,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (table->table.length) table->table.length= my_casedn_str(files_charset_info, (char*) table->table.str); - if (ptr->db_length && ptr->db != any_db) - ptr->db_length= my_casedn_str(files_charset_info, (char*) ptr->db); + if (ptr->db.length && ptr->db.str != any_db) + ptr->db.length= my_casedn_str(files_charset_info, (char*) ptr->db.str); } - ptr->table_name=table->table.str; - ptr->table_name_length=table->table.length; + ptr->table_name= table->table; ptr->lock_type= lock_type; ptr->updating= MY_TEST(table_options & TL_OPTION_UPDATING); /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ @@ -8158,7 +8189,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES); ptr->sequence= MY_TEST(table_options & TL_OPTION_SEQUENCE); ptr->derived= table->sel; - if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) + if (!ptr->derived && is_infoschema_db(&ptr->db)) { ST_SCHEMA_TABLE *schema_table; if (ptr->updating && @@ -8172,7 +8203,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, INFORMATION_SCHEMA_NAME.str); DBUG_RETURN(0); } - schema_table= find_schema_table(thd, ptr->table_name); + schema_table= find_schema_table(thd, &ptr->table_name); if (!schema_table || (schema_table->hidden && ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || @@ -8183,7 +8214,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, lex->sql_command == SQLCOM_SHOW_KEYS))) { my_error(ER_UNKNOWN_TABLE, MYF(0), - ptr->table_name, INFORMATION_SCHEMA_NAME.str); + ptr->table_name.str, INFORMATION_SCHEMA_NAME.str); DBUG_RETURN(0); } ptr->schema_table_name= ptr->table_name; @@ -8207,10 +8238,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, tables ; tables=tables->next_local) { - if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && - !strcmp(ptr->db, tables->db) && ! tables->sequence) + if (!my_strcasecmp(table_alias_charset, alias_str.str, tables->alias.str) && + !cmp(&ptr->db, &tables->db) && ! tables->sequence) { - my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */ + my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str.str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } @@ -8256,7 +8287,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, // Pure table aliases do not need to be locked: if (!MY_TEST(table_options & TL_OPTION_ALIAS)) { - ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type, + ptr->mdl_request.init(MDL_key::TABLE, ptr->db.str, ptr->table_name.str, + mdl_type, MDL_TRANSACTION); } DBUG_RETURN(ptr); @@ -8297,7 +8329,8 @@ bool st_select_lex::init_nested_join(THD *thd) join_list->push_front(ptr, thd->mem_root); ptr->embedding= embedding; ptr->join_list= join_list; - ptr->alias= (char*) "(nested_join)"; + ptr->alias.str="(nested_join)"; + ptr->alias.length= sizeof("(nested_join)")-1; embedding= ptr; join_list= &nested_join->join_list; join_list->empty(); @@ -8377,7 +8410,8 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) ptr->embedding= embedding; ptr->join_list= join_list; - ptr->alias= (char*) "(nest_last_join)"; + ptr->alias.str= "(nest_last_join)"; + ptr->alias.length= sizeof("(nest_last_join)")-1; embedded_list= &nested_join->join_list; embedded_list->empty(); @@ -8978,14 +9012,14 @@ void sql_kill_user(THD *thd, LEX_USER *user, killed_state state) /** If pointer is not a null pointer, append filename to it. */ bool append_file_to_dir(THD *thd, const char **filename_ptr, - const char *table_name) + const LEX_CSTRING *table_name) { char buff[FN_REFLEN],*ptr, *end; if (!*filename_ptr) return 0; // nothing to do /* Check that the filename is not too long and it's a hard path */ - if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 || + if (strlen(*filename_ptr)+table_name->length >= FN_REFLEN-1 || !test_if_hard_path(*filename_ptr)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr); @@ -8994,10 +9028,10 @@ bool append_file_to_dir(THD *thd, const char **filename_ptr, /* Fix is using unix filename format on dos */ strmov(buff,*filename_ptr); end=convert_dirname(buff, *filename_ptr, NullS); - if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1))) + if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + table_name->length + 1))) return 1; // End of memory *filename_ptr=ptr; - strxmov(ptr,buff,table_name,NullS); + strxmov(ptr,buff,table_name->str,NullS); return 0; } @@ -9132,12 +9166,12 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) continue; if (table->derived) table->grant.privilege= SELECT_ACL; - else if ((check_access(thd, UPDATE_ACL, table->db, + else if ((check_access(thd, UPDATE_ACL, table->db.str, &table->grant.privilege, &table->grant.m_internal, 0, 1) || check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) && - (check_access(thd, SELECT_ACL, table->db, + (check_access(thd, SELECT_ACL, table->db.str, &table->grant.privilege, &table->grant.m_internal, 0, 0) || @@ -9157,7 +9191,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) { if (!table->table_in_first_from_clause) { - if (check_access(thd, SELECT_ACL, table->db, + if (check_access(thd, SELECT_ACL, table->db.str, &table->grant.privilege, &table->grant.m_internal, 0, 0) || @@ -9255,25 +9289,25 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl, for (TABLE_LIST *elem= tables; elem; elem= elem->next_local) { - int cmp; + int res; if (tbl->is_fqtn && elem->is_alias) continue; /* no match */ if (tbl->is_fqtn && elem->is_fqtn) - cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) || - strcmp(tbl->db, elem->db); + res= (my_strcasecmp(table_alias_charset, tbl->table_name.str, elem->table_name.str) || + cmp(&tbl->db, &elem->db)); else if (elem->is_alias) - cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias); + res= my_strcasecmp(table_alias_charset, tbl->alias.str, elem->alias.str); else - cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) || - strcmp(tbl->db, elem->db); + res= (my_strcasecmp(table_alias_charset, tbl->table_name.str, elem->table_name.str) || + cmp(&tbl->db, &elem->db)); - if (cmp) + if (res) continue; if (match) { - my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias); + my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias.str); DBUG_RETURN(NULL); } @@ -9281,7 +9315,7 @@ static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl, } if (!match) - my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE"); + my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name.str, "MULTI DELETE"); DBUG_RETURN(match); } @@ -9316,10 +9350,7 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) if (!walk) DBUG_RETURN(TRUE); if (!walk->derived) - { target_tbl->table_name= walk->table_name; - target_tbl->table_name_length= walk->table_name_length; - } walk->updating= target_tbl->updating; walk->lock_type= target_tbl->lock_type; /* We can assume that tables to be deleted from are locked for write. */ @@ -9370,10 +9401,18 @@ bool update_precheck(THD *thd, TABLE_LIST *tables) bool delete_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("delete_precheck"); - if (check_one_table_access(thd, DELETE_ACL, tables)) - DBUG_RETURN(TRUE); - /* Set privilege for the WHERE clause */ - tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + if (tables->vers_conditions) + { + if (check_one_table_access(thd, DELETE_HISTORY_ACL, tables)) + DBUG_RETURN(TRUE); + } + else + { + if (check_one_table_access(thd, DELETE_ACL, tables)) + DBUG_RETURN(TRUE); + /* Set privilege for the WHERE clause */ + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + } DBUG_RETURN(FALSE); } @@ -9477,7 +9516,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, if (lex->create_info.or_replace() && !lex->tmp_table()) want_priv|= DROP_ACL; - if (check_access(thd, want_priv, create_table->db, + if (check_access(thd, want_priv, create_table->db.str, &create_table->grant.privilege, &create_table->grant.m_internal, 0, 0)) @@ -9544,7 +9583,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, goto err; } - if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info, create_table->db)) + if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info, + create_table->db.str)) goto err; error= FALSE; @@ -9724,7 +9764,7 @@ LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name, */ bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg, - uint max_byte_length) + size_t max_byte_length) { if (str->length <= max_byte_length) return FALSE; @@ -9754,7 +9794,7 @@ bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg, bool check_string_char_length(const LEX_CSTRING *str, uint err_msg, - uint max_char_length, CHARSET_INFO *cs, + size_t max_char_length, CHARSET_INFO *cs, bool no_error) { Well_formed_prefix prefix(cs, str->str, str->length, max_char_length); @@ -9798,7 +9838,7 @@ extern "C" { int path_starts_from_data_home_dir(const char *path) { - int dir_len= strlen(path); + size_t dir_len= strlen(path); DBUG_ENTER("path_starts_from_data_home_dir"); if (mysql_unpacked_real_data_home_len<= dir_len) diff --git a/sql/sql_parse.h b/sql/sql_parse.h index b8d7bb46e08..d23da6f1b68 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -16,7 +16,6 @@ #ifndef SQL_PARSE_INCLUDED #define SQL_PARSE_INCLUDED -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_acl.h" /* GLOBAL_ACLS */ class Comp_creator; @@ -74,9 +73,9 @@ LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name, LEX_CSTRING *host_nam LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true); bool sp_process_definer(THD *thd); bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg, - uint max_byte_length); + size_t max_byte_length); bool check_string_char_length(const LEX_CSTRING *str, uint err_msg, - uint max_char_length, CHARSET_INFO *cs, + size_t max_char_length, CHARSET_INFO *cs, bool no_error); bool check_ident_length(const LEX_CSTRING *ident); CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl); @@ -88,13 +87,13 @@ bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool sqlcom_can_generate_row_events(const THD *thd); bool is_update_query(enum enum_sql_command command); bool is_log_table_write_query(enum enum_sql_command command); -bool alloc_query(THD *thd, const char *packet, uint packet_length); +bool alloc_query(THD *thd, const char *packet, size_t packet_length); void mysql_init_select(LEX *lex); void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state, bool is_com_multi, bool is_next_command); bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); -void create_select_for_variable(const char *var_name); +void create_select_for_variable(THD *thd, LEX_CSTRING *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); @@ -108,9 +107,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, bool is_com_multi, bool is_next_command); void log_slow_statement(THD *thd); bool append_file_to_dir(THD *thd, const char **filename_ptr, - const char *table_name); -bool append_file_to_dir(THD *thd, const char **filename_ptr, - const char *table_name); + const LEX_CSTRING *table_name); void execute_init_command(THD *thd, LEX_STRING *init_command, mysql_rwlock_t *var_lock); bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group, bool asc); @@ -132,7 +129,7 @@ bool check_stack_overrun(THD *thd, long margin, uchar *dummy); extern const char* any_db; extern uint sql_command_flags[]; extern uint server_command_flags[]; -extern const LEX_STRING command_name[]; +extern const LEX_CSTRING command_name[]; extern uint server_command_flags[]; /* Inline functions */ @@ -150,8 +147,9 @@ inline bool check_identifier_name(LEX_CSTRING *str) bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors); -bool check_routine_access(THD *thd,ulong want_access,const char *db, - const char *name, +bool check_routine_access(THD *thd,ulong want_access, + const LEX_CSTRING *db, + const LEX_CSTRING *name, const Sp_handler *sph, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_some_routine_access(THD *thd, const char *db, const char *name, @@ -166,8 +164,9 @@ inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables inline bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors) { return false; } -inline bool check_routine_access(THD *thd,ulong want_access, const char *db, - const char *name, +inline bool check_routine_access(THD *thd,ulong want_access, + const LEX_CSTRING *db, + const LEX_CSTRING *name, const Sp_handler *sph, bool no_errors) { return false; } inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 173a7391e99..5d1f5d4e1a3 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -47,7 +47,7 @@ /* Some general useful functions */ #define MYSQL_LEX 1 -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_partition.h" #include "key.h" // key_restore @@ -67,6 +67,7 @@ #include "opt_range.h" // store_key_image_to_rec #include "sql_alter.h" // Alter_table_ctx #include "sql_select.h" +#include "sql_tablespace.h" // check_tablespace_name #include <algorithm> using std::max; @@ -87,6 +88,7 @@ static int get_partition_id_list_col(partition_info *, uint32 *, longlong *); static int get_partition_id_list(partition_info *, uint32 *, longlong *); static int get_partition_id_range_col(partition_info *, uint32 *, longlong *); static int get_partition_id_range(partition_info *, uint32 *, longlong *); +static int vers_get_partition_id(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_part(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_subpart(partition_info *, uint32 *); static int get_partition_id_hash_nosub(partition_info *, uint32 *, longlong *); @@ -1295,6 +1297,24 @@ static void set_up_partition_func_pointers(partition_info *part_info) part_info->get_subpartition_id= get_partition_id_hash_sub; } } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_part_partition_id= vers_get_partition_id; + if (part_info->list_of_subpart_fields) + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_key_sub; + else + part_info->get_subpartition_id= get_partition_id_key_sub; + } + else + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_hash_sub; + else + part_info->get_subpartition_id= get_partition_id_hash_sub; + } + } else /* LIST Partitioning */ { if (part_info->column_list) @@ -1335,6 +1355,10 @@ static void set_up_partition_func_pointers(partition_info *part_info) else part_info->get_partition_id= get_partition_id_list; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_partition_id= vers_get_partition_id; + } else /* HASH partitioning */ { if (part_info->list_of_part_fields) @@ -1559,15 +1583,15 @@ bool fix_partition_func(THD *thd, TABLE *table, { bool result= TRUE; partition_info *part_info= table->part_info; - enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + enum_column_usage saved_column_usage= thd->column_usage; DBUG_ENTER("fix_partition_func"); if (part_info->fixed) { DBUG_RETURN(FALSE); } - thd->mark_used_columns= MARK_COLUMNS_NONE; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= COLUMNS_WRITE; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); if (!is_create_table_ind || thd->lex->sql_command != SQLCOM_CREATE_TABLE) @@ -1607,6 +1631,7 @@ bool fix_partition_func(THD *thd, TABLE *table, } } DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION); + DBUG_ASSERT(part_info->part_type != VERSIONING_PARTITION || part_info->column_list); /* Partition is defined. We need to verify that partitioning function is correct. @@ -1639,6 +1664,9 @@ bool fix_partition_func(THD *thd, TABLE *table, const char *error_str; if (part_info->column_list) { + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_setup_expression(thd)) + goto end; List_iterator<const char> it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; @@ -1662,6 +1690,12 @@ bool fix_partition_func(THD *thd, TABLE *table, if (unlikely(part_info->check_list_constants(thd))) goto end; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + error_str= "SYSTEM_TIME"; + if (unlikely(part_info->check_range_constants(thd))) + goto end; + } else { DBUG_ASSERT(0); @@ -1722,8 +1756,8 @@ bool fix_partition_func(THD *thd, TABLE *table, table->file->set_part_info(part_info); result= FALSE; end: - thd->mark_used_columns= save_mark_used_columns; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= saved_column_usage; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); DBUG_RETURN(result); } @@ -1829,7 +1863,7 @@ static int add_keyword_path(String *str, const char *keyword, #ifdef __WIN__ /* Convert \ to / to be able to create table on unix */ char *pos, *end; - uint length= strlen(temp_path); + size_t length= strlen(temp_path); for (pos= temp_path, end= pos+length ; pos < end ; pos++) { if (*pos == '\\') @@ -2182,6 +2216,20 @@ static int add_partition_values(String *str, partition_info *part_info, } while (++i < num_items); err+= str->append(')'); } + else if (part_info->part_type == VERSIONING_PARTITION) + { + switch (p_elem->type()) + { + case partition_element::CURRENT: + err+= str->append(STRING_WITH_LEN(" CURRENT")); + break; + case partition_element::HISTORY: + err+= str->append(STRING_WITH_LEN(" HISTORY")); + break; + default: + DBUG_ASSERT(0 && "wrong p_elem->type"); + } + } end: return err; } @@ -2275,13 +2323,32 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, else err+= str.append(STRING_WITH_LEN("HASH ")); break; + case VERSIONING_PARTITION: + err+= str.append(STRING_WITH_LEN("SYSTEM_TIME ")); + break; default: DBUG_ASSERT(0); /* We really shouldn't get here, no use in continuing from here */ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); DBUG_RETURN(NULL); } - if (part_info->part_expr) + if (part_info->part_type == VERSIONING_PARTITION) + { + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info); + if (vers_info->interval) + { + err+= str.append(STRING_WITH_LEN("INTERVAL ")); + err+= str.append_ulonglong(vers_info->interval); + err+= str.append(STRING_WITH_LEN(" SECOND ")); + } + if (vers_info->limit) + { + err+= str.append(STRING_WITH_LEN("LIMIT ")); + err+= str.append_ulonglong(vers_info->limit); + } + } + else if (part_info->part_expr) { err+= str.append('('); part_info->part_expr->print_for_table_def(&str); @@ -3088,6 +3155,74 @@ int get_partition_id_range_col(partition_info *part_info, } +int vers_get_partition_id(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + DBUG_ENTER("vers_get_partition_id"); + DBUG_ASSERT(part_info); + Field *row_end= part_info->part_field_array[STAT_TRX_END]; + DBUG_ASSERT(row_end); + TABLE *table= part_info->table; + DBUG_ASSERT(table); + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info); + DBUG_ASSERT(vers_info->initialized()); + DBUG_ASSERT(row_end->table == table); + DBUG_ASSERT(table->versioned()); + DBUG_ASSERT(table->vers_end_field() == row_end); + + // new rows have NULL in row_end + if (row_end->is_max() || row_end->is_null()) + { + *part_id= vers_info->now_part->id; + } + else // row is historical + { + THD *thd= current_thd; + + switch (thd->lex->sql_command) + { + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_ALTER_TABLE: + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + part_info->vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + // transaction is not yet pushed to VTQ, so we use now-time + ulong sec_part; + my_time_t end_ts= row_end->table->versioned(VERS_TRX_ID) ? + my_time_t(0) : row_end->get_timestamp(&sec_part); + if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(end_ts)) + { + part_info->vers_part_rotate(thd); + } + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + break; + default: + ; + } + *part_id= vers_info->hist_part->id; + } + + DBUG_PRINT("exit",("partition: %d", *part_id)); + DBUG_RETURN(0); +} + + int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value) @@ -4444,6 +4579,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */ DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)); + partition_info *saved_part_info= NULL; + if (alter_info->flags & (Alter_info::ALTER_ADD_PARTITION | Alter_info::ALTER_DROP_PARTITION | @@ -4453,7 +4590,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, Alter_info::ALTER_REBUILD_PARTITION)) { partition_info *tab_part_info; - uint flags= 0; + ulonglong flags= 0; bool is_last_partition_reorged= FALSE; part_elem_value *tab_max_elem_val= NULL; part_elem_value *alt_max_elem_val= NULL; @@ -4473,8 +4610,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, object to allow fast_alter_partition_table to perform the changes. */ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, - alter_ctx->db, - alter_ctx->table_name, + alter_ctx->db.str, + alter_ctx->table_name.str, MDL_INTENTION_EXCLUSIVE)); tab_part_info= table->part_info; @@ -4608,6 +4745,10 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "LIST", "IN"); } + else if (thd->work_part_info->part_type == VERSIONING_PARTITION) + { + my_error(ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME"); + } else { DBUG_ASSERT(tab_part_info->part_type == RANGE_PARTITION || @@ -4640,6 +4781,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, } if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION) { + if (*fast_alter_table && thd->locked_tables_mode) + { + MEM_ROOT *old_root= thd->mem_root; + thd->mem_root= &thd->locked_tables_list.m_locked_tables_root; + saved_part_info= tab_part_info->get_clone(thd); + thd->mem_root= old_root; + saved_part_info->read_partitions= tab_part_info->read_partitions; + saved_part_info->lock_partitions= tab_part_info->lock_partitions; + saved_part_info->bitmaps_are_initialized= tab_part_info->bitmaps_are_initialized; + } /* We start by moving the new partitions to the list of temporary partitions. We will then check that the new partitions fit in the @@ -4655,7 +4806,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, must know the number of new partitions in this case. */ if (thd->lex->no_write_to_binlog && - tab_part_info->part_type != HASH_PARTITION) + tab_part_info->part_type != HASH_PARTITION && + tab_part_info->part_type != VERSIONING_PARTITION) { my_error(ER_NO_BINLOG_ERROR, MYF(0)); goto err; @@ -4860,6 +5012,21 @@ that are reorganised. partition configuration is made. */ { + partition_element *now_part= NULL; + if (tab_part_info->part_type == VERSIONING_PARTITION) + { + List_iterator<partition_element> it(tab_part_info->partitions); + partition_element *el; + while ((el= it++)) + { + if (el->type() == partition_element::CURRENT) + { + DBUG_ASSERT(tab_part_info->vers_info && el == tab_part_info->vers_info->now_part); + it.remove(); + now_part= el; + } + } + } List_iterator<partition_element> alt_it(alt_part_info->partitions); uint part_count= 0; do @@ -4874,6 +5041,15 @@ that are reorganised. } } while (++part_count < num_new_partitions); tab_part_info->num_parts+= num_new_partitions; + if (tab_part_info->part_type == VERSIONING_PARTITION) + { + DBUG_ASSERT(now_part); + if (tab_part_info->partitions.push_back(now_part, thd->mem_root)) + { + mem_alloc_error(1); + goto err; + } + } } /* If we specify partitions explicitly we don't use defaults anymore. @@ -4907,16 +5083,28 @@ that are reorganised. List_iterator<partition_element> part_it(tab_part_info->partitions); tab_part_info->is_auto_partitioned= FALSE; - if (!(tab_part_info->part_type == RANGE_PARTITION || - tab_part_info->part_type == LIST_PARTITION)) + if (tab_part_info->part_type == VERSIONING_PARTITION) { - my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); - goto err; + if (num_parts_dropped >= tab_part_info->num_parts - 1) + { + DBUG_ASSERT(table && table->s && table->s->table_name.str); + my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); + goto err; + } } - if (num_parts_dropped >= tab_part_info->num_parts) + else { - my_error(ER_DROP_LAST_PARTITION, MYF(0)); - goto err; + if (!(tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION)) + { + my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); + goto err; + } + if (num_parts_dropped >= tab_part_info->num_parts) + { + my_error(ER_DROP_LAST_PARTITION, MYF(0)); + goto err; + } } do { @@ -4924,6 +5112,13 @@ that are reorganised. if (is_name_in_list(part_elem->partition_name, alter_info->partition_names)) { + if (tab_part_info->part_type == VERSIONING_PARTITION && + part_elem->type() == partition_element::CURRENT) + { + DBUG_ASSERT(table && table->s && table->s->table_name.str); + my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); + goto err; + } /* Set state to indicate that the partition is to be dropped. */ @@ -5246,8 +5441,9 @@ the generated partition syntax in a correct manner. tab_part_info->use_default_subpartitions= FALSE; tab_part_info->use_default_num_subpartitions= FALSE; } + if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, - table->file, 0, TRUE)) + table->file, 0, alt_part_info)) { goto err; } @@ -5284,7 +5480,7 @@ the generated partition syntax in a correct manner. goto err; } } - } + } // ADD, DROP, COALESCE, REORGANIZE, TABLE_REORG, REBUILD else { /* @@ -5450,6 +5646,8 @@ the generated partition syntax in a correct manner. DBUG_RETURN(FALSE); err: *fast_alter_table= false; + if (saved_part_info) + table->part_info= saved_part_info; DBUG_RETURN(TRUE); } @@ -5488,7 +5686,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) THD *thd= lpt->thd; DBUG_ENTER("mysql_change_partitions"); - build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); if(mysql_trans_prepare_alter_copy_data(thd)) DBUG_RETURN(TRUE); @@ -5534,7 +5732,7 @@ static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) int error; DBUG_ENTER("mysql_rename_partitions"); - build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); if ((error= lpt->table->file->ha_rename_partitions(path))) { if (error != 1) @@ -5580,7 +5778,7 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->table->s->table_name.str, MDL_EXCLUSIVE)); - build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); if ((error= lpt->table->file->ha_drop_partitions(path))) { lpt->table->file->print_error(error, MYF(0)); @@ -5977,8 +6175,7 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_ENTER("write_log_rename_frm"); part_info->first_log_entry= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db, - lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE)) @@ -6029,8 +6226,7 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_ENTER("write_log_drop_partition"); part_info->first_log_entry= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db, - lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, @@ -6088,8 +6284,7 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) DBUG_ASSERT(old_first_log_entry); DBUG_ENTER("write_log_add_change_partition"); - build_table_filename(path, sizeof(path) - 1, lpt->db, - lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); @@ -6157,8 +6352,7 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) Replace the revert operations with forced retry operations. */ part_info->first_log_entry= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db, - lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); mysql_mutex_lock(&LOCK_gdl); if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) @@ -6343,8 +6537,8 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, Better to do that here, than leave the cleaning up to others. Aquire EXCLUSIVE mdl lock if not already aquired. */ - if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db, - lpt->table_name, + if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db.str, + lpt->table_name.str, MDL_EXCLUSIVE)) { if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) @@ -6541,8 +6735,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - const char *db, - const char *table_name) + const LEX_CSTRING *db, + const LEX_CSTRING *table_name) { /* Set-up struct used to write frm files */ partition_info *part_info; @@ -6565,8 +6759,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->table= table; lpt->key_info_buffer= 0; lpt->key_count= 0; - lpt->db= db; - lpt->table_name= table_name; + lpt->db= *db; + lpt->table_name= *table_name; lpt->copied= 0; lpt->deleted= 0; lpt->pack_frm_data= NULL; @@ -6914,6 +7108,39 @@ err: } #endif + +/* + Prepare for calling val_int on partition function by setting fields to + point to the record where the values of the PF-fields are stored. + + SYNOPSIS + set_field_ptr() + ptr Array of fields to change ptr + new_buf New record pointer + old_buf Old record pointer + + DESCRIPTION + Set ptr in field objects of field array to refer to new_buf record + instead of previously old_buf. Used before calling val_int and after + it is used to restore pointers to table->record[0]. + This routine is placed outside of partition code since it can be useful + also for other programs. +*/ + +void set_field_ptr(Field **ptr, const uchar *new_buf, + const uchar *old_buf) +{ + my_ptrdiff_t diff= (new_buf - old_buf); + DBUG_ENTER("set_field_ptr"); + + do + { + (*ptr)->move_field_offset(diff); + } while (*(++ptr)); + DBUG_VOID_RETURN; +} + + /* Prepare for calling val_int on partition function by setting fields to point to the record where the values of the PF-fields are stored. @@ -6952,6 +7179,61 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf, } +/** + Append all fields in read_set to string + + @param[in,out] str String to append to. + @param[in] row Row to append. + @param[in] table Table containing read_set and fields for the row. +*/ +void append_row_to_str(String &str, const uchar *row, TABLE *table) +{ + Field **fields, **field_ptr; + const uchar *rec; + uint num_fields= bitmap_bits_set(table->read_set); + uint curr_field_index= 0; + bool is_rec0= !row || row == table->record[0]; + if (!row) + rec= table->record[0]; + else + rec= row; + + /* Create a new array of all read fields. */ + fields= (Field**) my_malloc(sizeof(void*) * (num_fields + 1), + MYF(0)); + if (!fields) + return; + fields[num_fields]= NULL; + for (field_ptr= table->field; + *field_ptr; + field_ptr++) + { + if (!bitmap_is_set(table->read_set, (*field_ptr)->field_index)) + continue; + fields[curr_field_index++]= *field_ptr; + } + + + if (!is_rec0) + set_field_ptr(fields, rec, table->record[0]); + + for (field_ptr= fields; + *field_ptr; + field_ptr++) + { + Field *field= *field_ptr; + str.append(" "); + str.append(&field->field_name); + str.append(":"); + field_unpack(&str, field, rec, 0, false); + } + + if (!is_rec0) + set_field_ptr(fields, table->record[0], rec); + my_free(fields); +} + + /* SYNOPSIS mem_alloc_error() @@ -7098,6 +7380,7 @@ static void set_up_range_analysis_info(partition_info *part_info) switch (part_info->part_type) { case RANGE_PARTITION: case LIST_PARTITION: + case VERSIONING_PARTITION: if (!part_info->column_list) { if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC) @@ -7398,7 +7681,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info, uint full_length= 0; DBUG_ENTER("get_part_iter_for_interval_cols_via_map"); - if (part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION) { get_col_endpoint= get_partition_id_cols_range_for_endpoint; part_iter->get_next= get_next_partition_id_range; @@ -7444,7 +7727,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info, } if (flags & NO_MAX_RANGE) { - if (part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION) part_iter->part_nums.end= part_info->num_parts; else /* LIST_PARTITION */ { @@ -8147,4 +8430,5 @@ uint get_partition_field_store_length(Field *field) store_length+= HA_KEY_BLOB_LENGTH; return store_length; } + #endif diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 992229afb05..a5b502b37c9 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -41,6 +41,7 @@ typedef struct st_key_range key_range; #define HA_CAN_UPDATE_PARTITION_KEY (1 << 1) #define HA_CAN_PARTITION_UNIQUE (1 << 2) #define HA_USE_AUTO_PARTITION (1 << 3) +#define HA_ONLY_VERS_PARTITION (1 << 4) #define NORMAL_PART_NAME 0 #define TEMP_PART_NAME 1 @@ -56,8 +57,8 @@ typedef struct st_lock_param_type Alter_info *alter_info; TABLE *table; KEY *key_info_buffer; - const char *db; - const char *table_name; + LEX_CSTRING db; + LEX_CSTRING table_name; uchar *pack_frm_data; uint key_count; uint db_options; @@ -128,6 +129,14 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, bool check_part_func_fields(Field **ptr, bool ok_with_charsets); bool field_is_partition_charset(Field *field); Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs); +/** + Append all fields in read_set to string + + @param[in,out] str String to append to. + @param[in] row Row to append. + @param[in] table Table containing read_set and fields for the row. +*/ +void append_row_to_str(String &str, const uchar *row, TABLE *table); void mem_alloc_error(size_t size); void truncate_partition_filename(char *path); @@ -258,8 +267,8 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - const char *db, - const char *table_name); + const LEX_CSTRING *db, + const LEX_CSTRING *table_name); bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, enum partition_state part_state); uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, @@ -291,4 +300,30 @@ int __attribute__((warn_unused_result)) void set_key_field_ptr(KEY *key_info, const uchar *new_buf, const uchar *old_buf); +/** Set up table for creating a partition. +Copy info from partition to the table share so the created partition +has the correct info. + @param thd THD object + @param share Table share to be updated. + @param info Create info to be updated. + @param part_elem partition_element containing the info. + + @return status + @retval TRUE Error + @retval FALSE Success + + @details + Set up + 1) Comment on partition + 2) MAX_ROWS, MIN_ROWS on partition + 3) Index file name on partition + 4) Data file name on partition +*/ +bool set_up_table_before_create(THD *thd, + TABLE_SHARE *share, + const char *partition_name_with_path, + HA_CREATE_INFO *info, + partition_element *part_elem); + #endif /* SQL_PARTITION_INCLUDED */ + diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index d51556c94db..2e998878351 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -15,6 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_parse.h" // check_one_table_access // check_merge_table_access // check_one_table_access @@ -70,15 +71,15 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) DBUG_RETURN(TRUE); /* Must be set in the parser */ - DBUG_ASSERT(select_lex->db); + DBUG_ASSERT(select_lex->db.str); /* also check the table to be exchanged with the partition */ DBUG_ASSERT(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION); - if (check_access(thd, priv_needed, first_table->db, + if (check_access(thd, priv_needed, first_table->db.str, &first_table->grant.privilege, &first_table->grant.m_internal, 0, 0) || - check_access(thd, priv_needed, first_table->next_local->db, + check_access(thd, priv_needed, first_table->next_local->db.str, &first_table->next_local->grant.privilege, &first_table->next_local->grant.m_internal, 0, 0)) @@ -91,7 +92,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) DBUG_ASSERT(!create_info.data_file_name && !create_info.index_file_name); WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); - thd->enable_slow_log= opt_log_slow_admin_statements; + thd->prepare_logs_for_admin_command(); DBUG_RETURN(exchange_partition(thd, first_table, &alter_info)); #ifdef WITH_WSREP error: @@ -551,13 +552,13 @@ bool Sql_cmd_alter_table_exchange_partition:: /* Will append the partition name later in part_info->get_part_elem() */ part_file_name_len= build_table_filename(part_file_name, sizeof(part_file_name), - table_list->db, - table_list->table_name, + table_list->db.str, + table_list->table_name.str, "", 0); build_table_filename(swap_file_name, sizeof(swap_file_name), - swap_table_list->db, - swap_table_list->table_name, + swap_table_list->db.str, + swap_table_list->table_name.str, "", 0); /* create a unique temp name #sqlx-nnnn_nnnn, x for eXchange */ my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%llx", @@ -565,7 +566,7 @@ bool Sql_cmd_alter_table_exchange_partition:: if (lower_case_table_names) my_casedn_str(files_charset_info, temp_name); build_table_filename(temp_file_name, sizeof(temp_file_name), - table_list->next_local->db, + table_list->next_local->db.str, temp_name, "", FN_IS_TMP); if (!(part_elem= part_table->part_info->get_part_elem(partition_name, @@ -767,7 +768,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) (!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table)) && wsrep_to_isolation_begin( - thd, first_table->db, first_table->table_name, NULL) + thd, first_table->db.str, first_table->table_name.str, NULL) ) { WSREP_WARN("ALTER TABLE TRUNCATE PARTITION isolation failure"); @@ -801,8 +802,8 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) DBUG_RETURN(true); partition_names_list.push_back(str_partition_name, thd->mem_root); } - first_table->partition_names= &partition_names_list; - if (first_table->table->part_info->set_partition_bitmaps(first_table)) + if (first_table->table-> + part_info->set_partition_bitmaps(&partition_names_list)) DBUG_RETURN(true); if (lock_tables(thd, first_table, table_counter, 0)) @@ -817,8 +818,8 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) if (thd->mdl_context.upgrade_shared_lock(ticket, MDL_EXCLUSIVE, timeout)) DBUG_RETURN(TRUE); - tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db, - first_table->table_name, FALSE); + tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db.str, + first_table->table_name.str, FALSE); partition= (ha_partition*) first_table->table->file; /* Invoke the handler method responsible for truncating the partition. */ diff --git a/sql/sql_plist.h b/sql/sql_plist.h index df50cccc874..14f6eb5e2aa 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -16,8 +16,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> - template <typename T, typename L> class I_P_List_iterator; class I_P_List_null_counter; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 0a72e3c3d5a..605f96293d3 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -15,8 +15,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "sql_plugin.h" // Includes my_global.h -#include "sql_priv.h" // SHOW_MY_BOOL +#include "sql_plugin.h" // SHOW_MY_BOOL +#include "sql_priv.h" #include "unireg.h" #include "sql_class.h" // set_var.h: THD #include "sys_vars_shared.h" @@ -66,6 +66,8 @@ char *opt_plugin_dir_ptr; char opt_plugin_dir[FN_REFLEN]; ulong plugin_maturity; +static LEX_CSTRING MYSQL_PLUGIN_NAME= {STRING_WITH_LEN("plugin") }; + /* not really needed now, this map will become essential when we add more maturity levels. We cannot change existing maturity constants, @@ -80,18 +82,18 @@ uint plugin_maturity_map[]= When you ad a new plugin type, add both a string and make sure that the init and deinit array are correctly updated. */ -const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= +const LEX_CSTRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - { C_STRING_WITH_LEN("UDF") }, - { C_STRING_WITH_LEN("STORAGE ENGINE") }, - { C_STRING_WITH_LEN("FTPARSER") }, - { C_STRING_WITH_LEN("DAEMON") }, - { C_STRING_WITH_LEN("INFORMATION SCHEMA") }, - { C_STRING_WITH_LEN("AUDIT") }, - { C_STRING_WITH_LEN("REPLICATION") }, - { C_STRING_WITH_LEN("AUTHENTICATION") }, - { C_STRING_WITH_LEN("PASSWORD VALIDATION") }, - { C_STRING_WITH_LEN("ENCRYPTION") } + { STRING_WITH_LEN("UDF") }, + { STRING_WITH_LEN("STORAGE ENGINE") }, + { STRING_WITH_LEN("FTPARSER") }, + { STRING_WITH_LEN("DAEMON") }, + { STRING_WITH_LEN("INFORMATION SCHEMA") }, + { STRING_WITH_LEN("AUDIT") }, + { STRING_WITH_LEN("REPLICATION") }, + { STRING_WITH_LEN("AUTHENTICATION") }, + { STRING_WITH_LEN("PASSWORD VALIDATION") }, + { STRING_WITH_LEN("ENCRYPTION") } }; extern int initialize_schema_table(st_plugin_int *plugin); @@ -238,7 +240,7 @@ ulong dlopen_count; the following variables/structures */ static MEM_ROOT plugin_vars_mem_root; -static uint global_variables_dynamic_size= 0; +static size_t global_variables_dynamic_size= 0; static HASH bookmark_hash; @@ -733,7 +735,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_CSTRING *dl, int report) { #ifdef HAVE_DLOPEN char dlpath[FN_REFLEN]; - uint plugin_dir_len, dummy_errors, i; + size_t plugin_dir_len,i; + uint dummy_errors; struct st_plugin_dl *tmp= 0, plugin_dl; void *sym; st_ptr_backup tmp_backup[array_elements(list_of_services)]; @@ -950,6 +953,10 @@ SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type) } +/* + If LEX is passed non-NULL, an automatic unlock of the plugin will happen + in the LEX destructor. +*/ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, uint state_mask= PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED | @@ -995,6 +1002,16 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, } +/* + Notes on lifetime: + + If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made + in the thd->lex which will cause an automatic unlock of the plugin in the LEX + destructor. In this case, no manual unlock must be done. + + Otherwise, when passing a NULL THD, the caller must arrange that plugin + unlock happens later. +*/ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) { LEX *lex= thd ? thd->lex : 0; @@ -1031,6 +1048,16 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) } +/* + Notes on lifetime: + + If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made + in the thd->lex which will cause an automatic unlock of the plugin in the LEX + destructor. In this case, no manual unlock must be done. + + Otherwise, when passing a NULL THD, the caller must arrange that plugin + unlock happens later. +*/ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) { LEX *lex= thd ? thd->lex : 0; @@ -1136,6 +1163,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf); goto err; } + if (plugin_maturity_map[plugin->maturity] < plugin_maturity) { char buf[256]; @@ -1148,6 +1176,14 @@ static bool plugin_add(MEM_ROOT *tmp_root, report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf); goto err; } + else if (plugin_maturity_map[plugin->maturity] < SERVER_MATURITY_LEVEL) + { + sql_print_warning("Plugin '%s' is of maturity level %s while the server is %s", + tmp.name.str, + plugin_maturity_names[plugin->maturity], + plugin_maturity_names[SERVER_MATURITY_LEVEL]); + } + tmp.plugin= plugin; tmp.ref_count= 0; tmp.state= PLUGIN_IS_UNINITIALIZED; @@ -1488,14 +1524,14 @@ uchar *get_bookmark_hash_key(const uchar *buff, size_t *length, return (uchar*) var->key; } -static inline void convert_dash_to_underscore(char *str, int len) +static inline void convert_dash_to_underscore(char *str, size_t len) { for (char *p= str; p <= str+len; p++) if (*p == '-') *p= '_'; } -static inline void convert_underscore_to_dash(char *str, int len) +static inline void convert_underscore_to_dash(char *str, size_t len) { for (char *p= str; p <= str+len; p++) if (*p == '_') @@ -1648,7 +1684,7 @@ int plugin_init(int *argc, char **argv, int flags) */ global_system_variables.table_plugin = intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr)); - DBUG_ASSERT(plugin_ptr->ref_count == 1); + DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1); } mysql_mutex_unlock(&LOCK_plugin); @@ -1789,11 +1825,9 @@ static void plugin_load(MEM_ROOT *tmp_root) new_thd->thread_stack= (char*) &tables; new_thd->store_globals(); - new_thd->db= my_strdup("mysql", MYF(0)); - new_thd->db_length= 5; + new_thd->db= MYSQL_SCHEMA_NAME; bzero((char*) &new_thd->net, sizeof(new_thd->net)); - tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("plugin"), - "plugin", TL_READ); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_READ); tables.open_strategy= TABLE_LIST::OPEN_NORMAL; result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT); @@ -1819,7 +1853,7 @@ static void plugin_load(MEM_ROOT *tmp_root) goto end; } table->use_all_columns(); - while (!(error= read_record_info.read_record(&read_record_info))) + while (!(error= read_record_info.read_record())) { DBUG_PRINT("info", ("init plugin record")); String str_name, str_dl; @@ -1847,6 +1881,7 @@ static void plugin_load(MEM_ROOT *tmp_root) table->m_needs_reopen= TRUE; // Force close to free memory close_mysql_tables(new_thd); end: + new_thd->db= null_clex_str; // Avoid free on thd->db delete new_thd; DBUG_VOID_RETURN; } @@ -1943,6 +1978,12 @@ void plugin_shutdown(void) if (initialized) { + if (opt_gtid_pos_auto_plugins) + { + free_engine_list(opt_gtid_pos_auto_plugins); + opt_gtid_pos_auto_plugins= NULL; + } + mysql_mutex_lock(&LOCK_plugin); reap_needed= true; @@ -2135,7 +2176,7 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, { MYSQL_AUDIT_GENERAL_CLASSMASK }; DBUG_ENTER("mysql_install_plugin"); - tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE); if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); @@ -2278,7 +2319,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, { MYSQL_AUDIT_GENERAL_CLASSMASK }; DBUG_ENTER("mysql_uninstall_plugin"); - tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE); if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); @@ -2830,7 +2871,8 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, mysql_mutex_unlock(&LOCK_plugin); if (!throw_error && !var) - my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str); + my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), + (int) (length ? length : strlen(str)), (char*) str); DBUG_RETURN(var); } @@ -2849,7 +2891,7 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name, int flags) { st_bookmark *result= NULL; - uint namelen, length, pluginlen= 0; + size_t namelen, length, pluginlen= 0; char *varname, *p; if (!(flags & PLUGIN_VAR_THDLOCAL)) @@ -2905,7 +2947,7 @@ static size_t var_storage_size(int flags) static st_bookmark *register_var(const char *plugin, const char *name, int flags) { - uint length= strlen(plugin) + strlen(name) + 3, size, offset, new_size; + size_t length= strlen(plugin) + strlen(name) + 3, size, offset, new_size; st_bookmark *result; char *varname, *p; @@ -2924,7 +2966,7 @@ static st_bookmark *register_var(const char *plugin, const char *name, sizeof(struct st_bookmark) + length-1); varname[0]= plugin_var_bookmark_key(flags); memcpy(result->key, varname, length); - result->name_len= length - 2; + result->name_len= (uint)(length - 2); result->offset= -1; DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */ @@ -2957,10 +2999,10 @@ static st_bookmark *register_var(const char *plugin, const char *name, global_variables_dynamic_size= new_size; } - global_system_variables.dynamic_variables_head= offset; - max_system_variables.dynamic_variables_head= offset; - global_system_variables.dynamic_variables_size= offset + size; - max_system_variables.dynamic_variables_size= offset + size; + global_system_variables.dynamic_variables_head= (uint)offset; + max_system_variables.dynamic_variables_head= (uint)offset; + global_system_variables.dynamic_variables_size= (uint)(offset + size); + max_system_variables.dynamic_variables_size= (uint)(offset + size); global_system_variables.dynamic_variables_version++; max_system_variables.dynamic_variables_version++; @@ -3657,9 +3699,9 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, my_option *options) { const char *plugin_name= tmp->plugin->name; - const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") }; - uint plugin_name_len= strlen(plugin_name); - uint optnamelen; + const LEX_CSTRING plugin_dash = { STRING_WITH_LEN("plugin-") }; + size_t plugin_name_len= strlen(plugin_name); + size_t optnamelen; const int max_comment_len= 180; char *comment= (char *) alloc_root(mem_root, max_comment_len + 1); char *optname; @@ -3984,7 +4026,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, my_option *opts= NULL; int error= 1; struct st_bookmark *var; - uint len=0, count= EXTRA_OPTIONS; + size_t len=0, count= EXTRA_OPTIONS; st_ptr_backup *tmp_backup= 0; DBUG_ENTER("test_plugin_options"); DBUG_ASSERT(tmp->plugin && tmp->name.str); diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 469ae5c88a3..4e899e18f9b 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -24,8 +24,9 @@ #define SHOW_always_last SHOW_KEY_CACHE_LONG, \ SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ - SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_LEX_STRING -#include <my_global.h> + SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_UINT32_STATUS, \ + SHOW_LEX_STRING +#include "mariadb.h" #undef SHOW_always_last #include "m_string.h" /* LEX_STRING */ @@ -155,7 +156,7 @@ typedef int (*plugin_type_init)(struct st_plugin_int *); extern I_List<i_string> *opt_plugin_load_list_ptr; extern char *opt_plugin_dir_ptr; extern MYSQL_PLUGIN_IMPORT char opt_plugin_dir[FN_REFLEN]; -extern const LEX_STRING plugin_type_names[]; +extern const LEX_CSTRING plugin_type_names[]; extern ulong plugin_maturity; extern TYPELIB plugin_maturity_values; extern const char *plugin_maturity_names[]; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 52200798cf3..dbc03234c44 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -84,7 +84,7 @@ When one supplies long data for a placeholder: at statement execute. */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_class.h" // set_var.h: THD @@ -215,7 +215,7 @@ private: MEM_ROOT main_mem_root; sql_mode_t m_sql_mode; private: - bool set_db(const char *db, uint db_length); + bool set_db(const LEX_CSTRING *db); bool set_parameters(String *expanded_query, uchar *packet, uchar *packet_end); bool execute(String *expanded_query, bool open_cursor); @@ -1292,7 +1292,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, { my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), (table_list->view ? table_list->view_name.str : - table_list->table_name)); + table_list->table_name.str)); goto error; } while ((values= its++)) @@ -1304,7 +1304,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, goto error; } if (setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_NONE, 0, NULL, 0)) + *values, COLUMNS_READ, 0, NULL, 0)) goto error; } } @@ -1376,7 +1376,7 @@ static int mysql_test_update(Prepared_statement *stmt, if (!table_list->single_table_updatable()) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); goto error; } @@ -1410,7 +1410,7 @@ static int mysql_test_update(Prepared_statement *stmt, table_list->register_want_access(SELECT_ACL); #endif if (setup_fields(thd, Ref_ptr_array(), - stmt->lex->value_list, MARK_COLUMNS_NONE, 0, NULL, 0) || + stmt->lex->value_list, COLUMNS_READ, 0, NULL, 0) || check_unique_table(thd, table_list)) goto error; /* TODO: here we should send types of placeholders to the client. */ @@ -1454,7 +1454,7 @@ static bool mysql_test_delete(Prepared_statement *stmt, if (!table_list->single_table_updatable()) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "DELETE"); goto error; } if (!table_list->table || !table_list->table->is_created()) @@ -1583,7 +1583,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, DT_PREPARE | DT_CREATE)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_NONE, 0, NULL, 0)); + *values, COLUMNS_READ, 0, NULL, 0)); } @@ -2929,7 +2929,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) for (order= sl->order_list.first; order; order= order->next) order->item= &order->item_ptr; { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool res= #endif sl->handle_derived(lex, DT_REINIT); @@ -3781,7 +3781,7 @@ void Prepared_statement::cleanup_stmt() { DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: %p", this)); - thd->restore_set_statement_var(); + lex->restore_set_statement_var(); thd->rollback_item_tree_changes(); cleanup_items(free_list); thd->cleanup_after_query(); @@ -3805,24 +3805,22 @@ bool Prepared_statement::set_name(LEX_CSTRING *name_arg) a prepared statement since it affects execution environment: privileges, @@character_set_database, and other. - @return Returns an error if out of memory. + @return 1 if out of memory. */ bool -Prepared_statement::set_db(const char *db_arg, uint db_length_arg) +Prepared_statement::set_db(const LEX_CSTRING *db_arg) { /* Remember the current database. */ - if (db_arg && db_length_arg) + if (db_arg->length) { - db= this->strmake(db_arg, db_length_arg); - db_length= db_length_arg; + if (!(db.str= this->strmake(db_arg->str, db_arg->length))) + return 1; + db.length= db_arg->length; } else - { - db= NULL; - db_length= 0; - } - return db_arg != NULL && db == NULL; + db= null_clex_str; + return 0; } /************************************************************************** @@ -3871,7 +3869,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) DBUG_RETURN(TRUE); stmt_lex= lex; - if (set_db(thd->db, thd->db_length)) + if (set_db(&thd->db)) DBUG_RETURN(TRUE); /* @@ -4250,7 +4248,7 @@ Prepared_statement::execute_bulk_loop(String *expanded_query, packet_end= packet_end_arg; iterations= TRUE; start_param= true; -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS Item *free_list_state= thd->free_list; #endif thd->set_bulk_execution((void *)this); @@ -4445,7 +4443,7 @@ Prepared_statement::reprepare() char saved_cur_db_name_buf[SAFE_NAME_LEN+1]; LEX_STRING saved_cur_db_name= { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; - LEX_CSTRING stmt_db_name= { db, db_length }; + LEX_CSTRING stmt_db_name= db; bool cur_db_changed; bool error; @@ -4474,7 +4472,7 @@ Prepared_statement::reprepare() { swap_prepared_statement(©); swap_parameter_array(param_array, copy.param_array, param_count); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS is_reprepared= TRUE; #endif /* @@ -4568,8 +4566,7 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) /* Swap names, the old name is allocated in the wrong memory root */ swap_variables(LEX_CSTRING, name, copy->name); /* Ditto */ - swap_variables(char *, db, copy->db); - swap_variables(size_t, db_length, copy->db_length); + swap_variables(LEX_CSTRING, db, copy->db); DBUG_ASSERT(param_count == copy->param_count); DBUG_ASSERT(thd == copy->thd); @@ -4611,7 +4608,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; bool cur_db_changed; - LEX_CSTRING stmt_db_name= { db, db_length }; + LEX_CSTRING stmt_db_name= db; status_var_increment(thd->status_var.com_stmt_execute); @@ -4712,7 +4709,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) PSI_statement_locker *parent_locker; MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, - (char *) (thd->db ? thd->db : ""), + thd->get_db(), &thd->security_ctx->priv_user[0], (char *) thd->security_ctx->host_or_ip, 1); diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h index 203b37b3b26..ca040da341f 100644 --- a/sql/sql_prepare.h +++ b/sql/sql_prepare.h @@ -130,6 +130,7 @@ public: size_t get_field_count() const { return m_column_count; } static void operator delete(void *ptr, size_t size) throw (); + static void operator delete(void *, MEM_ROOT *){} private: Ed_result_set(const Ed_result_set &); /* not implemented */ Ed_result_set &operator=(Ed_result_set &); /* not implemented */ @@ -215,21 +216,6 @@ public: bool execute_direct(Server_runnable *server_runnable); /** - Get the number of result set fields. - - This method is valid only if we have a result: - execute_direct() has been called. Otherwise - the returned value is undefined. - - @sa Documentation for C API function - mysql_field_count() - */ - ulong get_field_count() const - { - return m_current_rset ? m_current_rset->get_field_count() : 0; - } - - /** Get the number of affected (deleted, updated) rows for the current statement. Can be used for statements with get_field_count() == 0. diff --git a/sql/sql_priv.h b/sql/sql_priv.h index db75dc2198b..ba37d933f12 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -227,7 +227,7 @@ #define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28) #define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) #define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) -#define OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED (1ULL << 31) +#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31) #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -254,7 +254,7 @@ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ - OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED) + OPTIMIZER_SWITCH_SPLIT_MATERIALIZED) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you @@ -327,6 +327,8 @@ /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) +#define IN_SUBQUERY_CONVERSION_THRESHOLD 1000 + #endif /* !MYSQL_CLIENT */ /* BINLOG_DUMP options */ @@ -352,6 +354,8 @@ enum enum_parsing_place IN_ON, IN_GROUP_BY, IN_ORDER_BY, + IN_UPDATE_ON_DUP_KEY, + IN_PART_FUNC, PARSING_PLACE_SIZE /* always should be the last */ }; diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 7206162792a..13f03fed5f3 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -29,10 +29,9 @@ - "profiling_history_size", integer, session + global, "Num queries stored?" */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_profile.h" -#include <my_sys.h> #include "sql_show.h" // schema_table_store_record #include "sql_class.h" // THD @@ -285,11 +284,10 @@ QUERY_PROFILE::~QUERY_PROFILE() /** @todo Provide a way to include the full text, as in SHOW PROCESSLIST. */ -void QUERY_PROFILE::set_query_source(char *query_source_arg, - uint query_length_arg) +void QUERY_PROFILE::set_query_source(char *query_source_arg, size_t query_length_arg) { /* Truncate to avoid DoS attacks. */ - uint length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg); + size_t length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg); DBUG_ASSERT(query_source == NULL); /* we don't leak memory */ if (query_source_arg != NULL) diff --git a/sql/sql_profile.h b/sql/sql_profile.h index c96828fc678..cb553d2f757 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -227,7 +227,7 @@ private: QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg); ~QUERY_PROFILE(); - void set_query_source(char *query_source_arg, uint query_length_arg); + void set_query_source(char *query_source_arg, size_t query_length_arg); /* Add a profile status change to the current profile. */ void new_status(const char *status_arg, @@ -275,7 +275,7 @@ public: This must be called exactly once per descrete statement. */ - void set_query_source(char *query_source_arg, uint query_length_arg) + void set_query_source(char *query_source_arg, size_t query_length_arg) { if (unlikely(current)) current->set_query_source(query_source_arg, query_length_arg); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 79968c029ea..b8f0023eefb 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_reload.h" #include "sql_priv.h" #include "mysqld.h" // select_errors @@ -289,7 +289,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, if (tables) { for (TABLE_LIST *t= tables; t; t= t->next_local) - if (!find_table_for_mdl_upgrade(thd, t->db, t->table_name, false)) + if (!find_table_for_mdl_upgrade(thd, t->db.str, t->table_name.str, false)) return 1; } else @@ -412,11 +412,11 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ if (options & REFRESH_GENERIC) { - List_iterator_fast<LEX_STRING> li(thd->lex->view_list); - LEX_STRING *ls; + List_iterator_fast<LEX_CSTRING> li(thd->lex->view_list); + LEX_CSTRING *ls; while ((ls= li++)) { - ST_SCHEMA_TABLE *table= find_schema_table(thd, ls->str); + ST_SCHEMA_TABLE *table= find_schema_table(thd, ls); if (table->reset_table()) result= 1; } @@ -540,8 +540,8 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) { /* Request removal of table from cache. */ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table_list->db, - table_list->table_name, FALSE); + table_list->db.str, + table_list->table_name.str, FALSE); /* Reset ticket to satisfy asserts in open_tables(). */ table_list->mdl_request.ticket= NULL; } @@ -573,7 +573,7 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) if (!(table_list->table->file->ha_table_flags() & HA_CAN_EXPORT)) { my_error(ER_ILLEGAL_HA, MYF(0),table_list->table->file->table_type(), - table_list->db, table_list->table_name); + table_list->db.str, table_list->table_name.str); goto error_reset_bits; } } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 2cba634e17c..fa0fa3f0ea5 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -19,7 +19,7 @@ Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...] */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_rename.h" @@ -30,11 +30,12 @@ #include "sql_base.h" // tdc_remove_table, lock_table_names, #include "sql_handler.h" // mysql_ha_rm_tables #include "sql_statistics.h" +#include "vtmd.h" static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error); -static bool do_rename(THD *thd, TABLE_LIST *ren_table, const char *new_db, - const char *new_table_name, const char *new_table_alias, +static bool do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table_name, const LEX_CSTRING *new_table_alias, bool skip_error); static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list); @@ -103,8 +104,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) Two renames of "log_table TO" w/o rename "TO log_table" in between. */ - my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name, - ren_table->table_name); + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), + ren_table->table_name.str, + ren_table->table_name.str); goto err; } } @@ -116,14 +118,15 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) Attempt to rename a table TO log_table w/o renaming log_table TO some table. */ - my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name, - ren_table->table_name); + my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), + ren_table->table_name.str, + ren_table->table_name.str); goto err; } else { /* save the name of the log table to report an error */ - rename_log_table[log_table_rename]= ren_table->table_name; + rename_log_table[log_table_rename]= ren_table->table_name.str; } } } @@ -215,21 +218,21 @@ static bool do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table, bool skip_error) { - const char *new_alias; + LEX_CSTRING *new_alias; DBUG_ENTER("do_rename_temporary"); - new_alias= (lower_case_table_names == 2) ? new_table->alias : - new_table->table_name; + new_alias= (lower_case_table_names == 2) ? &new_table->alias : + &new_table->table_name; if (is_temporary_table(new_table)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias->str); DBUG_RETURN(1); // This can't be skipped } DBUG_RETURN(thd->rename_temporary_table(ren_table->table, - new_table->db, new_alias)); + &new_table->db, new_alias)); } @@ -254,65 +257,71 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table, */ static bool -do_rename(THD *thd, TABLE_LIST *ren_table, const char *new_db, - const char *new_table_name, const char *new_table_alias, +do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table_name, const LEX_CSTRING *new_table_alias, bool skip_error) { int rc= 1; handlerton *hton; - const char *new_alias, *old_alias; + LEX_CSTRING old_alias, new_alias; DBUG_ENTER("do_rename"); if (lower_case_table_names == 2) { old_alias= ren_table->alias; - new_alias= new_table_alias; + new_alias= *new_table_alias; } else { old_alias= ren_table->table_name; - new_alias= new_table_name; + new_alias= *new_table_name; } - DBUG_ASSERT(new_alias); + DBUG_ASSERT(new_alias.str); - if (ha_table_exists(thd, new_db, new_alias)) + if (ha_table_exists(thd, new_db, &new_alias)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias.str); DBUG_RETURN(1); // This can't be skipped } - if (ha_table_exists(thd, ren_table->db, old_alias, &hton) && hton) + if (ha_table_exists(thd, &ren_table->db, &old_alias, &hton) && hton) { DBUG_ASSERT(!thd->locked_tables_mode); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, - ren_table->db, ren_table->table_name, false); + ren_table->db.str, ren_table->table_name.str, false); if (hton != view_pseudo_hton) { - if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias, - new_db, new_alias, 0))) + if (!(rc= mysql_rename_table(hton, &ren_table->db, &old_alias, + new_db, &new_alias, 0))) { - LEX_CSTRING db_name= { ren_table->db, ren_table->db_length }; - LEX_CSTRING table_name= { ren_table->table_name, - ren_table->table_name_length }; - LEX_CSTRING new_table= { new_alias, strlen(new_alias) }; - LEX_CSTRING new_db_name= { new_db, strlen(new_db)}; - (void) rename_table_in_stat_tables(thd, &db_name, &table_name, - &new_db_name, &new_table); - if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, - old_alias, - ren_table->table_name, + (void) rename_table_in_stat_tables(thd, &ren_table->db, + &ren_table->table_name, + new_db, &new_alias); + VTMD_rename vtmd(*ren_table); + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + rc= vtmd.try_rename(thd, new_db->str, new_alias.str); + if (rc) + goto revert_table_name; + } + if ((rc= Table_triggers_list::change_table_name(thd, &ren_table->db, + &old_alias, + &ren_table->table_name, new_db, - new_alias))) + &new_alias))) { + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + vtmd.revert_rename(thd, new_db->str); +revert_table_name: /* We've succeeded in renaming table's .frm and in updating corresponding handler data, but have failed to update table's triggers appropriately. So let us revert operations on .frm and handler's data and report about failure to rename table. */ - (void) mysql_rename_table(hton, new_db, new_alias, - ren_table->db, old_alias, NO_FK_CHECKS); + (void) mysql_rename_table(hton, new_db, &new_alias, + &ren_table->db, &old_alias, NO_FK_CHECKS); } } } @@ -324,15 +333,15 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const char *new_db, because a view has valid internal db&table names in this case. */ if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE && - strcmp(ren_table->db, new_db)) - my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, new_db); + cmp(&ren_table->db, new_db)) + my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db.str, new_db->str); else - rc= mysql_rename_view(thd, new_db, new_alias, ren_table); + rc= mysql_rename_view(thd, new_db, &new_alias, ren_table); } } else { - my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db, old_alias); + my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db.str, old_alias.str); } if (rc && !skip_error) DBUG_RETURN(1); @@ -378,8 +387,8 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) if (is_temporary_table(ren_table) ? do_rename_temporary(thd, ren_table, new_table, skip_error) : - do_rename(thd, ren_table, new_table->db, new_table->table_name, - new_table->alias, skip_error)) + do_rename(thd, ren_table, &new_table->db, &new_table->table_name, + &new_table->alias, skip_error)) DBUG_RETURN(ren_table); } DBUG_RETURN(0); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 11b9296c865..e1d1190e58f 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" @@ -28,9 +28,9 @@ #include "log_event.h" #include "rpl_filter.h" #include <my_dir.h> -#include "rpl_handler.h" #include "debug_sync.h" -#include "log.h" // get_gtid_list_event +#include "semisync_master.h" +#include "semisync_slave.h" enum enum_gtid_until_state { GTID_UNTIL_NOT_DONE, @@ -160,6 +160,7 @@ struct binlog_send_info { bool clear_initial_log_pos; bool should_stop; + size_t dirlen; binlog_send_info(THD *thd_arg, String *packet_arg, ushort flags_arg, char *lfn) @@ -313,16 +314,43 @@ static int reset_transmit_packet(binlog_send_info *info, ushort flags, packet->length(0); packet->set("\0", 1, &my_charset_bin); - if (RUN_HOOK(binlog_transmit, reserve_header, (info->thd, flags, packet))) + if (info->thd->semi_sync_slave) { - info->error= ER_UNKNOWN_ERROR; - *errmsg= "Failed to run hook 'reserve_header'"; - ret= 1; + if (repl_semisync_master.reserve_sync_header(packet)) + { + info->error= ER_UNKNOWN_ERROR; + *errmsg= "Failed to run hook 'reserve_header'"; + ret= 1; + } } + *ev_offset= packet->length(); return ret; } +int get_user_var_int(const char *name, + long long int *value, int *null_value) +{ + bool null_val; + user_var_entry *entry= + (user_var_entry*) my_hash_search(¤t_thd->user_vars, + (uchar*) name, strlen(name)); + if (!entry) + return 1; + *value= entry->val_int(&null_val); + if (null_value) + *null_value= null_val; + return 0; +} + +inline bool is_semi_sync_slave() +{ + int null_value; + long long val= 0; + get_user_var_int("rpl_semi_sync_slave", &val, &null_value); + return val; +} + static int send_file(THD *thd) { NET* net = &thd->net; @@ -417,7 +445,7 @@ inline void fix_checksum(String *packet, ulong ev_offset) static user_var_entry * get_binlog_checksum_uservar(THD * thd) { - LEX_STRING name= { C_STRING_WITH_LEN("master_binlog_checksum")}; + LEX_CSTRING name= { STRING_WITH_LEN("master_binlog_checksum")}; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -667,7 +695,7 @@ void set_read_error(binlog_send_info *info, int error) static ulonglong get_heartbeat_period(THD * thd) { bool null_value; - LEX_STRING name= { C_STRING_WITH_LEN("master_heartbeat_period")}; + LEX_CSTRING name= { STRING_WITH_LEN("master_heartbeat_period")}; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -686,7 +714,7 @@ static int get_mariadb_slave_capability(THD *thd) { bool null_value; - const LEX_STRING name= { C_STRING_WITH_LEN("mariadb_slave_capability") }; + const LEX_CSTRING name= { STRING_WITH_LEN("mariadb_slave_capability") }; const user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -707,7 +735,7 @@ get_slave_connect_state(THD *thd, String *out_str) { bool null_value; - const LEX_STRING name= { C_STRING_WITH_LEN("slave_connect_state") }; + const LEX_CSTRING name= { STRING_WITH_LEN("slave_connect_state") }; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -720,7 +748,7 @@ get_slave_gtid_strict_mode(THD *thd) { bool null_value; - const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_strict_mode") }; + const LEX_CSTRING name= { STRING_WITH_LEN("slave_gtid_strict_mode") }; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -733,7 +761,7 @@ get_slave_gtid_ignore_duplicates(THD *thd) { bool null_value; - const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_ignore_duplicates") }; + const LEX_CSTRING name= { STRING_WITH_LEN("slave_gtid_ignore_duplicates") }; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -754,7 +782,7 @@ get_slave_until_gtid(THD *thd, String *out_str) { bool null_value; - const LEX_STRING name= { C_STRING_WITH_LEN("slave_until_gtid") }; + const LEX_CSTRING name= { STRING_WITH_LEN("slave_until_gtid") }; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str, name.length); @@ -800,8 +828,8 @@ static int send_heartbeat_event(binlog_send_info *info, char* p= coord->file_name + dirname_length(coord->file_name); - uint ident_len = strlen(p); - ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + + size_t ident_len = strlen(p); + size_t event_len = ident_len + LOG_EVENT_HEADER_LEN + (do_checksum ? BINLOG_CHECKSUM_LEN : 0); int4store(header + SERVER_ID_OFFSET, global_system_variables.server_id); int4store(header + EVENT_LEN_OFFSET, event_len); @@ -1607,6 +1635,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, enum enum_binlog_checksum_alg current_checksum_alg= info->current_checksum_alg; slave_connection_state *gtid_state= &info->gtid_state; slave_connection_state *until_gtid_state= info->until_gtid_state; + bool need_sync= false; if (event_type == GTID_LIST_EVENT && info->using_gtid_state && until_gtid_state) @@ -1914,8 +1943,10 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, THD_STAGE_INFO(info->thd, stage_sending_binlog_event_to_slave); pos= my_b_tell(log); - if (RUN_HOOK(binlog_transmit, before_send_event, - (info->thd, info->flags, packet, info->log_file_name, pos))) + if (repl_semisync_master.update_sync_header(info->thd, + (uchar*) packet->c_ptr(), + info->log_file_name + info->dirlen, + pos, &need_sync)) { info->error= ER_UNKNOWN_ERROR; return "run 'before_send_event' hook failed"; @@ -1937,8 +1968,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, } } - if (RUN_HOOK(binlog_transmit, after_send_event, - (info->thd, info->flags, packet))) + if (need_sync && repl_semisync_master.flush_net(info->thd, packet->c_ptr())) { info->error= ER_UNKNOWN_ERROR; return "Failed to run hook 'after_send_event'"; @@ -2368,7 +2398,7 @@ static int wait_new_events(binlog_send_info *info, /* in */ PSI_stage_info old_stage; mysql_bin_log.lock_binlog_end_pos(); - info->thd->ENTER_COND(mysql_bin_log.get_log_cond(), + info->thd->ENTER_COND(mysql_bin_log.get_bin_log_cond(), mysql_bin_log.get_binlog_end_pos_lock(), &stage_master_has_sent_all_binlog_to_slave, &old_stage); @@ -2679,7 +2709,7 @@ static int send_one_binlog_file(binlog_send_info *info, /** end of file or error */ return (int)end_pos; } - + info->dirlen= dirname_length(info->log_file_name); /** * send events from current position up to end_pos */ @@ -2701,6 +2731,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, binlog_send_info infoobj(thd, packet, flags, linfo.log_file_name); binlog_send_info *info= &infoobj; + bool has_transmit_started= false; int old_max_allowed_packet= thd->variables.max_allowed_packet; thd->variables.max_allowed_packet= MAX_MAX_ALLOWED_PACKET; @@ -2713,11 +2744,11 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, if (init_binlog_sender(info, &linfo, log_ident, &pos)) goto err; - /* - run hook first when all check has been made that slave seems to - be requesting a reasonable position. i.e when transmit actually starts - */ - if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos))) + has_transmit_started= true; + + /* Check if the dump thread is created by a slave with semisync enabled. */ + thd->semi_sync_slave = is_semi_sync_slave(); + if (repl_semisync_master.dump_start(thd, log_ident, pos)) { info->errmsg= "Failed to run hook 'transmit_start'"; info->error= ER_UNKNOWN_ERROR; @@ -2839,7 +2870,10 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, err: THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination); - RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags)); + if (has_transmit_started) + { + repl_semisync_master.dump_end(thd); + } if (info->thd->killed == KILL_SLAVE_SAME_ID) { @@ -3305,7 +3339,8 @@ int reset_slave(THD *thd, Master_info* mi) else if (global_system_variables.log_warnings > 1) sql_print_information("Deleted Master_info file '%s'.", fname); - RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi)); + if (rpl_semi_sync_slave_enabled) + repl_semisync_slave.reset_slave(mi); err: mi->unlock_slave_threads(); if (error) @@ -3374,7 +3409,7 @@ static bool get_string_parameter(char *to, const char *from, size_t length, if (from) // Empty paramaters allowed { size_t from_length= strlen(from); - uint from_numchars= cs->cset->numchars(cs, from, from + from_length); + size_t from_numchars= cs->cset->numchars(cs, from, from + from_length); if (from_numchars > length / cs->mbmaxlen) { my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, @@ -3808,11 +3843,13 @@ int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len, return 1; } - if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len, - next_log_number)) - return 1; - RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */)); - return 0; + bool ret= 0; + /* Temporarily disable master semisync before reseting master. */ + repl_semisync_master.before_reset_master(); + ret= mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len, + next_log_number); + repl_semisync_master.after_reset_master(); + return ret; } @@ -3929,7 +3966,7 @@ bool mysql_show_binlog_events(THD* thd) my_off_t scan_pos = BIN_LOG_HEADER_SIZE; while (scan_pos < pos) { - ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event, + ev= Log_event::read_log_event(&log, description_event, opt_master_verify_checksum); scan_pos = my_b_tell(&log); if (ev == NULL || !ev->is_valid()) @@ -3963,7 +4000,7 @@ bool mysql_show_binlog_events(THD* thd) my_b_seek(&log, pos); for (event_count = 0; - (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0, + (ev = Log_event::read_log_event(&log, description_event, opt_master_verify_checksum)); ) { @@ -4087,7 +4124,7 @@ bool show_binlog_info(THD* thd) { LOG_INFO li; mysql_bin_log.get_current_log(&li); - int dir_len = dirname_length(li.log_file_name); + size_t dir_len = dirname_length(li.log_file_name); protocol->store(li.log_file_name + dir_len, &my_charset_bin); protocol->store((ulonglong) li.pos); protocol->store(binlog_filter->get_do_db()); @@ -4129,8 +4166,8 @@ bool show_binlogs(THD* thd) File file; char fname[FN_REFLEN]; List<Item> field_list; - uint length; - int cur_dir_len; + size_t length; + size_t cur_dir_len; Protocol *protocol= thd->protocol; DBUG_ENTER("show_binlogs"); @@ -4160,7 +4197,7 @@ bool show_binlogs(THD* thd) /* The file ends with EOF or empty line */ while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1) { - int dir_len; + size_t dir_len; ulonglong file_length= 0; // Length if open fails fname[--length] = '\0'; // remove the newline @@ -4229,7 +4266,7 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) lf_info->last_pos_in_file= my_b_get_pos_in_file(file); if (lf_info->wrote_create_file) { - Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer, + Append_block_log_event a(lf_info->thd, lf_info->thd->db.str, buffer, MY_MIN(block_len, max_event_size), lf_info->log_delayed); if (mysql_bin_log.write(&a)) @@ -4237,7 +4274,7 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) } else { - Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db, + Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db.str, buffer, MY_MIN(block_len, max_event_size), lf_info->log_delayed); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6fd161f7160..a58c34cc2fa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -29,7 +29,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_select.h" @@ -50,17 +50,20 @@ #include "filesort.h" // filesort_free_buffers #include "sql_union.h" // mysql_union #include "opt_subselect.h" -#include "log_slow.h" #include "sql_derived.h" #include "sql_statistics.h" #include "sql_cte.h" #include "sql_window.h" +#include "tztime.h" #include "debug_sync.h" // DEBUG_SYNC #include <m_ctype.h> #include <my_bit.h> #include <hash.h> #include <ft_global.h> +#include "sys_vars_shared.h" +#include "sp_head.h" +#include "sp_rcontext.h" /* A key part number that means we're using a fulltext scan. @@ -81,9 +84,11 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", "index_merge", "hash_ALL", "hash_range", "hash_index", "hash_index_merge" }; +LEX_CSTRING group_key= {STRING_WITH_LEN("group_key")}; +LEX_CSTRING distinct_key= {STRING_WITH_LEN("distinct_key")}; + struct st_sargable_param; -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); static bool make_join_statistics(JOIN *join, List<TABLE_LIST> &leaves, DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, @@ -91,8 +96,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables, COND *conds, table_map table_map, SELECT_LEX *select_lex, SARGABLE_PARAM **sargables); -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, @@ -670,6 +673,398 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(res); } +bool vers_select_conds_t::init_from_sysvar(THD *thd) +{ + vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp; + type= (vers_system_time_t) in.type; + unit_start= VERS_TIMESTAMP; + from_query= false; + if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL) + { + DBUG_ASSERT(type == SYSTEM_TIME_AS_OF); + start= new (thd->mem_root) + Item_datetime_literal(thd, &in.ltime, TIME_SECOND_PART_DIGITS); + if (!start) + return true; + } + else + start= NULL; + end= NULL; + return false; +} + +void vers_select_conds_t::print(String *str, enum_query_type query_type) +{ + const static LEX_CSTRING unit_type[]= + { + { STRING_WITH_LEN("") }, + { STRING_WITH_LEN("TIMESTAMP ") }, + { STRING_WITH_LEN("TRANACTION ") } + }; + switch (type) { + case SYSTEM_TIME_UNSPECIFIED: + break; + case SYSTEM_TIME_AS_OF: + str->append(STRING_WITH_LEN(" FOR SYSTEM_TIME AS OF ")); + str->append(unit_type + unit_start); + start->print(str, query_type); + break; + case SYSTEM_TIME_FROM_TO: + str->append(STRING_WITH_LEN(" FOR SYSTEM_TIME FROM ")); + str->append(unit_type + unit_start); + start->print(str, query_type); + str->append(STRING_WITH_LEN(" TO ")); + str->append(unit_type + unit_end); + end->print(str, query_type); + break; + case SYSTEM_TIME_BETWEEN: + str->append(STRING_WITH_LEN(" FOR SYSTEM_TIME BETWEEN ")); + str->append(unit_type + unit_start); + start->print(str, query_type); + str->append(STRING_WITH_LEN(" AND ")); + str->append(unit_type + unit_end); + end->print(str, query_type); + break; + case SYSTEM_TIME_BEFORE: + DBUG_ASSERT(0); + break; + case SYSTEM_TIME_ALL: + str->append(" FOR SYSTEM_TIME ALL"); + break; + } +} + +int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables, COND **where_expr) +{ + DBUG_ENTER("SELECT_LEX::vers_setup_cond"); +#define newx new (thd->mem_root) + + TABLE_LIST *table; + + if (!thd->stmt_arena->is_conventional() && + !thd->stmt_arena->is_stmt_prepare() && !thd->stmt_arena->is_sp_execute()) + { + // statement is already prepared + DBUG_RETURN(0); + } + + if (thd->lex->is_view_context_analysis()) + DBUG_RETURN(0); + + if (!versioned_tables) + { + for (table= tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + versioned_tables++; + else if (table->vers_conditions.user_defined() && + (table->is_non_derived() || !table->vers_conditions.used)) + { + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table->alias.str); + DBUG_RETURN(-1); + } + } + } + + if (versioned_tables == 0) + DBUG_RETURN(0); + + /* For prepared statements we create items on statement arena, + because they must outlive execution phase for multiple executions. */ + Query_arena_stmt on_stmt_arena(thd); + + // find outer system_time + SELECT_LEX *outer_slex= outer_select(); + TABLE_LIST* outer_table= NULL; + + if (outer_slex) + { + TABLE_LIST* derived= master_unit()->derived; + // inner SELECT may not be a derived table (derived == NULL) + while (derived && outer_slex && !derived->vers_conditions) + { + derived= outer_slex->master_unit()->derived; + outer_slex= outer_slex->outer_select(); + } + if (derived && outer_slex) + { + DBUG_ASSERT(derived->vers_conditions); + outer_table= derived; + } + } + + COND** dst_cond= where_expr; + COND* vers_cond= NULL; + + for (table= tables; table; table= table->next_local) + { + if (!table->table || !table->table->versioned()) + continue; + + vers_select_conds_t &vers_conditions= table->vers_conditions; + +#ifdef WITH_PARTITION_STORAGE_ENGINE + /* + if the history is stored in partitions, then partitions + themselves are not versioned + */ + if (table->partition_names && table->table->part_info->vers_info) + { + if (vers_conditions) + { +#define PART_VERS_ERR_MSG "%s PARTITION (%s)" + char buf[NAME_LEN*2 + sizeof(PART_VERS_ERR_MSG)]; + my_snprintf(buf, sizeof(buf), PART_VERS_ERR_MSG, table->alias.str, + table->partition_names->head()->c_ptr()); + my_error(ER_VERS_NOT_VERSIONED, MYF(0), buf); + DBUG_RETURN(-1); + } + else + vers_conditions.init(SYSTEM_TIME_ALL); + } +#endif + + if (outer_table && !vers_conditions) + { + // propagate system_time from nearest outer SELECT_LEX + vers_conditions= outer_table->vers_conditions; + outer_table->vers_conditions.used= true; + } + + // propagate system_time from sysvar + if (!vers_conditions) + { + if (vers_conditions.init_from_sysvar(thd)) + DBUG_RETURN(-1); + } + + if (vers_conditions) + { + if (vers_conditions == SYSTEM_TIME_ALL) + continue; + + lock_type= TL_READ; // ignore TL_WRITE, history is immutable anyway + } + + if (table->on_expr) + { + dst_cond= &table->on_expr; + } + + const LEX_CSTRING *fstart= &table->table->vers_start_field()->field_name; + const LEX_CSTRING *fend= &table->table->vers_end_field()->field_name; + + Item *row_start= + newx Item_field(thd, &this->context, table->db.str, table->alias.str, fstart); + Item *row_end= + newx Item_field(thd, &this->context, table->db.str, table->alias.str, fend); + + bool tmp_from_ib= + table->table->s->table_category == TABLE_CATEGORY_TEMPORARY && + table->table->vers_start_field()->type() == MYSQL_TYPE_LONGLONG; + bool timestamps_only= table->table->versioned(VERS_TIMESTAMP) && !tmp_from_ib; + + if (vers_conditions) + { + /* TODO: do resolve fix_length_and_dec(), fix_fields(). This requires + storing vers_conditions as Item and make some magic related to + vers_system_time_t/VERS_TRX_ID at stage of fix_fields() + (this is large refactoring). */ + vers_conditions.resolve_units(timestamps_only); + if (timestamps_only && (vers_conditions.unit_start == VERS_TRX_ID || + vers_conditions.unit_end == VERS_TRX_ID)) + { + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name.str); + DBUG_RETURN(-1); + } + } + + Item *cond1= 0, *cond2= 0, *curr= 0; + // Temporary tables of can be created from INNODB tables and thus will + // have uint64 type of sys_trx_(start|end) field. + // They need special handling. + TABLE *t= table->table; + if (tmp_from_ib || t->versioned(VERS_TIMESTAMP) || + thd->variables.vers_innodb_algorithm_simple) + { + if (vers_conditions) + { + if (vers_conditions.start) + { + if (!vers_conditions.unit_start) + vers_conditions.unit_start= t->s->versioned; + switch (vers_conditions.unit_start) + { + case VERS_TIMESTAMP: + { + vers_conditions.start= newx Item_datetime_from_unixtime_typecast( + thd, vers_conditions.start, 6); + break; + } + case VERS_TRX_ID: + { + vers_conditions.start= newx Item_longlong_typecast( + thd, vers_conditions.start); + break; + } + default: + DBUG_ASSERT(0); + break; + } + } + + if (vers_conditions.end) + { + if (!vers_conditions.unit_end) + vers_conditions.unit_end= t->s->versioned; + switch (vers_conditions.unit_end) + { + case VERS_TIMESTAMP: + { + vers_conditions.end= newx Item_datetime_from_unixtime_typecast( + thd, vers_conditions.end, 6); + break; + } + case VERS_TRX_ID: + { + vers_conditions.end= newx Item_longlong_typecast( + thd, vers_conditions.end); + break; + } + default: + DBUG_ASSERT(0); + break; + } + } + } + switch (vers_conditions.type) + { + case SYSTEM_TIME_UNSPECIFIED: + if (t->vers_start_field()->real_type() != MYSQL_TYPE_LONGLONG) + { + MYSQL_TIME max_time; + thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); + max_time.second_part= TIME_MAX_SECOND_PART; + curr= newx Item_datetime_literal(thd, &max_time, + TIME_SECOND_PART_DIGITS); + cond1= newx Item_func_eq(thd, row_end, curr); + } + else + { + curr= newx Item_int(thd, ULONGLONG_MAX); + cond1= newx Item_func_eq(thd, row_end, curr); + } + cond1= or_items(thd, cond1, newx Item_func_isnull(thd, row_end)); + break; + case SYSTEM_TIME_AS_OF: + cond1= newx Item_func_le(thd, row_start, + vers_conditions.start); + cond2= newx Item_func_gt(thd, row_end, + vers_conditions.start); + break; + case SYSTEM_TIME_FROM_TO: + cond1= newx Item_func_lt(thd, row_start, + vers_conditions.end); + cond2= newx Item_func_ge(thd, row_end, + vers_conditions.start); + break; + case SYSTEM_TIME_BETWEEN: + cond1= newx Item_func_le(thd, row_start, + vers_conditions.end); + cond2= newx Item_func_ge(thd, row_end, + vers_conditions.start); + break; + case SYSTEM_TIME_BEFORE: + cond1= newx Item_func_lt(thd, row_end, + vers_conditions.start); + break; + default: + DBUG_ASSERT(0); + } + } + else + { + DBUG_ASSERT(table->table->s && table->table->s->db_plugin); + + Item *trx_id0, *trx_id1; + + switch (vers_conditions.type) + { + case SYSTEM_TIME_UNSPECIFIED: + curr= newx Item_int(thd, ULONGLONG_MAX); + cond1= newx Item_func_eq(thd, row_end, curr); + break; + case SYSTEM_TIME_AS_OF: + trx_id0= vers_conditions.unit_start == VERS_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.start, TR_table::FLD_TRX_ID) : + vers_conditions.start; + cond1= newx Item_func_vtq_trx_sees_eq(thd, trx_id0, row_start); + cond2= newx Item_func_vtq_trx_sees(thd, row_end, trx_id0); + break; + case SYSTEM_TIME_FROM_TO: + case SYSTEM_TIME_BETWEEN: + trx_id0= vers_conditions.unit_start == VERS_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.start, TR_table::FLD_TRX_ID, true) : + vers_conditions.start; + trx_id1= vers_conditions.unit_end == VERS_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.end, TR_table::FLD_TRX_ID, false) : + vers_conditions.end; + cond1= vers_conditions.type == SYSTEM_TIME_FROM_TO ? + newx Item_func_vtq_trx_sees(thd, trx_id1, row_start) : + newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); + cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); + break; + case SYSTEM_TIME_BEFORE: + trx_id0= vers_conditions.unit_start == VERS_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.start, TR_table::FLD_TRX_ID) : + vers_conditions.start; + cond1= newx Item_func_lt(thd, row_end, trx_id0); + break; + default: + DBUG_ASSERT(0); + } + } + vers_conditions.type= SYSTEM_TIME_ALL; + + if (cond1) + { + vers_cond= and_items(thd, + vers_cond, + and_items(thd, + cond2, + cond1)); + if (table->is_view_or_derived()) + vers_cond= or_items(thd, vers_cond, newx Item_func_isnull(thd, row_end)); + } + } // for (table= tables; ...) + + if (vers_cond) + { + COND *all_cond= and_items(thd, *dst_cond, vers_cond); + bool from_where= dst_cond == where_expr; + if (on_stmt_arena.arena_replaced()) + *dst_cond= all_cond; + else + thd->change_item_tree(dst_cond, all_cond); + + if (from_where) + { + this->where= *dst_cond; + this->where->top_level_item(); + } + + // Invalidate current SP [#52, #422] + if (thd->spcont) + { + DBUG_ASSERT(thd->spcont->m_sp); + thd->spcont->m_sp->set_sp_cache_version(0); + } + } + + DBUG_RETURN(0); +#undef newx +} + /***************************************************************************** Check fields, find best join, do the select and output fields. mysql_select assumes that all tables are already opened @@ -717,7 +1112,7 @@ JOIN::prepare(TABLE_LIST *tables_init, dbug_print_item(conds); if (select_lex->handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(1); + DBUG_RETURN(-1); thd->lex->current_select->context_analysis_place= NO_MATTER; thd->lex->current_select->is_item_list_lookup= 1; @@ -748,11 +1143,15 @@ JOIN::prepare(TABLE_LIST *tables_init, { remove_redundant_subquery_clauses(select_lex); } - + + /* System Versioning: handle FOR SYSTEM_TIME clause. */ + if (select_lex->vers_setup_conds(thd, tables_list, &conds) < 0) + DBUG_RETURN(-1); + /* TRUE if the SELECT list mixes elements with and without grouping, and there is no GROUP BY clause. Mixing non-aggregated fields with - aggregate functions in the SELECT list is a MySQL exptenstion that + aggregate functions in the SELECT list is a MySQL extenstion that is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set. */ mixed_implicit_grouping= false; @@ -974,6 +1373,8 @@ JOIN::prepare(TABLE_LIST *tables_init, (*ord->item)->field_type() == MYSQL_TYPE_BIT) { Item_field *field= new (thd->mem_root) Item_field(thd, *(Item_field**)ord->item); + if (!field) + DBUG_RETURN(-1); int el= all_fields.elements; ref_ptrs[el]= field; all_fields.push_front(field, thd->mem_root); @@ -1106,14 +1507,16 @@ err: DBUG_RETURN(res); /* purecov: inspected */ } -void JOIN::build_explain() + +bool JOIN::build_explain() { create_explain_query_if_not_exists(thd->lex, thd->mem_root); have_query_plan= QEP_AVAILABLE; - save_explain_data(thd->lex->explain, false /* can overwrite */, - need_tmp, - !skip_sort_order && !no_order && (order || group_list), - select_distinct); + if (save_explain_data(thd->lex->explain, false /* can overwrite */, + need_tmp, + !skip_sort_order && !no_order && (order || group_list), + select_distinct)) + return 1; uint select_nr= select_lex->select_number; JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt(); for (uint i= 0; i < aggr_tables; i++, curr_tab++) @@ -1131,8 +1534,10 @@ void JOIN::build_explain() get_using_temporary_read_tracker(); } } + return 0; } + int JOIN::optimize() { int res= 0; @@ -1145,14 +1550,13 @@ int JOIN::optimize() if (optimization_state != JOIN::NOT_OPTIMIZED) return FALSE; optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS; - is_for_splittable_grouping_derived= false; res= optimize_inner(); } if (!with_two_phase_optimization || init_state == JOIN::OPTIMIZATION_PHASE_1_DONE) { if (!res && have_query_plan != QEP_DELETED) - build_explain(); + res= build_explain(); optimization_state= JOIN::OPTIMIZATION_DONE; } return res; @@ -1229,6 +1633,11 @@ JOIN::optimize_inner() DBUG_RETURN(TRUE); table_count= select_lex->leaf_tables.elements; } + + if (select_lex->first_cond_optimization && + transform_in_predicates_into_in_subq(thd)) + DBUG_RETURN(1); + // Update used tables after all handling derived table procedures select_lex->update_used_tables(); @@ -1586,9 +1995,6 @@ int JOIN::optimize_stage2() if (subq_exit_fl) goto setup_subq_exit; - if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) - DBUG_RETURN(1); - if (thd->check_killed()) DBUG_RETURN(1); @@ -1596,6 +2002,9 @@ int JOIN::optimize_stage2() if (get_best_combination()) DBUG_RETURN(1); + if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) drop_unused_derived_keys(); @@ -1741,6 +2150,9 @@ int JOIN::optimize_stage2() { ref_item= substitute_for_best_equal_field(thd, tab, ref_item, equals, map2table); + if (thd->is_fatal_error) + DBUG_RETURN(1); + if (first_inner) { equals= first_inner->cond_equal; @@ -2053,7 +2465,8 @@ int JOIN::optimize_stage2() /* Perform FULLTEXT search before all regular searches */ if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, MY_TEST(order)); + if (init_ftfuncs(thd, select_lex, MY_TEST(order))) + DBUG_RETURN(1); /* It's necessary to check const part of HAVING cond as @@ -2414,20 +2827,22 @@ bool JOIN::make_aggr_tables_info() /* All optimization is done. Check if we can use the storage engines - group by handler to evaluate the group by + group by handler to evaluate the group by. + Some storage engines, like spider can also do joins, group by and + distinct in the engine, so we do this for all queries, not only + GROUP BY queries. */ - if (tables_list && (tmp_table_param.sum_func_count || group_list) && - !procedure) + if (tables_list && !procedure) { /* At the moment we only support push down for queries where all tables are in the same storage engine */ TABLE_LIST *tbl= tables_list; - handlerton *ht= tbl && tbl->table ? tbl->table->file->ht : 0; + handlerton *ht= tbl && tbl->table ? tbl->table->file->partition_ht() : 0; for (tbl= tbl->next_local; ht && tbl; tbl= tbl->next_local) { - if (!tbl->table || tbl->table->file->ht != ht) + if (!tbl->table || tbl->table->file->partition_ht() != ht) ht= 0; } @@ -2440,7 +2855,8 @@ bool JOIN::make_aggr_tables_info() if (gbh) { - pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh); + if (!(pushdown_query= new (thd->mem_root) Pushdown_query(select_lex, gbh))) + DBUG_RETURN(1); /* We must store rows in the tmp table if we need to do an ORDER BY or DISTINCT and the storage handler can't handle it. @@ -2457,17 +2873,19 @@ bool JOIN::make_aggr_tables_info() curr_tab->ref.key= -1; curr_tab->join= this; - curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param); + if (!(curr_tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param))) + DBUG_RETURN(1); TABLE* table= create_tmp_table(thd, curr_tab->tmp_table_param, all_fields, NULL, query.distinct, TRUE, select_options, HA_POS_ERROR, - "", !need_tmp, + &empty_clex_str, !need_tmp, query.order_by || query.group_by); if (!table) DBUG_RETURN(1); - curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab); + if (!(curr_tab->aggr= new (thd->mem_root) AGGR_OP(curr_tab))) + DBUG_RETURN(1); curr_tab->aggr->set_write_func(::end_send); curr_tab->table= table; /* @@ -2915,7 +3333,8 @@ bool JOIN::make_aggr_tables_info() curr_tab= join_tab + exec_join_tab_cnt() + aggr_tables - 1; if (select_lex->window_funcs.elements) { - curr_tab->window_funcs_step= new Window_funcs_computation; + if (!(curr_tab->window_funcs_step= new Window_funcs_computation)) + DBUG_RETURN(true); if (curr_tab->window_funcs_step->setup(thd, &select_lex->window_funcs, curr_tab)) DBUG_RETURN(true); @@ -2957,12 +3376,13 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields, !select_lex->with_sum_func) ? select_limit : HA_POS_ERROR; - tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param); + if (!(tab->tmp_table_param= new TMP_TABLE_PARAM(tmp_table_param))) + DBUG_RETURN(true); tab->tmp_table_param->skip_create_table= true; TABLE* table= create_tmp_table(thd, tab->tmp_table_param, *table_fields, table_group, distinct, save_sum_fields, select_options, table_rows_limit, - "", true, keep_row_order); + &empty_clex_str, true, keep_row_order); if (!table) DBUG_RETURN(true); tmp_table_param.using_outer_summary_function= @@ -2971,8 +3391,7 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields, DBUG_ASSERT(tab > tab->join->join_tab || !top_join_tab_count || !tables_list); if (tab > join_tab) (tab - 1)->next_select= sub_select_postjoin_aggr; - tab->aggr= new (thd->mem_root) AGGR_OP(tab); - if (!tab->aggr) + if (!(tab->aggr= new (thd->mem_root) AGGR_OP(tab))) goto err; tab->table= table; table->reginfo.join_tab= tab; @@ -3124,33 +3543,42 @@ bool JOIN::setup_subquery_caches() select_lex->expr_cache_may_be_used[IN_ON] || select_lex->expr_cache_may_be_used[NO_MATTER]) { - if (conds) - conds= conds->transform(thd, &Item::expr_cache_insert_transformer, - NULL); JOIN_TAB *tab; + if (conds && + !(conds= conds->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { - if (tab->select_cond) - tab->select_cond= - tab->select_cond->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (tab->select_cond && + !(tab->select_cond= + tab->select_cond->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); if (tab->cache_select && tab->cache_select->cond) - tab->cache_select->cond= - tab->cache_select-> - cond->transform(thd, &Item::expr_cache_insert_transformer, - NULL); - + if (!(tab->cache_select->cond= + tab->cache_select-> + cond->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } - if (having) - having= having->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (having && + !(having= having->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); + if (tmp_having) { DBUG_ASSERT(having == NULL); - tmp_having= tmp_having->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(tmp_having= + tmp_having->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } if (select_lex->expr_cache_may_be_used[SELECT_LIST] || @@ -3161,9 +3589,11 @@ bool JOIN::setup_subquery_caches() Item *item; while ((item= li++)) { - Item *new_item= - item->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + Item *new_item; + if (!(new_item= + item->transform(thd, &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); if (new_item != item) { thd->change_item_tree(li.ref(), new_item); @@ -3171,18 +3601,22 @@ bool JOIN::setup_subquery_caches() } for (ORDER *tmp_group= group_list; tmp_group ; tmp_group= tmp_group->next) { - *tmp_group->item= - (*tmp_group->item)->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(*tmp_group->item= + (*tmp_group->item)->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } if (select_lex->expr_cache_may_be_used[NO_MATTER]) { for (ORDER *ord= order; ord; ord= ord->next) { - *ord->item= - (*ord->item)->transform(thd, &Item::expr_cache_insert_transformer, - NULL); + if (!(*ord->item= + (*ord->item)->transform(thd, + &Item::expr_cache_insert_transformer, + NULL))) + DBUG_RETURN(TRUE); } } DBUG_RETURN(FALSE); @@ -3310,7 +3744,8 @@ JOIN::reinit() } if (!(select_options & SELECT_DESCRIBE)) - init_ftfuncs(thd, select_lex, MY_TEST(order)); + if (init_ftfuncs(thd, select_lex, MY_TEST(order))) + DBUG_RETURN(1); DBUG_RETURN(0); } @@ -3352,7 +3787,14 @@ err: } -void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, +/** + @retval + 0 ok + 1 error +*/ + + +bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite, bool need_tmp_table, bool need_order, bool distinct) { @@ -3382,9 +3824,8 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, /* It's a degenerate join */ message= zero_result_cause ? zero_result_cause : "No tables used"; } - save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order, - distinct, message); - return; + return save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order, + distinct, message); } /* @@ -3404,11 +3845,13 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite, { if (join_tab[i].filesort) { - join_tab[i].filesort->tracker= - new Filesort_tracker(thd->lex->analyze_stmt); + if (!(join_tab[i].filesort->tracker= + new Filesort_tracker(thd->lex->analyze_stmt))) + return 1; } } } + return 0; } @@ -3626,6 +4069,7 @@ void JOIN::exec_inner() result->send_result_set_metadata( procedure ? procedure_fields_list : *fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + error= do_select(this, procedure); /* Accumulate the counts from all join iterations of all join parts. */ thd->inc_examined_row_count(join_examined_rows); @@ -3675,7 +4119,11 @@ JOIN::destroy() cleanup_item_list(tmp_all_fields1); cleanup_item_list(tmp_all_fields3); destroy_sj_tmp_tables(this); - delete_dynamic(&keyuse); + delete_dynamic(&keyuse); + if (save_qep) + delete(save_qep); + if (ext_keyuses_for_splitting) + delete(ext_keyuses_for_splitting); delete procedure; DBUG_RETURN(error); } @@ -3833,7 +4281,7 @@ err: if (free_join) { THD_STAGE_INFO(thd, stage_end); - err|= select_lex->cleanup(); + err|= (int)(select_lex->cleanup()); DBUG_RETURN(err || thd->is_error()); } DBUG_RETURN(join->error ? join->error: err); @@ -4205,6 +4653,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, keyuse->val->is_null() && keyuse->null_rejecting) { s->type= JT_CONST; + s->table->const_table= 1; mark_as_null_row(table); found_const_table_map|= table->map; join->const_table_map|= table->map; @@ -4310,6 +4759,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, s->type= JT_CONST; join->const_table_map|=table->map; set_position(join,const_count++,s,start_keyuse); + /* create_ref_for_key will set s->table->const_table */ if (create_ref_for_key(join, s, start_keyuse, FALSE, found_const_table_map)) goto error; @@ -4433,6 +4883,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, s->scan_time(); } + if (s->table->is_splittable()) + s->add_keyuses_for_splitting(); + /* Set a max range of how many seeks we can expect when using keys This is can't be to high as otherwise we are likely to use @@ -4515,12 +4968,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, 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; - s->type= JT_CONST; mark_as_null_row(s->table); // All fields are NULL } } @@ -4600,9 +5053,6 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->choose_subquery_plan(all_table_map & ~join->const_table_map)) goto error; - if (join->improve_chosen_plan(join->thd)) - goto error; - DEBUG_SYNC(join->thd, "inside_make_join_statistics"); DBUG_RETURN(0); @@ -4632,23 +5082,6 @@ error: keyuse Pointer to possible keys *****************************************************************************/ -/// Used when finding key fields -struct KEY_FIELD { - Field *field; - Item_bool_func *cond; - Item *val; ///< May be empty if diff constant - uint level; - uint optimize; - bool eq_func; - /** - If true, the condition this struct represents will not be satisfied - when val IS NULL. - */ - bool null_rejecting; - bool *cond_guard; /* See KEYUSE::cond_guard */ - uint sj_pred_no; /* See KEYUSE::sj_pred_no */ -}; - /** Merge new key definitions to old ones, remove those not used in both. @@ -5299,7 +5732,7 @@ Item_func_ne::add_key_fields(JOIN *join, KEY_FIELD **key_fields, /* QQ: perhaps test for !is_local_field(args[1]) is not really needed here. Other comparison functions, e.g. Item_func_le, Item_func_gt, etc, - do not have this test. See Item_bool_func2::add_key_field_optimize_op(). + do not have this test. See Item_bool_func2::add_key_fieldoptimize_op(). Check with the optimizer team. */ if (is_local_field(args[0]) && !is_local_field(args[1])) @@ -5482,6 +5915,7 @@ add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field, keyuse.null_rejecting= key_field->null_rejecting; keyuse.cond_guard= key_field->cond_guard; keyuse.sj_pred_no= key_field->sj_pred_no; + keyuse.validity_ref= 0; return (insert_dynamic(keyuse_array,(uchar*) &keyuse)); } @@ -5527,7 +5961,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) key_field->val->used_tables()) { if (!field->can_optimize_hash_join(key_field->cond, key_field->val)) - return false; + return false; + if (form->is_splittable()) + form->add_splitting_info_for_key_field(key_field); /* If a key use is extracted from an equi-join predicate then it is added not only as a key use for every index whose component can @@ -5541,7 +5977,6 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) return FALSE; } - static bool add_ft_keys(DYNAMIC_ARRAY *keyuse_array, JOIN_TAB *stat,COND *cond,table_map usable_tables) @@ -5603,6 +6038,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, keyuse.optimize= 0; keyuse.keypart_map= 0; keyuse.sj_pred_no= UINT_MAX; + keyuse.validity_ref= 0; return insert_dynamic(keyuse_array,(uchar*) &keyuse); } @@ -5890,8 +6326,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts) +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; uint found_eq_constant, i; @@ -5959,7 +6395,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, Update some values in keyuse for faster choose_plan() loop. */ -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) { KEYUSE *end,*keyuse= dynamic_element(keyuse_array, 0, KEYUSE*); @@ -6000,7 +6436,6 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) } - /** Check for the presence of AGGFN(DISTINCT a) queries that may be subject to loose index scan. @@ -6308,6 +6743,7 @@ best_access_path(JOIN *join, bool best_uses_jbuf= FALSE; MY_BITMAP *eq_join_set= &s->table->eq_join_set; KEYUSE *hj_start_key= 0; + SplM_plan_info *spl_plan= 0; disable_jbuf= disable_jbuf || idx == join->const_tables; @@ -6317,7 +6753,10 @@ best_access_path(JOIN *join, bitmap_clear_all(eq_join_set); loose_scan_opt.init(join, s, remaining_tables); - + + if (s->table->is_splittable()) + spl_plan= s->choose_best_splitting(record_count, remaining_tables); + if (s->keyuse) { /* Use key if possible */ KEYUSE *keyuse; @@ -6367,7 +6806,7 @@ best_access_path(JOIN *join, loose_scan_opt.next_ref_key(); DBUG_PRINT("info", ("Considering ref access on key %s", - keyuse->table->key_info[keyuse->key].name)); + keyuse->table->key_info[keyuse->key].name.str)); do /* For each keypart */ { @@ -6382,6 +6821,7 @@ best_access_path(JOIN *join, 2. we won't get two ref-or-null's */ if (!(remaining_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && s->access_from_tables_is_allowed(keyuse->used_tables, join->sjm_lookup_tables) && !(ref_or_null_part && (keyuse->optimize & @@ -6694,6 +7134,7 @@ best_access_path(JOIN *join, tmp += s->startup_cost; loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ + if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE) { best_time= tmp + records/(double) TIME_FOR_COMPARE; @@ -6881,6 +7322,7 @@ best_access_path(JOIN *join, pos->ref_depend_map= best_ref_depends_map; pos->loosescan_picker.loosescan_key= MAX_KEY; pos->use_join_buffer= best_uses_jbuf; + pos->spl_plan= spl_plan; loose_scan_opt.save_to_position(s, loose_scan_pos); @@ -8935,192 +9377,15 @@ JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) return tab; } -static -bool key_can_be_used_to_split_by_fields(KEY *key_info, uint used_key_parts, - List<Field> &fields) -{ - if (used_key_parts < fields.elements) - return false; - List_iterator_fast<Field> li(fields); - Field *fld; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + fields.elements; - while ((fld= li++)) - { - KEY_PART_INFO *key_part; - for (key_part= start; key_part < end; key_part++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - if (key_part == end) - return false; - } - return true; -} - -bool JOIN::check_for_splittable_grouping_derived(THD *thd) -{ - partition_list= 0; - st_select_lex_unit *unit= select_lex->master_unit(); - TABLE_LIST *derived= unit->derived; - if (!optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED)) - return false; - if (!(derived && derived->is_materialized_derived())) - return false; - if (unit->first_select()->next_select()) - return false; - if (derived->prohibit_cond_pushdown) - return false; - if (derived->is_recursive_with_table()) - return false; - if (group_list) - { - if (!select_lex->have_window_funcs()) - partition_list= group_list; - } - else if (select_lex->have_window_funcs() && - select_lex->window_specs.elements == 1) - { - partition_list= - select_lex->window_specs.head()->partition_list->first; - } - if (!partition_list) - return false; - - ORDER *ord; - TABLE *table= 0; - key_map ref_keys; - uint group_fields= 0; - ref_keys.set_all(); - for (ord= partition_list; ord; ord= ord->next, group_fields++) - { - Item *ord_item= *ord->item; - if (ord_item->real_item()->type() != Item::FIELD_ITEM) - return false; - Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; - if (!table) - table= ord_field->table; - else if (table != ord_field->table) - return false; - ref_keys.intersect(ord_field->part_of_key); - } - if (ref_keys.is_clear_all()) - return false; - - uint i; - List<Field> grouping_fields; - List<Field> splitting_fields; - List_iterator<Item> li(fields_list); - for (ord= partition_list; ord; ord= ord->next) - { - Item *item; - i= 0; - while ((item= li++)) - { - if ((*ord->item)->eq(item, 0)) - break; - i++; - } - if (!item) - return false; - if (splitting_fields.push_back(derived->table->field[i], thd->mem_root)) - return false; - Item_field *ord_field= (Item_field *)(item->real_item()); - if (grouping_fields.push_back(ord_field->field, thd->mem_root)) - return false; - li.rewind(); - } - - for (i= 0; i < table->s->keys; i++) - { - if (!(ref_keys.is_set(i))) - continue; - KEY *key_info= table->key_info + i; - if (key_can_be_used_to_split_by_fields(key_info, - table->actual_n_key_parts(key_info), - grouping_fields)) - break; - } - if (i == table->s->keys) - return false; - - derived->table->splitting_fields= splitting_fields; - is_for_splittable_grouping_derived= true; - return true; -} - bool JOIN::check_two_phase_optimization(THD *thd) { - if (!check_for_splittable_grouping_derived(thd)) - return false; - return true; + if (check_for_splittable_materialized()) + return true; + return false; } -Item *JOIN_TAB::get_splitting_cond_for_grouping_derived(THD *thd) -{ - /* this is a stub */ - TABLE_LIST *derived= table->pos_in_table_list; - st_select_lex *sel= derived->get_unit()->first_select(); - Item *cond= 0; - table_map used_tables= OUTER_REF_TABLE_BIT; - POSITION *pos= join->best_positions; - for (; pos->table != this; pos++) - { - used_tables|= pos->table->table->map; - } - - if (!pos->key) - return 0; - - KEY *key_info= table->key_info + pos->key->key; - if (!key_can_be_used_to_split_by_fields(key_info, - key_info->user_defined_key_parts, - table->splitting_fields)) - return 0; - - create_ref_for_key(join, this, pos->key, - false, used_tables); - List<Item> cond_list; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + table->splitting_fields.elements; - List_iterator_fast<Field> li(table->splitting_fields); - Field *fld= li++; - for (ORDER *ord= sel->join->partition_list; ord; - ord= ord->next, fld= li++) - { - Item *left_item= (*ord->item)->build_clone(thd, thd->mem_root); - uint i= 0; - for (KEY_PART_INFO *key_part= start; key_part < end; key_part++, i++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - Item *right_item= ref.items[i]->build_clone(thd, thd->mem_root); - Item_func_eq *eq_item= 0; - right_item= right_item->build_clone(thd, thd->mem_root); - if (left_item && right_item) - { - right_item->walk(&Item::set_fields_as_dependent_processor, - false, join->select_lex); - right_item->update_used_tables(); - eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); - } - if (!eq_item || cond_list.push_back(eq_item, thd->mem_root)) - return 0; - } - switch (cond_list.elements) { - case 0: break; - case 1: cond= cond_list.head(); break; - default: cond= new (thd->mem_root) Item_cond_and(thd, cond_list); - } - if (cond) - cond->fix_fields(thd,0); - return cond; -} - bool JOIN::inject_cond_into_where(Item *injected_cond) { Item *where_item= injected_cond; @@ -9155,48 +9420,6 @@ bool JOIN::inject_cond_into_where(Item *injected_cond) } -bool JOIN::push_splitting_cond_into_derived(THD *thd, Item *cond) -{ - enum_reopt_result reopt_result= REOPT_NONE; - table_map all_table_map= 0; - for (JOIN_TAB *tab= join_tab; - tab < join_tab + top_join_tab_count; tab++) - all_table_map|= tab->table->map; - reopt_result= reoptimize(cond, all_table_map & ~const_table_map, NULL); - if (reopt_result == REOPT_ERROR) - return true; - if (inject_cond_into_where(cond)) - return true; - if (cond->used_tables() & OUTER_REF_TABLE_BIT) - { - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - st_select_lex_unit *unit= select_lex->master_unit(); - unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - } - return false; -} - -bool JOIN::improve_chosen_plan(THD *thd) -{ - for (JOIN_TAB *tab= join_tab + const_tables; - tab < join_tab + table_count; tab++) - { - TABLE_LIST *tbl= tab->table->pos_in_table_list; - if (tbl->is_materialized_derived()) - { - st_select_lex *sel= tbl->get_unit()->first_select(); - JOIN *derived_join= sel->join; - if (derived_join && derived_join->is_for_splittable_grouping_derived) - { - Item *cond= tab->get_splitting_cond_for_grouping_derived(thd); - if (cond && derived_join->push_splitting_cond_into_derived(thd, cond)) - return true; - } - } - } - return false; -} - static Item * const null_ptr= NULL; @@ -9265,6 +9488,9 @@ bool JOIN::get_best_combination() full_join=0; hash_join= FALSE; + if (fix_all_splittings_in_plan()) + DBUG_RETURN(TRUE); + fix_semijoin_strategies_for_picked_join_order(this); JOIN_TAB_RANGE *root_range; @@ -9479,7 +9705,8 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->flags= HA_GENERATED_KEY; keyinfo->is_statistics_from_stat_tables= FALSE; - keyinfo->name= (char *) "$hj"; + keyinfo->name.str= "$hj"; + keyinfo->name.length= 3; keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts); if (!keyinfo->rec_per_key) DBUG_RETURN(TRUE); @@ -9604,6 +9831,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, do { if (!(~used_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse)) { if (are_tables_local(j, keyuse->val->used_tables())) @@ -9674,6 +9902,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, for (i=0 ; i < keyparts ; keyuse++,i++) { while (((~used_tables) & keyuse->used_tables) || + (keyuse->validity_ref && !(*keyuse->validity_ref)) || !j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse) || keyuse->keypart == NO_KEYPART || (keyuse->keypart != @@ -11058,32 +11287,32 @@ pick_table_access_method(JOIN_TAB *tab) { case JT_REF: tab->read_first_record= join_read_always_key; - tab->read_record.read_record= join_read_next_same; + tab->read_record.read_record_func= join_read_next_same; break; case JT_REF_OR_NULL: tab->read_first_record= join_read_always_key_or_null; - tab->read_record.read_record= join_read_next_same_or_null; + tab->read_record.read_record_func= join_read_next_same_or_null; break; case JT_CONST: tab->read_first_record= join_read_const; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; case JT_EQ_REF: tab->read_first_record= join_read_key; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; case JT_FT: tab->read_first_record= join_ft_read_first; - tab->read_record.read_record= join_ft_read_next; + tab->read_record.read_record_func= join_ft_read_next; break; case JT_SYSTEM: tab->read_first_record= join_read_system; - tab->read_record.read_record= join_no_more_records; + tab->read_record.read_record_func= join_no_more_records; break; /* keep gcc happy */ @@ -12285,7 +12514,8 @@ bool JOIN_TAB::preread_init() /* init ftfuns for just initialized derived table */ if (table->fulltext_searched) - init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order)); + if (init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order))) + return TRUE; return FALSE; } @@ -13137,6 +13367,8 @@ public: size_t size __attribute__((unused))) { TRASH_FREE(ptr, size); } + static void operator delete(void *, MEM_ROOT*) {} + Item *and_level; Item_bool_func2 *cmp_func; COND_CMP(Item *a,Item_bool_func2 *b) :and_level(a),cmp_func(b) {} @@ -14330,7 +14562,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, equals on top level, or the constant. */ Item *head_item= (!item_const && current_sjm && - current_sjm_head != field_item) ? current_sjm_head: head; + current_sjm_head != field_item) ? current_sjm_head: head; Item *head_real_item= head_item->real_item(); if (head_real_item->type() == Item::FIELD_ITEM) head_item= head_real_item; @@ -14495,7 +14727,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, This works OK with PS/SP re-execution as changes are made to the arguments of AND/OR items only */ - if (new_item != item) + if (new_item && new_item != item) li.replace(new_item); } @@ -14574,7 +14806,9 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab, while((item_equal= it++)) { REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab}; - cond= cond->transform(thd, &Item::replace_equal_field, (uchar *) &arg); + if (!(cond= cond->transform(thd, &Item::replace_equal_field, + (uchar *) &arg))) + return 0; } cond_equal= cond_equal->upper_levels; } @@ -14731,6 +14965,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, { cond->marker=1; COND_CMP *tmp2; + /* Will work, even if malloc would fail */ if ((tmp2= new (thd->mem_root) COND_CMP(and_father, func))) save_list->push_back(tmp2); } @@ -14763,6 +14998,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, thd->change_item_tree(args + 1, value); cond->marker=1; COND_CMP *tmp2; + /* Will work, even if malloc would fail */ if ((tmp2=new (thd->mem_root) COND_CMP(and_father, func))) save_list->push_back(tmp2); } @@ -16195,6 +16431,7 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value, query_cache_abort(thd, &thd->query_cache_tls); #endif COND *new_cond, *cond= this; + /* If this fails, we will catch it later before executing query */ if ((new_cond= new (thd->mem_root) Item_func_eq(thd, args[0], new (thd->mem_root) Item_int(thd, "last_insert_id()", thd->read_first_successful_insert_id_in_prev_stmt(), @@ -16399,7 +16636,11 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, item->result_field= new_field; else new_field->field_name= *name; - new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG); + new_field->flags|= (org_field->flags & ( + NO_DEFAULT_VALUE_FLAG | + VERS_SYS_START_FLAG | + VERS_SYS_END_FLAG | + VERS_UPDATE_UNVERSIONED_FLAG)); if (org_field->maybe_null() || (item && item->maybe_null)) new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join if (org_field->type() == MYSQL_TYPE_VAR_STRING || @@ -16659,6 +16900,10 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, modify_item ? field : NULL); } + + if (field->field->vers_sys_field()) + result->invisible= field->field->invisible; + if (orig_type == Item::REF_ITEM && orig_modify) ((Item_ref*)orig_item)->set_result_field(result); /* @@ -16809,7 +17054,7 @@ TABLE * create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, ulonglong select_options, ha_rows rows_limit, - const char *table_alias, bool do_not_open, + const LEX_CSTRING *table_alias, bool do_not_open, bool keep_row_order) { MEM_ROOT *mem_root_save, own_root; @@ -16846,12 +17091,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, DBUG_ENTER("create_tmp_table"); DBUG_PRINT("enter", ("table_alias: '%s' distinct: %d save_sum_fields: %d " - "rows_limit: %lu group: %d", table_alias, + "rows_limit: %lu group: %d", table_alias->str, (int) distinct, (int) save_sum_fields, (ulong) rows_limit, MY_TEST(group))); - thd->query_plan_flags|= QPLAN_TMP_TABLE; - if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES)) temp_pool_slot = bitmap_lock_set_next(&temp_pool); @@ -16965,7 +17208,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, thd->mem_root= &table->mem_root; table->field=reg_field; - table->alias.set(table_alias, strlen(table_alias), table_alias_charset); + table->alias.set(table_alias->str, table_alias->length, table_alias_charset); table->reginfo.lock_type=TL_WRITE; /* Will be updated */ table->map=1; @@ -17035,7 +17278,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, Item *arg= sum_item->get_arg(i); if (!arg->const_item()) { - Field *new_field= + Item *tmp_item; + Field *new_field= create_tmp_field(thd, table, arg, arg->type(), ©_func, tmp_from_field, &default_field[fieldnr], group != 0,not_all_columns, @@ -17060,7 +17304,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, string_total_length+= new_field->pack_length(); } thd->mem_root= mem_root_save; - arg= sum_item->set_arg(i, thd, new (thd->mem_root) Item_temptable_field(thd, new_field)); + if (!(tmp_item= new (thd->mem_root) + Item_temptable_field(thd, new_field))) + goto err; + arg= sum_item->set_arg(i, thd, tmp_item); thd->mem_root= &table->mem_root; if (param->force_not_null_cols) { @@ -17415,7 +17662,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->collected_stats= NULL; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; - keyinfo->name= (char*) "group_key"; + keyinfo->name= group_key; ORDER *cur_group= group; for (; cur_group ; cur_group= cur_group->next, key_part_info++) { @@ -17526,7 +17773,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->key_length= 0; // Will compute the sum of the parts below. - keyinfo->name= (char*) "distinct_key"; + keyinfo->name= distinct_key; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->read_stats= NULL; @@ -17656,7 +17903,6 @@ err: } - /****************************************************************************/ void *Virtual_tmp_table::operator new(size_t size, THD *thd) throw() @@ -18046,7 +18292,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, table->in_use->inc_status_created_tmp_disk_tables(); table->in_use->inc_status_created_tmp_tables(); - table->in_use->query_plan_flags|= QPLAN_TMP_DISK; share->db_record_offset= 1; table->set_created(); DBUG_RETURN(0); @@ -18201,7 +18446,6 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } table->in_use->inc_status_created_tmp_disk_tables(); table->in_use->inc_status_created_tmp_tables(); - table->in_use->query_plan_flags|= QPLAN_TMP_DISK; share->db_record_offset= 1; table->set_created(); DBUG_RETURN(0); @@ -18368,7 +18612,12 @@ free_tmp_table(THD *thd, TABLE *entry) { entry->file->ha_index_or_rnd_end(); if (entry->db_stat) + { + entry->file->info(HA_STATUS_VARIABLE); + thd->tmp_tables_size+= (entry->file->stats.data_file_length + + entry->file->stats.index_file_length); entry->file->ha_drop_table(entry->s->table_name.str); + } else entry->file->ha_delete_table(entry->s->table_name.str); delete entry->file; @@ -18384,6 +18633,12 @@ free_tmp_table(THD *thd, TABLE *entry) plugin_unlock(0, entry->s->db_plugin); entry->alias.free(); + if (entry->pos_in_table_list && entry->pos_in_table_list->table) + { + DBUG_ASSERT(entry->pos_in_table_list->table == entry); + entry->pos_in_table_list->table= NULL; + } + free_root(&own_root, MYF(0)); /* the table is allocated in its own root */ thd_proc_info(thd, save_proc_info); @@ -18709,6 +18964,10 @@ bool instantiate_tmp_table(TABLE *table, KEY *keyinfo, { if (table->s->db_type() == TMP_ENGINE_HTON) { + /* + If it is not heap (in-memory) table then convert index to unique + constrain. + */ if (create_internal_tmp_table(table, keyinfo, start_recinfo, recinfo, options)) return TRUE; @@ -19074,7 +19333,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) skip_over= TRUE; } - error= info->read_record(info); + error= info->read_record(); if (skip_over && !error) { @@ -19592,6 +19851,7 @@ join_read_system(JOIN_TAB *tab) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); + table->const_table= 1; mark_as_null_row(tab->table); empty_record(table); // Make empty record return -1; @@ -19917,7 +20177,7 @@ int read_first_record_seq(JOIN_TAB *tab) { if (tab->read_record.table->file->ha_rnd_init_with_error(1)) return 1; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } static int @@ -19977,7 +20237,7 @@ int join_init_read_record(JOIN_TAB *tab) if (init_read_record(&tab->read_record, tab->join->thd, tab->table, tab->select, tab->filesort_result, 1,1, FALSE)) return 1; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } int @@ -19997,9 +20257,9 @@ join_read_record_no_init(JOIN_TAB *tab) tab->read_record.copy_field= save_copy; tab->read_record.copy_field_end= save_copy_end; - tab->read_record.read_record= rr_sequential_and_unpack; + tab->read_record.read_record_func= rr_sequential_and_unpack; - return (*tab->read_record.read_record)(&tab->read_record); + return tab->read_record.read_record(); } @@ -20032,7 +20292,7 @@ join_read_first(JOIN_TAB *tab) !table->covering_keys.is_set(tab->index) || table->file->keyread == tab->index); tab->table->status=0; - tab->read_record.read_record=join_read_next; + tab->read_record.read_record_func= join_read_next; tab->read_record.table=table; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; @@ -20072,7 +20332,7 @@ join_read_last(JOIN_TAB *tab) !table->covering_keys.is_set(tab->index) || table->file->keyread == tab->index); tab->table->status=0; - tab->read_record.read_record=join_read_prev; + tab->read_record.read_record_func= join_read_prev; tab->read_record.table=table; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; @@ -22038,7 +22298,7 @@ check_reverse_order: with key part (A) and then traverse the index backwards. */ tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; + tab->read_record.read_record_func= join_read_prev_same; /* Cancel Pushed Index Condition, as it doesn't work for reverse scans. */ @@ -22200,7 +22460,7 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort) table->file->ha_end_keyread(); if (tab->type == JT_FT) - table->file->ft_end(); + table->file->ha_ft_end(); else table->file->ha_index_or_rnd_end(); @@ -22980,7 +23240,7 @@ setup_new_fields(THD *thd, List<Item> &fields, enum_resolution_type not_used; DBUG_ENTER("setup_new_fields"); - thd->mark_used_columns= MARK_COLUMNS_READ; // Not really needed, but... + thd->column_usage= MARK_COLUMNS_READ; // Not really needed, but... for (; new_field ; new_field= new_field->next) { if ((item= find_item_in_list(*new_field->item, fields, &counter, @@ -23003,6 +23263,10 @@ setup_new_fields(THD *thd, List<Item> &fields, Try to use the fields in the order given by 'order' to allow one to optimize away 'order by'. + + @retval + 0 OOM error if thd->is_fatal_error is set. Otherwise group was eliminated + # Pointer to new group */ ORDER * @@ -23065,6 +23329,8 @@ create_distinct_group(THD *thd, Ref_ptr_array ref_pointer_array, BIT type and will be returned [el]client. */ Item_field *new_item= new (thd->mem_root) Item_field(thd, (Item_field*)item); + if (!new_item) + return 0; int el= all_fields.elements; orig_ref_pointer_array[el]= new_item; all_fields.push_front(new_item, thd->mem_root); @@ -23747,7 +24013,10 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) item_field= item; else if (item->type() == Item::FIELD_ITEM) - item_field= item->get_tmp_table_item(thd); + { + if (!(item_field= item->get_tmp_table_item(thd))) + DBUG_RETURN(true); + } else if (item->type() == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC) { @@ -23855,8 +24124,13 @@ change_refs_to_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, if (item->type() == Item::SUM_FUNC_ITEM && item->const_item()) new_item= item; else - new_item= item->get_tmp_table_item(thd); - res_all_fields.push_back(new_item, thd->mem_root); + { + if (!(new_item= item->get_tmp_table_item(thd))) + return 1; + } + + if (res_all_fields.push_back(new_item, thd->mem_root)) + return 1; ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]= new_item; } @@ -24222,7 +24496,9 @@ bool JOIN::rollup_init() */ for (i= 0 ; i < send_group_parts ; i++) { - rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd); + if (!(rollup.null_items[i]= new (thd->mem_root) Item_null_result(thd))) + return true; + List<Item> *rollup_fields= &rollup.fields[i]; rollup_fields->empty(); rollup.ref_pointer_arrays[i]= Ref_ptr_array(ref_array, all_fields.elements); @@ -24566,8 +24842,12 @@ void JOIN::clear() } -/* +/** Print an EXPLAIN line with all NULLs and given message in the 'Extra' column + + @retval + 0 ok + 1 OOM error or error from send_data() */ int print_explain_message_line(select_result_sink *result, @@ -24626,7 +24906,7 @@ int print_explain_message_line(select_result_sink *result, else item_list.push_back(item_null, mem_root); - if (result->send_data(item_list)) + if (thd->is_fatal_error || result->send_data(item_list)) return 1; return 0; } @@ -24660,13 +24940,14 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table, for (j=0 ; j < table->s->keys ; j++) { if (possible_keys.is_set(j)) - list.append_str(alloc, table->key_info[j].name); + if (!(list.append_str(alloc, table->key_info[j].name.str))) + return 1; } return 0; } -void JOIN_TAB::save_explain_data(Explain_table_access *eta, +bool JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tables, bool distinct_arg, JOIN_TAB *first_top_tab) { @@ -24695,9 +24976,11 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (filesort) { - eta->pre_join_sort= new Explain_aggr_filesort(thd->mem_root, - thd->lex->analyze_stmt, - filesort); + if (!(eta->pre_join_sort= + new Explain_aggr_filesort(thd->mem_root, + thd->lex->analyze_stmt, + filesort))) + return 1; } tracker= &eta->tracker; @@ -24713,7 +24996,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (table->derived_select_number) { /* Derived table name generation */ - int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, + size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, "<derived%u>", table->derived_select_number); eta->table_name.copy(table_name_buffer, len, cs); @@ -24722,7 +25005,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, { JOIN_TAB *ctab= bush_children->start; /* table */ - int len= my_snprintf(table_name_buffer, + size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, "<subquery%d>", ctab->emb_sj_nest->sj_subq_pred->get_identifier()); @@ -24751,7 +25034,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, } } } - eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs); + eta->table_name.copy(real_table->alias.str, real_table->alias.length, cs); } /* "partitions" column */ @@ -24794,7 +25077,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, // psergey-todo: why does this use thd MEM_ROOT??? Doesn't this // break ANALYZE ? thd->mem_root will be freed, and after that we will // attempt to print the query plan? - append_possible_keys(thd->mem_root, eta->possible_keys, table, keys); + if (append_possible_keys(thd->mem_root, eta->possible_keys, table, keys)) + return 1; // psergey-todo: ^ check for error return code /* Build "key", "key_len", and "ref" */ @@ -24815,7 +25099,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, */ if (tab_select && tab_select->quick && tab_type != JT_CONST) { - eta->quick_info= tab_select->quick->get_explain(thd->mem_root); + if (!(eta->quick_info= tab_select->quick->get_explain(thd->mem_root))) + return 1; } if (key_info) /* 'index' or 'ref' access */ @@ -24828,10 +25113,14 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, for (uint kp= 0; kp < ref.key_parts; kp++) { if ((key_part_map(1) << kp) & ref.const_ref_part_map) - eta->ref_list.append_str(thd->mem_root, "const"); + { + if (!(eta->ref_list.append_str(thd->mem_root, "const"))) + return 1; + } else { - eta->ref_list.append_str(thd->mem_root, (*key_ref)->name()); + if (!(eta->ref_list.append_str(thd->mem_root, (*key_ref)->name()))) + return 1; key_ref++; } } @@ -25065,13 +25354,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, { char namebuf[NAME_LEN]; /* Derived table name generation */ - int len= my_snprintf(namebuf, sizeof(namebuf)-1, + size_t len= my_snprintf(namebuf, sizeof(namebuf)-1, "<derived%u>", prev_table->derived_select_number); eta->firstmatch_table_name.append(namebuf, len); } else - eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias); + eta->firstmatch_table_name.append(&prev_table->pos_in_table_list->alias); } } @@ -25088,7 +25377,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (cache) { eta->push_extra(ET_USING_JOIN_BUFFER); - cache->save_explain_data(&eta->bka_type); + if (cache->save_explain_data(&eta->bka_type)) + return 1; } } @@ -25101,15 +25391,21 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, /* The same for non-merged semi-joins */ eta->non_merged_sjm_number = get_non_merged_semijoin_select(); + + return 0; } /* Walk through join->aggr_tables and save aggregation/grouping query plan into an Explain_select object + + @retval + 0 ok + 1 error */ -void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) +bool save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { JOIN_TAB *join_tab=join->join_tab + join->exec_join_tab_cnt(); Explain_aggr_node *prev_node; @@ -25121,7 +25417,8 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { // Each aggregate means a temp.table prev_node= node; - node= new Explain_aggr_tmp_table; + if (!(node= new Explain_aggr_tmp_table)) + return 1; node->child= prev_node; if (join_tab->window_funcs_step) @@ -25129,19 +25426,20 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) Explain_aggr_node *new_node= join_tab->window_funcs_step->save_explain_plan(thd->mem_root, is_analyze); - if (new_node) - { - prev_node=node; - node= new_node; - node->child= prev_node; - } + if (!new_node) + return 1; + + prev_node=node; + node= new_node; + node->child= prev_node; } /* The below matches execution in join_init_read_record() */ if (join_tab->distinct) { prev_node= node; - node= new Explain_aggr_remove_dups; + if (!(node= new Explain_aggr_remove_dups)) + return 1; node->child= prev_node; } @@ -25149,20 +25447,27 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) { Explain_aggr_filesort *eaf = new Explain_aggr_filesort(thd->mem_root, is_analyze, join_tab->filesort); + if (!eaf) + return 1; prev_node= node; node= eaf; node->child= prev_node; } } xpl_sel->aggr_tree= node; + return 0; } -/* +/** Save Query Plan Footprint @note Currently, this function may be called multiple times + + @retval + 0 ok + 1 error */ int JOIN::save_explain_data_intern(Explain_query *output, @@ -25171,7 +25476,6 @@ int JOIN::save_explain_data_intern(Explain_query *output, const char *message) { JOIN *join= this; /* Legacy: this code used to be a non-member function */ - int cur_error= 0; DBUG_ENTER("JOIN::save_explain_data_intern"); DBUG_PRINT("info", ("Select %p, type %s, message %s", join->select_lex, join->select_lex->type, @@ -25189,10 +25493,10 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (message) { - explain= new (output->mem_root) Explain_select(output->mem_root, - thd->lex->analyze_stmt); - if (!explain) - DBUG_RETURN(1); // EoM + if (!(explain= new (output->mem_root) + Explain_select(output->mem_root, + thd->lex->analyze_stmt))) + DBUG_RETURN(1); #ifndef DBUG_OFF explain->select_lex= select_lex; #endif @@ -25208,13 +25512,17 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (select_lex->master_unit()->derived) explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; - save_agg_explain_data(this, explain); + if (save_agg_explain_data(this, explain)) + DBUG_RETURN(1); + output->add_node(explain); } else if (pushdown_query) { - explain= new (output->mem_root) Explain_select(output->mem_root, - thd->lex->analyze_stmt); + if (!(explain= new (output->mem_root) + Explain_select(output->mem_root, + thd->lex->analyze_stmt))) + DBUG_RETURN(1); select_lex->set_explain_type(true); explain->select_id= select_lex->select_number; @@ -25234,6 +25542,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain= xpl_sel= new (output->mem_root) Explain_select(output->mem_root, thd->lex->analyze_stmt); + if (!explain) + DBUG_RETURN(1); + table_map used_tables=0; join->select_lex->set_explain_type(true); @@ -25243,7 +25554,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, if (select_lex->master_unit()->derived) xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; - save_agg_explain_data(this, xpl_sel); + if (save_agg_explain_data(this, xpl_sel)) + DBUG_RETURN(1); xpl_sel->exec_const_cond= exec_const_cond; xpl_sel->outer_ref_cond= outer_ref_cond; @@ -25275,6 +25587,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, Explain_table_access *eta= (new (output->mem_root) Explain_table_access(output->mem_root)); + if (!eta) + DBUG_RETURN(1); if (tab->bush_root_tab != prev_bush_root_tab) { if (tab->bush_root_tab) @@ -25282,7 +25596,9 @@ int JOIN::save_explain_data_intern(Explain_query *output, /* We've entered an SJ-Materialization nest. Create an object for it. */ - cur_parent= new (output->mem_root) Explain_basic_join(output->mem_root); + if (!(cur_parent= + new (output->mem_root) Explain_basic_join(output->mem_root))) + DBUG_RETURN(1); JOIN_TAB *first_child= tab->bush_root_tab->bush_children->start; cur_parent->select_id= @@ -25302,7 +25618,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, prev_bush_root_tab= tab->bush_root_tab; cur_parent->add_table(eta, output); - tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab); + if (tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab)) + DBUG_RETURN(1); if (saved_join_tab) tab= saved_join_tab; @@ -25334,10 +25651,10 @@ int JOIN::save_explain_data_intern(Explain_query *output, } } - if (!cur_error && select_lex->is_top_level_node()) + if (select_lex->is_top_level_node()) output->query_plan_ready(); - DBUG_RETURN(cur_error); + DBUG_RETURN(0); } @@ -25644,8 +25961,8 @@ Index_hint::print(THD *thd, String *str) strlen(primary_key_name))) str->append(primary_key_name); else - append_identifier(thd, str, key_name.str, key_name.length); - } + append_identifier(thd, str, &key_name); +} str->append(')'); } @@ -25701,10 +26018,10 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, if (!(belong_to_view && belong_to_view->compact_view_format)) { - append_identifier(thd, str, view_db.str, view_db.length); + append_identifier(thd, str, &view_db); str->append('.'); } - append_identifier(thd, str, view_name.str, view_name.length); + append_identifier(thd, str, &view_name); cmp_name= view_name.str; } else if (derived) @@ -25719,8 +26036,8 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, } else { - append_identifier(thd, str, table_name, table_name_length); - cmp_name= table_name; + append_identifier(thd, str, &table_name); + cmp_name= table_name.str; } } else @@ -25730,19 +26047,18 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, if (!(belong_to_view && belong_to_view->compact_view_format)) { - append_identifier(thd, str, db, db_length); + append_identifier(thd, str, &db); str->append('.'); } if (schema_table) { - append_identifier(thd, str, schema_table_name, - strlen(schema_table_name)); - cmp_name= schema_table_name; + append_identifier(thd, str, &schema_table_name); + cmp_name= schema_table_name.str; } else { - append_identifier(thd, str, table_name, table_name_length); - cmp_name= table_name; + append_identifier(thd, str, &table_name); + cmp_name= table_name.str; } #ifdef WITH_PARTITION_STORAGE_ENGINE if (partition_names && partition_names->elements) @@ -25761,23 +26077,26 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, } #endif /* WITH_PARTITION_STORAGE_ENGINE */ } - if (my_strcasecmp(table_alias_charset, cmp_name, alias)) + if (table && table->versioned()) + vers_conditions.print(str, query_type); + + if (my_strcasecmp(table_alias_charset, cmp_name, alias.str)) { char t_alias_buff[MAX_ALIAS_NAME]; - const char *t_alias= alias; + LEX_CSTRING t_alias= alias; str->append(' '); if (lower_case_table_names== 1) { - if (alias && alias[0]) + if (alias.str && alias.str[0]) { - strmov(t_alias_buff, alias); - my_casedn_str(files_charset_info, t_alias_buff); - t_alias= t_alias_buff; + strmov(t_alias_buff, alias.str); + t_alias.length= my_casedn_str(files_charset_info, t_alias_buff); + t_alias.str= t_alias_buff; } } - append_identifier(thd, str, t_alias, strlen(t_alias)); + append_identifier(thd, str, &t_alias); } if (index_hints) @@ -25798,6 +26117,12 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { DBUG_ASSERT(thd); + + if (tvc) + { + tvc->print(thd, str, query_type); + return; + } if ((query_type & QT_SHOW_SELECT_NUMBER) && thd->lex->all_selects_list && @@ -26977,7 +27302,7 @@ AGGR_OP::end_send() error= join_init_read_record(join_tab); } else - error= join_tab->read_record.read_record(&join_tab->read_record); + error= join_tab->read_record.read_record(); if (error > 0 || (join->thd->is_error())) // Fatal error rc= NESTED_LOOP_ERROR; diff --git a/sql/sql_select.h b/sql/sql_select.h index eb78f4216f9..f8911fbba01 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -73,9 +73,45 @@ typedef struct keyuse_t { */ uint sj_pred_no; + /* + If this is NULL than KEYUSE is always enabled. + Otherwise it points to the enabling flag for this keyuse (true <=> enabled) + */ + bool *validity_ref; + bool is_for_hash_join() { return is_hash_join_key_no(key); } } KEYUSE; + +struct KEYUSE_EXT: public KEYUSE +{ + /* + This keyuse can be used only when the partial join being extended + contains the tables from this table map + */ + table_map needed_in_prefix; + /* The enabling flag for keyuses usable for splitting */ + bool validity_var; +}; + +/// Used when finding key fields +struct KEY_FIELD { + Field *field; + Item_bool_func *cond; + Item *val; ///< May be empty if diff constant + uint level; + uint optimize; + bool eq_func; + /** + If true, the condition this struct represents will not be satisfied + when val IS NULL. + */ + bool null_rejecting; + bool *cond_guard; /* See KEYUSE::cond_guard */ + uint sj_pred_no; /* See KEYUSE::sj_pred_no */ +}; + + #define NO_KEYPART ((uint)(-1)) class store_key; @@ -201,6 +237,8 @@ class SJ_TMP_TABLE; class JOIN_TAB_RANGE; class AGGR_OP; class Filesort; +struct SplM_plan_info; +class SplM_opt_info; typedef struct st_join_table { st_join_table() {} @@ -608,14 +646,16 @@ typedef struct st_join_table { void remove_redundant_bnl_scan_conds(); - void save_explain_data(Explain_table_access *eta, table_map prefix_tables, + bool save_explain_data(Explain_table_access *eta, table_map prefix_tables, bool distinct, struct st_join_table *first_top_tab); bool use_order() const; ///< Use ordering provided by chosen index? bool sort_table(); bool remove_duplicates(); - Item *get_splitting_cond_for_grouping_derived(THD *thd); - + void add_keyuses_for_splitting(); + SplM_plan_info *choose_best_splitting(double record_count, + table_map remaining_tables); + bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables); } JOIN_TAB; @@ -920,6 +960,9 @@ typedef struct st_position Firstmatch_picker firstmatch_picker; LooseScan_picker loosescan_picker; Sj_materialization_picker sjmat_picker; + + /* Info on splitting plan used at this position */ + SplM_plan_info *spl_plan; } POSITION; typedef Bounds_checked_array<Item_null_result*> Item_null_array; @@ -1053,11 +1096,13 @@ protected: /* Support for plan reoptimization with rewritten conditions. */ enum_reopt_result reoptimize(Item *added_where, table_map join_tables, Join_plan_state *save_to); + /* Choose a subquery plan for a table-less subquery. */ + bool choose_tableless_subquery_plan(); + +public: void save_query_plan(Join_plan_state *save_to); void reset_query_plan(); void restore_query_plan(Join_plan_state *restore_from); - /* Choose a subquery plan for a table-less subquery. */ - bool choose_tableless_subquery_plan(); public: JOIN_TAB *join_tab, **best_ref; @@ -1415,9 +1460,14 @@ public: */ bool implicit_grouping; - bool is_for_splittable_grouping_derived; bool with_two_phase_optimization; - ORDER *partition_list; + + /* Saved execution plan for this join */ + Join_plan_state *save_qep; + /* Info on splittability of the table materialized by this plan*/ + SplM_opt_info *spl_opt_info; + /* Contains info on keyuses usable for splitting */ + Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting; JOIN_TAB *sort_and_group_aggr_tab; @@ -1436,7 +1486,7 @@ public: table_count= 0; top_join_tab_count= 0; const_tables= 0; - const_table_map= 0; + const_table_map= found_const_table_map= 0; aggr_tables= 0; eliminated_tables= 0; join_list= 0; @@ -1465,7 +1515,10 @@ public: need_distinct= 0; skip_sort_order= 0; with_two_phase_optimization= 0; - is_for_splittable_grouping_derived= 0; + save_qep= 0; + spl_opt_info= 0; + ext_keyuses_for_splitting= 0; + spl_opt_info= 0; need_tmp= 0; hidden_group_fields= 0; /*safety*/ error= 0; @@ -1526,7 +1579,7 @@ public: int optimize(); int optimize_inner(); int optimize_stage2(); - void build_explain(); + bool build_explain(); int reinit(); int init_execution(); void exec(); @@ -1667,17 +1720,20 @@ public: { return (unit->item && unit->item->is_in_predicate()); } - void save_explain_data(Explain_query *output, bool can_overwrite, + bool save_explain_data(Explain_query *output, bool can_overwrite, bool need_tmp_table, bool need_order, bool distinct); int save_explain_data_intern(Explain_query *output, bool need_tmp_table, bool need_order, bool distinct, const char *message); JOIN_TAB *first_breadth_first_tab() { return join_tab; } bool check_two_phase_optimization(THD *thd); - bool check_for_splittable_grouping_derived(THD *thd); bool inject_cond_into_where(Item *injected_cond); - bool push_splitting_cond_into_derived(THD *thd, Item *cond); - bool improve_chosen_plan(THD *thd); + bool check_for_splittable_materialized(); + void add_keyuses_for_splitting(); + bool inject_best_splitting_cond(table_map remaining_tables); + bool fix_all_splittings_in_plan(); + + bool transform_in_predicates_into_in_subq(THD *thd); private: /** Create a temporary table to be used for processing DISTINCT/ORDER @@ -1711,7 +1767,6 @@ private: void cleanup_item_list(List<Item> &items) const; bool add_having_as_table_cond(JOIN_TAB *tab); bool make_aggr_tables_info(); - }; enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS}; @@ -2057,7 +2112,8 @@ public: @param thd - Current thread. */ static void *operator new(size_t size, THD *thd) throw(); - static void operator delete(void *ptr, size_t size) {TRASH_FREE(ptr, size);} + static void operator delete(void *ptr, size_t size) { TRASH_FREE(ptr, size); } + static void operator delete(void *, THD *) throw(){} Virtual_tmp_table(THD *thd) { @@ -2255,6 +2311,10 @@ inline Item * and_items(THD *thd, Item* cond, Item *item) { return (cond ? (new (thd->mem_root) Item_cond_and(thd, cond, item)) : item); } +inline Item * or_items(THD *thd, Item* cond, Item *item) +{ + return (cond ? (new (thd->mem_root) Item_cond_or(thd, cond, item)) : item); +} bool choose_plan(JOIN *join, table_map join_tables); void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, table_map last_remaining_tables, @@ -2316,7 +2376,7 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table, TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, ulonglong select_options, ha_rows rows_limit, - const char* alias, bool do_not_open=FALSE, + const LEX_CSTRING *alias, bool do_not_open=FALSE, bool keep_row_order= FALSE); void free_tmp_table(THD *thd, TABLE *entry); bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table, @@ -2336,6 +2396,11 @@ bool open_tmp_table(TABLE *table); void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps); double prev_record_reads(POSITION *positions, uint idx, table_map found_ref); void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size); +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size); +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts); struct st_cond_statistic { diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index 540b5cb7f09..de7ec26eebb 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -15,6 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_class.h" #include "sql_list.h" #include "sql_sequence.h" @@ -53,7 +54,7 @@ static Field_definition sequence_structure[]= {"maximum_value", 21, &type_handler_longlong, STRING_WITH_LEN(""), FL}, {"start_value", 21, &type_handler_longlong, {STRING_WITH_LEN("start value when sequences is created or value if RESTART is used")}, FL}, {"increment", 21, &type_handler_longlong, - {C_STRING_WITH_LEN("increment value")}, FL}, + {STRING_WITH_LEN("increment value")}, FL}, {"cache_size", 21, &type_handler_longlong, STRING_WITH_LEN(""), FL | UNSIGNED_FLAG}, {"cycle_option", 1, &type_handler_tiny, {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed")}, @@ -219,8 +220,8 @@ bool check_sequence_fields(LEX *lex, List<Create_field> *fields) err: my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0), - lex->select_lex.table_list.first->db, - lex->select_lex.table_list.first->table_name, reason); + lex->select_lex.table_list.first->db.str, + lex->select_lex.table_list.first->table_name.str, reason); DBUG_RETURN(TRUE); } @@ -842,13 +843,13 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) No_such_table_error_handler no_such_table_handler; DBUG_ENTER("Sql_cmd_alter_sequence::execute"); - if (check_access(thd, ALTER_ACL, first_table->db, + if (check_access(thd, ALTER_ACL, first_table->db.str, &first_table->grant.privilege, &first_table->grant.m_internal, 0, 0)) DBUG_RETURN(TRUE); /* purecov: inspected */ - if (check_grant(thd, ALTER_ACL, first_table, FALSE, UINT_MAX, FALSE)) + if (check_grant(thd, ALTER_ACL, first_table, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ if (if_exists()) @@ -864,9 +865,9 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) if (trapped_errors) { StringBuffer<FN_REFLEN> tbl_name; - tbl_name.append(first_table->db); + tbl_name.append(&first_table->db); tbl_name.append('.'); - tbl_name.append(first_table->table_name); + tbl_name.append(&first_table->table_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_SEQUENCES, ER_THD(thd, ER_UNKNOWN_SEQUENCES), @@ -908,8 +909,8 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) if (new_seq->check_and_adjust(0)) { my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), - first_table->db, - first_table->table_name); + first_table->db.str, + first_table->table_name.str); error= 1; goto end; } diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 58c5bb7dd78..353d1f551a5 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -33,7 +33,7 @@ currently running transactions etc will not be disrupted. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_servers.h" #include "unireg.h" @@ -54,6 +54,8 @@ static HASH servers_cache; static MEM_ROOT mem; static mysql_rwlock_t THR_LOCK_servers; +static LEX_CSTRING MYSQL_SERVERS_NAME= {STRING_WITH_LEN("servers") }; + static bool get_server_from_table_to_cache(TABLE *table); @@ -83,7 +85,7 @@ static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length, my_bool not_used __attribute__((unused))) { DBUG_ENTER("servers_cache_get_key"); - DBUG_PRINT("info", ("server_name_length %d server_name %s", + DBUG_PRINT("info", ("server_name_length %zd server_name %s", server->server_name_length, server->server_name)); @@ -209,7 +211,7 @@ static bool servers_load(THD *thd, TABLE_LIST *tables) if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL, 1,0, FALSE)) DBUG_RETURN(1); - while (!(read_record_info.read_record(&read_record_info))) + while (!(read_record_info.read_record())) { /* return_val is already TRUE, so no need to set */ if ((get_server_from_table_to_cache(table))) @@ -252,7 +254,7 @@ bool servers_reload(THD *thd) DBUG_PRINT("info", ("locking servers_cache")); mysql_rwlock_wrlock(&THR_LOCK_servers); - tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ); + tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_READ); if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { @@ -384,10 +386,9 @@ insert_server(THD *thd, FOREIGN_SERVER *server) int error= -1; TABLE_LIST tables; TABLE *table; - DBUG_ENTER("insert_server"); - tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) @@ -432,7 +433,7 @@ insert_server_record_into_cache(FOREIGN_SERVER *server) We succeded in insertion of the server to the table, now insert the server to the cache */ - DBUG_PRINT("info", ("inserting server %s at %p, length %d", + DBUG_PRINT("info", ("inserting server %s at %p, length %zd", server->server_name, server, server->server_name_length)); if (my_hash_insert(&servers_cache, (uchar*) server)) @@ -604,7 +605,7 @@ static int drop_server_internal(THD *thd, LEX_SERVER_OPTIONS *server_options) DBUG_PRINT("info", ("server name server->server_name %s", server_options->server_name.str)); - tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE); /* hit the memory hit first */ if ((error= delete_server_record_in_cache(server_options))) @@ -688,7 +689,7 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options) We succeded in deletion of the server to the table, now delete the server from the cache */ - DBUG_PRINT("info",("deleting server %s length %d", + DBUG_PRINT("info",("deleting server %s length %zd", server->server_name, server->server_name_length)); @@ -735,8 +736,7 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) TABLE_LIST tables; DBUG_ENTER("update_server"); - tables.init_one_table("mysql", 5, "servers", 7, "servers", - TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE); if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) { diff --git a/sql/sql_servers.h b/sql/sql_servers.h index d5668f0dfcb..b2fa40cef27 100644 --- a/sql/sql_servers.h +++ b/sql/sql_servers.h @@ -16,7 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* uint */ #include "slave.h" // for tables_ok(), rpl_filter class THD; @@ -28,7 +27,7 @@ typedef struct st_federated_server { const char *server_name; long port; - uint server_name_length; + size_t server_name_length; const char *db, *scheme, *username, *password, *socket, *owner, *host, *sport; } FOREIGN_SERVER; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index fb0d871e7b8..6d5c4adc521 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -17,7 +17,7 @@ /* Function with list databases, tables or fields */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" // SHOW_MY_BOOL #include "sql_priv.h" #include "unireg.h" #include "sql_acl.h" // fill_schema_*_privileges @@ -62,6 +62,8 @@ #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif +#include "vtmd.h" +#include "transaction.h" enum enum_i_s_events_fields { @@ -172,7 +174,7 @@ static bool is_show_command(THD *thd) static int make_version_string(char *buf, int buf_length, uint version) { - return my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff); + return (int)my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff); } @@ -591,6 +593,7 @@ static struct show_privileges_st sys_privileges[]= {"Create view", "Tables", "To create new views"}, {"Create user", "Server Admin", "To create new users"}, {"Delete", "Tables", "To delete existing rows"}, + {"Delete versioning rows", "Tables", "To delete versioning table historical rows"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, #ifdef HAVE_EVENT_SCHEDULER {"Event","Server Admin","To create, alter, drop and execute events"}, @@ -1082,6 +1085,17 @@ find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, if (is_show_command(thd)) #endif tl.sort(); +#ifndef DBUG_OFF + else + { + /* + sort_desc() is used to find easier unstable mtr tests that query + INFORMATION_SCHEMA.{SCHEMATA|TABLES} without a proper ORDER BY. + This can be removed in some release after 10.3 (e.g. in 10.4). + */ + tl.sort_desc(); + } +#endif DBUG_PRINT("info",("found: %zu files", files->elements())); my_dirend(dirp); @@ -1233,8 +1247,8 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, bool error= TRUE; MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("mysqld_show_create_get_fields"); - DBUG_PRINT("enter",("db: %s table: %s",table_list->db, - table_list->table_name)); + DBUG_PRINT("enter",("db: %s table: %s",table_list->db.str, + table_list->table_name.str)); /* We want to preserve the tree for views. */ thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; @@ -1262,14 +1276,14 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, if (thd->lex->table_type == TABLE_TYPE_VIEW && !table_list->view) { my_error(ER_WRONG_OBJECT, MYF(0), - table_list->db, table_list->table_name, "VIEW"); + table_list->db.str, table_list->table_name.str, "VIEW"); goto exit; } else if (thd->lex->table_type == TABLE_TYPE_SEQUENCE && table_list->table->s->table_type != TABLE_TYPE_SEQUENCE) { my_error(ER_NOT_SEQUENCE, MYF(0), - table_list->db, table_list->table_name); + table_list->db.str, table_list->table_name.str); goto exit; } @@ -1345,8 +1359,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) List<Item> field_list; bool error= TRUE; DBUG_ENTER("mysqld_show_create"); - DBUG_PRINT("enter",("db: %s table: %s",table_list->db, - table_list->table_name)); + DBUG_PRINT("enter",("db: %s table: %s",table_list->db.str, + table_list->table_name.str)); /* Metadata locks taken during SHOW CREATE should be released when @@ -1354,6 +1368,15 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST archive; + bool versioned= table_list->vers_conditions; + if (versioned) + { + DBUG_ASSERT(table_list->vers_conditions == SYSTEM_TIME_AS_OF); + VTMD_table vtmd(*table_list); + if (vtmd.setup_select(thd)) + goto exit; + } if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) goto exit; @@ -1369,8 +1392,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) else { if (table_list->schema_table) - protocol->store(table_list->schema_table->table_name, - system_charset_info); + protocol->store(table_list->schema_table->table_name, system_charset_info); else protocol->store(table_list->table->alias.c_ptr(), system_charset_info); } @@ -1396,6 +1418,13 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) my_eof(thd); exit: + if (versioned) + { + /* If commit fails, we should be able to reset the OK status. */ + thd->get_stmt_da()->set_overwrite_status(true); + trans_commit_stmt(thd); + thd->get_stmt_da()->set_overwrite_status(false); + } close_thread_tables(thd); /* Release any metadata locks taken during SHOW CREATE. */ thd->mdl_context.rollback_to_savepoint(mdl_savepoint); @@ -1451,7 +1480,7 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname, DBUG_RETURN(TRUE); } #endif - if (is_infoschema_db(dbname->str)) + if (is_infoschema_db(dbname)) { *dbname= INFORMATION_SCHEMA_NAME; create.default_table_charset= system_charset_info; @@ -1480,7 +1509,7 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname, buffer.append(STRING_WITH_LEN("CREATE DATABASE ")); if (options.if_not_exists()) buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ ")); - append_identifier(thd, &buffer, dbname->str, dbname->length); + append_identifier(thd, &buffer, dbname); if (create.default_table_charset) { @@ -1515,7 +1544,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) TABLE *table; MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("mysqld_list_fields"); - DBUG_PRINT("enter",("table: %s",table_list->table_name)); + DBUG_PRINT("enter",("table: %s", table_list->table_name.str)); if (open_normal_and_derived_tables(thd, table_list, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL, @@ -1598,7 +1627,7 @@ static const char *require_quotes(const char *name, uint name_length) */ bool -append_identifier(THD *thd, String *packet, const char *name, uint length) +append_identifier(THD *thd, String *packet, const char *name, size_t length) { const char *name_end; char quote_char; @@ -1677,11 +1706,11 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) # Quote character */ -int get_quote_char_for_identifier(THD *thd, const char *name, uint length) +int get_quote_char_for_identifier(THD *thd, const char *name, size_t length) { if (length && - !is_keyword(name,length) && - !require_quotes(name, length) && + !is_keyword(name,(uint)length) && + !require_quotes(name, (uint)length) && !(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE)) return EOF; if (thd->variables.sql_mode & MODE_ANSI_QUOTES) @@ -1697,7 +1726,7 @@ static void append_directory(THD *thd, String *packet, const char *dir_type, { if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) { - uint length= dirname_length(filename); + size_t length= dirname_length(filename); packet->append(' '); packet->append(dir_type); packet->append(STRING_WITH_LEN(" DIRECTORY='")); @@ -1758,6 +1787,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, has_default= (field->default_value || (!(field->flags & NO_DEFAULT_VALUE_FLAG) && + !field->vers_sys_field() && field->unireg_check != Field::NEXT_NUMBER)); def_value->length(0); @@ -1854,12 +1884,12 @@ static void append_create_options(THD *thd, String *packet, DBUG_ASSERT(opt->value.str); packet->append(' '); - append_identifier(thd, packet, opt->name.str, opt->name.length); + append_identifier(thd, packet, &opt->name); packet->append('='); if (opt->quoted_value) append_unescaped(packet, opt->value.str, opt->value.length); else - packet->append(opt->value.str, opt->value.length); + packet->append(&opt->value); } if (in_comment) packet->append(STRING_WITH_LEN(" */")); @@ -2068,7 +2098,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { List<Item> field_list; char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH]; - const char *alias; + LEX_CSTRING alias; String type; String def_value; Field **ptr,*field; @@ -2077,6 +2107,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, TABLE *table= table_list->table; TABLE_SHARE *share= table->s; sql_mode_t sql_mode= thd->variables.sql_mode; + bool explicit_fields= false; bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | MODE_DB2 | MODE_MAXDB | MODE_ANSI); @@ -2113,15 +2144,19 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, if (create_info_arg && create_info_arg->if_not_exists()) packet->append(STRING_WITH_LEN("IF NOT EXISTS ")); if (table_list->schema_table) - alias= table_list->schema_table->table_name; + { + alias.str= table_list->schema_table->table_name; + alias.length= strlen(alias.str); + } else { - if (lower_case_table_names == 2) - alias= table->alias.c_ptr(); - else + if (lower_case_table_names == 2 || table_list->vers_force_alias) { - alias= share->table_name.str; + alias.str= table->alias.c_ptr(); + alias.length= table->alias.length(); } + else + alias= share->table_name; } /* @@ -2135,14 +2170,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { const LEX_CSTRING *const db= table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db; - if (!thd->db || strcmp(db->str, thd->db)) + if (!thd->db.str || cmp(db, &thd->db)) { - append_identifier(thd, packet, db->str, db->length); + append_identifier(thd, packet, db); packet->append(STRING_WITH_LEN(".")); } } - append_identifier(thd, packet, alias, strlen(alias)); + append_identifier(thd, packet, &alias); packet->append(STRING_WITH_LEN(" (\n")); /* We need this to get default values from the table @@ -2151,16 +2186,20 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, */ old_map= tmp_use_all_columns(table, table->read_set); + bool not_the_first_field= false; for (ptr=table->field ; (field= *ptr); ptr++) { + uint flags = field->flags; - if (ptr != table->field) + if (field->invisible > INVISIBLE_USER) + continue; + if (not_the_first_field) packet->append(STRING_WITH_LEN(",\n")); + not_the_first_field= true; packet->append(STRING_WITH_LEN(" ")); - append_identifier(thd,packet,field->field_name.str, - field->field_name.length); + append_identifier(thd, packet, &field->field_name); packet->append(' '); type.set(tmp, sizeof(tmp), system_charset_info); @@ -2199,7 +2238,15 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } else { - if (flags & NOT_NULL_FLAG) + if (field->flags & VERS_SYS_START_FLAG) + { + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START")); + } + else if (field->flags & VERS_SYS_END_FLAG) + { + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END")); + } + else if (flags & NOT_NULL_FLAG) packet->append(STRING_WITH_LEN(" NOT NULL")); else if (field->type() == MYSQL_TYPE_TIMESTAMP) { @@ -2210,6 +2257,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" NULL")); } + if (field->invisible == INVISIBLE_USER) + { + packet->append(STRING_WITH_LEN(" INVISIBLE")); + } def_value.set(def_value_buf, sizeof(def_value_buf), system_charset_info); if (get_field_default_value(thd, field, &def_value, 1)) { @@ -2217,6 +2268,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(def_value.ptr(), def_value.length(), system_charset_info); } + if (field->vers_update_unversioned()) + { + packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING")); + } + if (!limited_mysql_mode && print_on_update_clause(field, &def_value, false)) { @@ -2251,11 +2307,13 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, for (uint i=0 ; i < share->keys ; i++,key_info++) { + if (key_info->flags & HA_INVISIBLE_KEY) + continue; KEY_PART_INFO *key_part= key_info->key_part; bool found_primary=0; packet->append(STRING_WITH_LEN(",\n ")); - if (i == primary_key && !strcmp(key_info->name, primary_key_name)) + if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) { found_primary=1; /* @@ -2274,18 +2332,21 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("KEY ")); if (!found_primary) - append_identifier(thd, packet, key_info->name, strlen(key_info->name)); + append_identifier(thd, packet, &key_info->name); packet->append(STRING_WITH_LEN(" (")); for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) { + Field *field= key_part->field; + if (field->invisible > INVISIBLE_USER) + continue; + if (j) packet->append(','); if (key_part->field) - append_identifier(thd,packet, key_part->field->field_name.str, - key_part->field->field_name.length); + append_identifier(thd, packet, &key_part->field->field_name); if (key_part->field && (key_part->length != table->field[key_part->fieldnr-1]->key_length() && @@ -2301,13 +2362,36 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { LEX_CSTRING *parser_name= plugin_name(key_info->parser); packet->append(STRING_WITH_LEN(" /*!50100 WITH PARSER ")); - append_identifier(thd, packet, parser_name->str, parser_name->length); + append_identifier(thd, packet, parser_name); packet->append(STRING_WITH_LEN(" */ ")); } append_create_options(thd, packet, key_info->option_list, check_options, hton->index_options); } + if (table->versioned()) + { + const Field *fs = table->vers_start_field(); + const Field *fe = table->vers_end_field(); + DBUG_ASSERT(fs); + DBUG_ASSERT(fe); + explicit_fields= fs->invisible < INVISIBLE_SYSTEM; + DBUG_ASSERT(!explicit_fields || fe->invisible < INVISIBLE_SYSTEM); + if (explicit_fields) + { + packet->append(STRING_WITH_LEN(",\n PERIOD FOR SYSTEM_TIME (")); + append_identifier(thd,packet,fs->field_name.str, fs->field_name.length); + packet->append(STRING_WITH_LEN(", ")); + append_identifier(thd,packet,fe->field_name.str, fe->field_name.length); + packet->append(STRING_WITH_LEN(")")); + } + else + { + DBUG_ASSERT(fs->invisible == INVISIBLE_SYSTEM); + DBUG_ASSERT(fe->invisible == INVISIBLE_SYSTEM); + } + } + /* Get possible foreign key definitions stored in InnoDB and append them to the CREATE TABLE statement @@ -2330,10 +2414,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, check->print(&str); packet->append(STRING_WITH_LEN(",\n ")); - if (check->name.length) + if (check->name.str) { packet->append(STRING_WITH_LEN("CONSTRAINT ")); - append_identifier(thd, packet, check->name.str, check->name.length); + append_identifier(thd, packet, &check->name); } packet->append(STRING_WITH_LEN(" CHECK (")); packet->append(str); @@ -2346,6 +2430,9 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); + if (table->versioned()) + packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); + #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && @@ -2474,11 +2561,11 @@ void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user, const LEX_CSTRING *definer_host) { buffer->append(STRING_WITH_LEN("DEFINER=")); - append_identifier(thd, buffer, definer_user->str, definer_user->length); + append_identifier(thd, buffer, definer_user); if (definer_host->str && definer_host->str[0]) { buffer->append('@'); - append_identifier(thd, buffer, definer_host->str, definer_host->length); + append_identifier(thd, buffer, definer_host); } buffer->append(' '); } @@ -2494,7 +2581,7 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) MODE_MAXDB | MODE_ANSI)) != 0; - if (!thd->db || strcmp(thd->db, table->view_db.str)) + if (!thd->db.str || cmp(&thd->db, &table->view_db)) /* print compact view name if the view belongs to the current database */ @@ -2511,7 +2598,7 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) tbl; tbl= tbl->next_global) { - if (strcmp(table->view_db.str, tbl->view ? tbl->view_db.str :tbl->db)!= 0) + if (cmp(&table->view_db, tbl->view ? &tbl->view_db : &tbl->db)) { table->compact_view_format= FALSE; break; @@ -2527,10 +2614,10 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) buff->append(STRING_WITH_LEN("VIEW ")); if (!compact_view_name) { - append_identifier(thd, buff, table->view_db.str, table->view_db.length); + append_identifier(thd, buff, &table->view_db); buff->append('.'); } - append_identifier(thd, buff, table->view_name.str, table->view_name.length); + append_identifier(thd, buff, &table->view_name); buff->append(STRING_WITH_LEN(" AS ")); /* @@ -2573,7 +2660,7 @@ static int show_create_sequence(THD *thd, TABLE_LIST *table_list, alias= table->s->table_name; packet->append(STRING_WITH_LEN("CREATE SEQUENCE ")); - append_identifier(thd, packet, alias.str, alias.length); + append_identifier(thd, packet, &alias); packet->append(STRING_WITH_LEN(" start with ")); packet->append_longlong(seq->start); packet->append(STRING_WITH_LEN(" minvalue ")); @@ -2612,6 +2699,7 @@ public: static void operator delete(void *ptr __attribute__((unused)), size_t size __attribute__((unused))) { TRASH_FREE(ptr, size); } + static void operator delete(void *, MEM_ROOT *){} my_thread_id thread_id; uint32 os_thread_id; @@ -2747,8 +2835,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) The following variables are only safe to access under a lock */ - if ((thd_info->db= tmp->db)) // Safe test - thd_info->db= thd->strdup(thd_info->db); + thd_info->db= 0; + if (tmp->db.str) + thd_info->db= thd->strmake(tmp->db.str, tmp->db.length); if (tmp->query()) { @@ -3108,7 +3197,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) while ((tmp= it++)) { Security_context *tmp_sctx= tmp->security_ctx; - const char *val, *db; + const char *val; ulonglong max_counter; bool got_thd_data; @@ -3140,9 +3229,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) { /* DB */ - if ((db= tmp->db)) + if (tmp->db.str) { - table->field[3]->store(db, strlen(db), cs); + table->field[3]->store(tmp->db.str, tmp->db.length, cs); table->field[3]->set_notnull(); } } @@ -3481,6 +3570,9 @@ const char* get_one_variable(THD *thd, case SHOW_MY_BOOL: end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); break; + case SHOW_UINT32_STATUS: + value= ((char *) status_var + (intptr) value); + /* fall through */ case SHOW_UINT: end= int10_to_str((long) *(uint*) value, buff, 10); break; @@ -3707,6 +3799,8 @@ uint calc_sum_of_all_status(STATUS_VAR *to) add_to_status(to, &tmp->status_var); to->local_memory_used+= tmp->status_var.local_memory_used; } + if (tmp->get_command() != COM_SLEEP) + to->threads_running++; } mysql_mutex_unlock(&LOCK_thread_count); @@ -4052,7 +4146,7 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables, case SQLCOM_SHOW_TRIGGERS: case SQLCOM_SHOW_EVENTS: thd->make_lex_string(&lookup_field_values->db_value, - lex->select_lex.db, strlen(lex->select_lex.db)); + lex->select_lex.db.str, lex->select_lex.db.length); if (wild) { thd->make_lex_string(&lookup_field_values->table_value, @@ -4150,8 +4244,7 @@ static int make_db_list(THD *thd, Dynamic_array<LEX_CSTRING*> *files, return 0; } - if (is_infoschema_db(lookup_field_vals->db_value.str, - lookup_field_vals->db_value.length)) + if (is_infoschema_db(&lookup_field_vals->db_value)) { if (files->append_val(&INFORMATION_SCHEMA_NAME)) return 1; @@ -4272,7 +4365,7 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_CSTRING*> *files, @retval 2 Not fatal error; Safe to ignore this file list */ -static int +int make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names, LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, LEX_CSTRING *db_name) @@ -4294,7 +4387,7 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names, { LEX_CSTRING *name; ST_SCHEMA_TABLE *schema_table= - find_schema_table(thd, lookup_field_vals->table_value.str); + find_schema_table(thd, &lookup_field_vals->table_value); if (schema_table && !schema_table->hidden) { if (!(name= thd->make_clex_string(schema_table->table_name, @@ -4574,8 +4667,7 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, CHARSET_INFO *cs= system_charset_info; handlerton *hton; bool is_sequence; - if (ha_table_exists(thd, db_name->str, table_name->str, &hton, - &is_sequence)) + if (ha_table_exists(thd, db_name, table_name, &hton, &is_sequence)) { if (hton == view_pseudo_hton) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); @@ -4677,7 +4769,7 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, bool can_deadlock) { bool error; - table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str, MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION); if (can_deadlock) @@ -4756,15 +4848,15 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table, */ strmov(db_name_buff, db_name->str); strmov(table_name_buff, table_name->str); - my_casedn_str(files_charset_info, db_name_buff); - my_casedn_str(files_charset_info, table_name_buff); - table_list.db= db_name_buff; - table_list.table_name= table_name_buff; + table_list.db.length= my_casedn_str(files_charset_info, db_name_buff); + table_list.table_name.length= my_casedn_str(files_charset_info, table_name_buff); + table_list.db.str= db_name_buff; + table_list.table_name.str= table_name_buff; } else { - table_list.table_name= table_name->str; - table_list.db= db_name->str; + table_list.table_name= *table_name; + table_list.db= *db_name; } /* @@ -4794,7 +4886,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_I_S_SKIPPED_TABLE, ER_THD(thd, ER_WARN_I_S_SKIPPED_TABLE), - table_list.db, table_list.table_name); + table_list.db.str, table_list.table_name.str); return 0; } @@ -4802,8 +4894,8 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table, { init_sql_alloc(&tbl.mem_root, "fill_schema_table_from_frm", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); - if (!Table_triggers_list::check_n_load(thd, db_name->str, - table_name->str, &tbl, 1)) + if (!Table_triggers_list::check_n_load(thd, db_name, + table_name, &tbl, 1)) { table_list.table= &tbl; res= schema_table->process_table(thd, &table_list, table, @@ -4860,7 +4952,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table, goto end_share; } - if (!open_table_from_share(thd, share, table_name->str, 0, + if (!open_table_from_share(thd, share, table_name, 0, (EXTRA_RECORD | OPEN_FRM_FILE_ONLY), thd->open_options, &tbl, FALSE)) { @@ -4925,6 +5017,59 @@ public: } }; +static bool get_all_archive_tables(THD *thd, + Dynamic_array<String> &all_archive_tables) +{ + if (thd->variables.vers_alter_history != VERS_ALTER_HISTORY_SURVIVE) + return false; + + Dynamic_array<LEX_CSTRING *> all_db; + LOOKUP_FIELD_VALUES lookup_field_values= { + {C_STRING_WITH_LEN("%")}, {NULL, 0}, true, false}; + if (make_db_list(thd, &all_db, &lookup_field_values)) + return true; + + LEX_STRING information_schema= {C_STRING_WITH_LEN("information_schema")}; + for (size_t i= 0; i < all_db.elements(); i++) + { + LEX_CSTRING db= *all_db.at(i); + if (db.length == information_schema.length && + !memcmp(db.str, information_schema.str, db.length)) + { + all_db.del(i); + break; + } + } + + for (size_t i= 0; i < all_db.elements(); i++) + { + LEX_CSTRING db_name= *all_db.at(i); + Dynamic_array<String> archive_tables; + if (VTMD_table::get_archive_tables(thd, db_name.str, db_name.length, + archive_tables)) + return true; + for (size_t i= 0; i < archive_tables.elements(); i++) + if (all_archive_tables.push(archive_tables.at(i))) + return true; + } + + return false; +} + +static bool is_archive_table(const Dynamic_array<String> &all_archive_tables, + const LEX_CSTRING candidate) +{ + for (size_t i= 0; i < all_archive_tables.elements(); i++) + { + const String &archive_table= all_archive_tables.at(i); + if (candidate.length == archive_table.length() && + !memcmp(candidate.str, archive_table.ptr(), candidate.length)) + { + return true; + } + } + return false; +} /** @brief Fill I_S tables whose data are retrieved @@ -4967,6 +5112,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #endif uint table_open_method= tables->table_open_method; bool can_deadlock; + Dynamic_array<String> all_archive_tables; MEM_ROOT tmp_mem_root; DBUG_ENTER("get_all_tables"); @@ -4999,17 +5145,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) */ if (lsel && lsel->table_list.first) { - LEX_CSTRING db_name, table_name; - - db_name.str= lsel->table_list.first->db; - db_name.length= lsel->table_list.first->db_length; - - table_name.str= lsel->table_list.first->table_name; - table_name.length= lsel->table_list.first->table_name_length; - error= fill_schema_table_by_open(thd, thd->mem_root, TRUE, table, schema_table, - &db_name, &table_name, + &lsel->table_list.first->db, + &lsel->table_list.first->table_name, &open_tables_state_backup, can_deadlock); goto err; @@ -5033,6 +5172,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (make_db_list(thd, &db_names, &plan->lookup_field_vals)) goto err; + if (get_all_archive_tables(thd, all_archive_tables)) + goto err; + /* Use tmp_mem_root to allocate data for opened tables */ init_alloc_root(&tmp_mem_root, "get_all_tables", SHOW_ALLOC_BLOCK_SIZE, SHOW_ALLOC_BLOCK_SIZE, MY_THREAD_SPECIFIC); @@ -5062,13 +5204,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) LEX_CSTRING *table_name= table_names.at(i); DBUG_ASSERT(table_name->length <= NAME_LEN); + if (is_archive_table(all_archive_tables, *table_name)) + continue; + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!(thd->col_access & TABLE_ACLS)) { - table_acl_check.db= db_name->str; - table_acl_check.db_length= db_name->length; - table_acl_check.table_name= table_name->str; - table_acl_check.table_name_length= table_name->length; + table_acl_check.db= *db_name; + table_acl_check.table_name= *table_name; table_acl_check.grant.privilege= thd->col_access; if (check_grant(thd, TABLE_ACLS, &table_acl_check, TRUE, 1, TRUE)) continue; @@ -5285,7 +5428,10 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, else { DBUG_ASSERT(share->tmp_table == NO_TMP_TABLE); - table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); + if (share->versioned) + table->field[3]->store(STRING_WITH_LEN("SYSTEM VERSIONED"), cs); + else + table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); } for (uint i= 4; i < table->s->fields; i++) @@ -5708,6 +5854,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, for (; (field= *ptr) ; ptr++) { + if(field->invisible > INVISIBLE_USER) + continue; uchar *pos; char tmp[MAX_FIELD_WIDTH]; String type(tmp,sizeof(tmp), system_charset_info); @@ -5765,11 +5913,11 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[16]->store((const char*) pos, strlen((const char*) pos), cs); + StringBuffer<256> buf; if (field->unireg_check == Field::NEXT_NUMBER) - table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs); + buf.set(STRING_WITH_LEN("auto_increment"),cs); if (print_on_update_clause(field, &type, true)) - table->field[17]->store(type.ptr(), type.length(), cs); - + buf.set(type.ptr(), type.length(),cs); if (field->vcol_info) { String gen_s(tmp,sizeof(tmp), system_charset_info); @@ -5780,13 +5928,29 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[20]->store(STRING_WITH_LEN("ALWAYS"), cs); if (field->vcol_info->stored_in_db) - table->field[17]->store(STRING_WITH_LEN("STORED GENERATED"), cs); + buf.set(STRING_WITH_LEN("STORED GENERATED"), cs); + else + buf.set(STRING_WITH_LEN("VIRTUAL GENERATED"), cs); + } + else if (field->flags & VERS_SYSTEM_FIELD) + { + if (field->flags & VERS_SYS_START_FLAG) + table->field[21]->store(STRING_WITH_LEN("ROW START"), cs); else - table->field[17]->store(STRING_WITH_LEN("VIRTUAL GENERATED"), cs); + table->field[21]->store(STRING_WITH_LEN("ROW END"), cs); + table->field[21]->set_notnull(); + table->field[20]->store(STRING_WITH_LEN("ALWAYS"), cs); } else table->field[20]->store(STRING_WITH_LEN("NEVER"), cs); - + /*Invisible can coexist with auto_increment and virtual */ + if (field->invisible == INVISIBLE_USER) + { + if (buf.length()) + buf.append(STRING_WITH_LEN(", ")); + buf.append(STRING_WITH_LEN("INVISIBLE"),cs); + } + table->field[17]->store(buf.ptr(), buf.length(), cs); table->field[19]->store(field->comment.str, field->comment.length, cs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -5849,7 +6013,7 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin, { restore_record(table, s->default_values); table->field[0]->store(plug->name, strlen(plug->name), scs); - table->field[1]->store(C_STRING_WITH_LEN("NO"), scs); + table->field[1]->store(STRING_WITH_LEN("NO"), scs); table->field[2]->store(plug->descr, strlen(plug->descr), scs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -6273,10 +6437,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) thd->security_ctx->priv_host, NullS); /* We use this TABLE_LIST instance only for checking of privileges. */ bzero((char*) &proc_tables,sizeof(proc_tables)); - proc_tables.db= (char*) "mysql"; - proc_tables.db_length= 5; - proc_tables.table_name= proc_tables.alias= (char*) "proc"; - proc_tables.table_name_length= 4; + proc_tables.db= MYSQL_SCHEMA_NAME; + proc_tables.table_name= MYSQL_PROC_NAME; + proc_tables.alias= MYSQL_PROC_NAME; proc_tables.lock_type= TL_READ; full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE, 1, TRUE); @@ -6366,6 +6529,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, } for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) { + if ((key_info->flags & HA_INVISIBLE_KEY) && + DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + continue; KEY_PART_INFO *key_part= key_info->key_part; LEX_CSTRING *str; LEX_CSTRING unknown= {STRING_WITH_LEN("?unknown field?") }; @@ -6378,7 +6544,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, table->field[3]->store((longlong) ((key_info->flags & HA_NOSAME) ? 0 : 1), TRUE); table->field[4]->store(db_name->str, db_name->length, cs); - table->field[5]->store(key_info->name, strlen(key_info->name), cs); + table->field[5]->store(key_info->name.str, key_info->name.length, cs); table->field[6]->store((longlong) (j+1), TRUE); str= (key_part->field ? &key_part->field->field_name : &unknown); @@ -6583,7 +6749,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, static bool store_constraints(THD *thd, TABLE *table, const LEX_CSTRING *db_name, const LEX_CSTRING *table_name, const char *key_name, - uint key_len, const char *con_type, uint con_len) + size_t key_len, const char *con_type, size_t con_len) { CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); @@ -6626,17 +6792,17 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, if (i != primary_key && !(key_info->flags & HA_NOSAME)) continue; - if (i == primary_key && !strcmp(key_info->name, primary_key_name)) + if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) { - if (store_constraints(thd, table, db_name, table_name, key_info->name, - strlen(key_info->name), + if (store_constraints(thd, table, db_name, table_name, + key_info->name.str, key_info->name.length, STRING_WITH_LEN("PRIMARY KEY"))) DBUG_RETURN(1); } else if (key_info->flags & HA_NOSAME) { - if (store_constraints(thd, table, db_name, table_name, key_info->name, - strlen(key_info->name), + if (store_constraints(thd, table, db_name, table_name, + key_info->name.str, key_info->name.length, STRING_WITH_LEN("UNIQUE"))) DBUG_RETURN(1); } @@ -6779,7 +6945,7 @@ ret: static void store_key_column_usage(TABLE *table, const LEX_CSTRING *db_name, const LEX_CSTRING *table_name, const char *key_name, - uint key_len, const char *con_type, uint con_len, + size_t key_len, const char *con_type, size_t con_len, longlong idx) { CHARSET_INFO *cs= system_charset_info; @@ -6832,8 +6998,7 @@ static int get_schema_key_column_usage_record(THD *thd, f_idx++; restore_record(table, s->default_values); store_key_column_usage(table, db_name, table_name, - key_info->name, - strlen(key_info->name), + key_info->name.str, key_info->name.length, key_part->field->field_name.str, key_part->field->field_name.length, (longlong) f_idx); @@ -7139,6 +7304,10 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, tmp_res.append(STRING_WITH_LEN("HASH")); table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); break; + case VERSIONING_PARTITION: + tmp_res.length(0); + tmp_res.append(STRING_WITH_LEN("SYSTEM_TIME")); + break; default: DBUG_ASSERT(0); my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); @@ -7232,7 +7401,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, tmp_res.length(0); if (part_elem->has_null_value) { - tmp_str.append("NULL"); + tmp_str.append(STRING_WITH_LEN("NULL")); if (num_items > 0) tmp_str.append(","); } @@ -7241,7 +7410,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, if (part_info->column_list) { if (part_info->part_field_list.elements > 1U) - tmp_str.append("("); + tmp_str.append(STRING_WITH_LEN("(")); if (get_partition_column_description(thd, part_info, list_value, @@ -7498,7 +7667,7 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) TABLE *table= tables->table; CHARSET_INFO *cs= system_charset_info; OPEN_TABLE_LIST *open_list; - if (!(open_list=list_open_tables(thd,thd->lex->select_lex.db, wild)) + if (!(open_list=list_open_tables(thd,thd->lex->select_lex.db.str, wild)) && thd->is_fatal_error) DBUG_RETURN(1); @@ -7718,7 +7887,8 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin, if (!my_strcasecmp(system_charset_info, schema_table->table_name, - table_name)) { + table_name)) + { my_plugin_lock(thd, plugin); p_schema_table->schema_table= schema_table; DBUG_RETURN(1); @@ -7729,7 +7899,7 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin, /* - Find schema_tables elment by name + Find schema_tables element by name SYNOPSIS find_schema_table() @@ -7741,7 +7911,7 @@ static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin, # pointer to 'schema_tables' element */ -ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name, +ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name, bool *in_plugin) { schema_table_ref schema_table_a; @@ -7753,12 +7923,12 @@ ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name, { if (!my_strcasecmp(system_charset_info, schema_table->table_name, - table_name)) + table_name->str)) DBUG_RETURN(schema_table); } *in_plugin= true; - schema_table_a.table_name= table_name; + schema_table_a.table_name= table_name->str; if (plugin_foreach(thd, find_schema_table_in_plugin, MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a)) DBUG_RETURN(schema_table_a.schema_table); @@ -7808,7 +7978,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) for (; fields_info->field_name; fields_info++) { - uint field_name_length= strlen(fields_info->field_name); + size_t field_name_length= strlen(fields_info->field_name); switch (fields_info->field_type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_LONG: @@ -7828,14 +7998,14 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) case MYSQL_TYPE_DATE: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - field_name_length, + (uint)field_name_length, fields_info->field_type))) DBUG_RETURN(0); break; case MYSQL_TYPE_TIME: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - field_name_length, + (uint)field_name_length, fields_info->field_type))) DBUG_RETURN(0); break; @@ -7843,8 +8013,9 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) case MYSQL_TYPE_DATETIME: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - field_name_length, - fields_info->field_type))) + (uint)field_name_length, + fields_info->field_type, + fields_info->field_length))) DBUG_RETURN(0); item->decimals= fields_info->field_length; break; @@ -7918,7 +8089,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) field_list, (ORDER*) 0, 0, 0, (select_lex->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR, - table_list->alias, false, keep_row_order))) + &table_list->alias, false, keep_row_order))) DBUG_RETURN(0); my_bitmap_map* bitmaps= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); @@ -8016,7 +8187,7 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) buffer.length(0); buffer.append(field_info->old_name); - buffer.append(lex->select_lex.db); + buffer.append(&lex->select_lex.db); if (lex->wild && lex->wild->ptr()) { buffer.append(STRING_WITH_LEN(" (")); @@ -8158,12 +8329,11 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) views working correctly */ - if (table_list->schema_table_name) + if (table_list->schema_table_name.str) table->alias_name_used= my_strcasecmp(table_alias_charset, - table_list->schema_table_name, - table_list->alias); - table_list->table_name= table->s->table_name.str; - table_list->table_name_length= table->s->table_name.length; + table_list->schema_table_name.str, + table_list->alias.str); + table_list->table_name= table->s->table_name; table_list->table= table; table->next= thd->derived_tables; thd->derived_tables= table; @@ -8564,7 +8734,7 @@ int hton_fill_schema_table(THD *thd, TABLE_LIST *tables, COND *cond) static int store_key_cache_table_record(THD *thd, TABLE *table, - const char *name, uint name_length, + const char *name, size_t name_length, KEY_CACHE *key_cache, uint partitions, uint partition_no) { @@ -9662,7 +9832,7 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger) fields.push_back(new (mem_root) Item_empty_string(thd, "Trigger", NAME_LEN), mem_root); fields.push_back(new (mem_root) - Item_empty_string(thd, "sql_mode", trg_sql_mode_str.length), + Item_empty_string(thd, "sql_mode", (uint)trg_sql_mode_str.length), mem_root); { @@ -9673,7 +9843,7 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger) Item_empty_string *stmt_fld= new (mem_root) Item_empty_string(thd, "SQL Original Statement", - MY_MAX(trg_sql_original_stmt.length, + (uint)MY_MAX(trg_sql_original_stmt.length, 1024)); stmt_fld->maybe_null= TRUE; @@ -9783,12 +9953,12 @@ static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) { char trn_path_buff[FN_REFLEN]; - LEX_STRING trn_path= { trn_path_buff, 0 }; + LEX_CSTRING trn_path= { trn_path_buff, 0 }; LEX_CSTRING db; LEX_CSTRING tbl_name; TABLE_LIST *table; - build_trn_path(thd, trg_name, &trn_path); + build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path); if (check_trn_exists(&trn_path)) { @@ -9796,8 +9966,7 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) return NULL; } - if (load_table_name_for_trigger(thd, trg_name, (LEX_CSTRING*) &trn_path, - &tbl_name)) + if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) return NULL; /* We need to reset statement table list to be PS/SP friendly. */ @@ -9815,8 +9984,7 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) if (db.str == NULL || tbl_name.str == NULL) return NULL; - table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length, - tbl_name.str, TL_IGNORE); + table->init_one_table(&db, &tbl_name, 0, TL_IGNORE); return table; } @@ -9864,7 +10032,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) { my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), (const char *) trg_name->m_db.str, - (const char *) lst->table_name); + (const char *) lst->table_name.str); goto exit; @@ -9885,8 +10053,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) { my_error(ER_TRG_CORRUPTED_FILE, MYF(0), (const char *) trg_name->m_db.str, - (const char *) lst->table_name); - + (const char *) lst->table_name.str); goto exit; } diff --git a/sql/sql_show.h b/sql/sql_show.h index dc2fe9738fe..f6d5d4d2c3c 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -82,8 +82,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table); -bool append_identifier(THD *thd, String *packet, const char *name, - uint length); +bool append_identifier(THD *thd, String *packet, const char *name, size_t length); +static inline bool append_identifier(THD *thd, String *packet, const LEX_CSTRING *name) +{ + return append_identifier(thd, packet, name->str, name->length); +} void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, @@ -118,8 +121,9 @@ bool schema_table_store_record(THD *thd, TABLE *table); void initialize_information_schema_acl(); COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table); -ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name, bool *in_plugin); -static inline ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name) +ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name, + bool *in_plugin); +static inline ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name) { bool unused; return find_schema_table(thd, table_name, &unused); } ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx); @@ -138,7 +142,7 @@ const char* get_one_variable(THD *thd, const SHOW_VAR *variable, size_t *length); /* These functions were under INNODB_COMPATIBILITY_HOOKS */ -int get_quote_char_for_identifier(THD *thd, const char *name, uint length); +int get_quote_char_for_identifier(THD *thd, const char *name, size_t length); THD *find_thread_by_id(longlong id, bool query_id= false); class select_result_explain_buffer; @@ -199,6 +203,9 @@ typedef struct st_lookup_field_values bool wild_table_value; } LOOKUP_FIELD_VALUES; +int make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING *> *table_names, + LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, + LEX_CSTRING *db_name); /* INFORMATION_SCHEMA: Execution plan for get_all_tables() call diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index 6a57b8fc9ce..1da8b145151 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sp_head.h" #include "sp_pcontext.h" @@ -29,38 +29,38 @@ */ #define MAX_MYSQL_ERRNO 65534 -const LEX_STRING Diag_condition_item_names[]= +const LEX_CSTRING Diag_condition_item_names[]= { - { C_STRING_WITH_LEN("CLASS_ORIGIN") }, - { C_STRING_WITH_LEN("SUBCLASS_ORIGIN") }, - { C_STRING_WITH_LEN("CONSTRAINT_CATALOG") }, - { C_STRING_WITH_LEN("CONSTRAINT_SCHEMA") }, - { C_STRING_WITH_LEN("CONSTRAINT_NAME") }, - { C_STRING_WITH_LEN("CATALOG_NAME") }, - { C_STRING_WITH_LEN("SCHEMA_NAME") }, - { C_STRING_WITH_LEN("TABLE_NAME") }, - { C_STRING_WITH_LEN("COLUMN_NAME") }, - { C_STRING_WITH_LEN("CURSOR_NAME") }, - { C_STRING_WITH_LEN("MESSAGE_TEXT") }, - { C_STRING_WITH_LEN("MYSQL_ERRNO") }, - - { C_STRING_WITH_LEN("CONDITION_IDENTIFIER") }, - { C_STRING_WITH_LEN("CONDITION_NUMBER") }, - { C_STRING_WITH_LEN("CONNECTION_NAME") }, - { C_STRING_WITH_LEN("MESSAGE_LENGTH") }, - { C_STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") }, - { C_STRING_WITH_LEN("PARAMETER_MODE") }, - { C_STRING_WITH_LEN("PARAMETER_NAME") }, - { C_STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") }, - { C_STRING_WITH_LEN("RETURNED_SQLSTATE") }, - { C_STRING_WITH_LEN("ROUTINE_CATALOG") }, - { C_STRING_WITH_LEN("ROUTINE_NAME") }, - { C_STRING_WITH_LEN("ROUTINE_SCHEMA") }, - { C_STRING_WITH_LEN("SERVER_NAME") }, - { C_STRING_WITH_LEN("SPECIFIC_NAME") }, - { C_STRING_WITH_LEN("TRIGGER_CATALOG") }, - { C_STRING_WITH_LEN("TRIGGER_NAME") }, - { C_STRING_WITH_LEN("TRIGGER_SCHEMA") } + { STRING_WITH_LEN("CLASS_ORIGIN") }, + { STRING_WITH_LEN("SUBCLASS_ORIGIN") }, + { STRING_WITH_LEN("CONSTRAINT_CATALOG") }, + { STRING_WITH_LEN("CONSTRAINT_SCHEMA") }, + { STRING_WITH_LEN("CONSTRAINT_NAME") }, + { STRING_WITH_LEN("CATALOG_NAME") }, + { STRING_WITH_LEN("SCHEMA_NAME") }, + { STRING_WITH_LEN("TABLE_NAME") }, + { STRING_WITH_LEN("COLUMN_NAME") }, + { STRING_WITH_LEN("CURSOR_NAME") }, + { STRING_WITH_LEN("MESSAGE_TEXT") }, + { STRING_WITH_LEN("MYSQL_ERRNO") }, + + { STRING_WITH_LEN("CONDITION_IDENTIFIER") }, + { STRING_WITH_LEN("CONDITION_NUMBER") }, + { STRING_WITH_LEN("CONNECTION_NAME") }, + { STRING_WITH_LEN("MESSAGE_LENGTH") }, + { STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") }, + { STRING_WITH_LEN("PARAMETER_MODE") }, + { STRING_WITH_LEN("PARAMETER_NAME") }, + { STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") }, + { STRING_WITH_LEN("RETURNED_SQLSTATE") }, + { STRING_WITH_LEN("ROUTINE_CATALOG") }, + { STRING_WITH_LEN("ROUTINE_NAME") }, + { STRING_WITH_LEN("ROUTINE_SCHEMA") }, + { STRING_WITH_LEN("SERVER_NAME") }, + { STRING_WITH_LEN("SPECIFIC_NAME") }, + { STRING_WITH_LEN("TRIGGER_CATALOG") }, + { STRING_WITH_LEN("TRIGGER_NAME") }, + { STRING_WITH_LEN("TRIGGER_SCHEMA") } }; @@ -210,7 +210,7 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con int result= 1; enum enum_diag_condition_item_name item_enum; String *member; - const LEX_STRING *name; + const LEX_CSTRING *name; DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations"); diff --git a/sql/sql_sort.h b/sql/sql_sort.h index 6c97ad7e9ab..d57239671a8 100644 --- a/sql/sql_sort.h +++ b/sql/sql_sort.h @@ -17,7 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "my_base.h" /* ha_rows */ -#include "my_sys.h" /* qsort2_cmp */ +#include <my_sys.h> /* qsort2_cmp */ #include "queues.h" typedef struct st_buffpek BUFFPEK; diff --git a/sql/sql_state.c b/sql/sql_state.c index 2bfd61d6696..046868a78a7 100644 --- a/sql/sql_state.c +++ b/sql/sql_state.c @@ -16,7 +16,7 @@ /* Functions to map mysqld errno to sql_state */ -#include <my_global.h> +#include "mariadb.h" #include <mysqld_error.h> #include <my_base.h> diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index e1465d47f72..26fdce12e7f 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -23,7 +23,7 @@ @{ */ -#include <my_global.h> +#include "mariadb.h" #include "sql_base.h" #include "key.h" #include "sql_statistics.h" @@ -65,16 +65,13 @@ static const uint STATISTICS_TABLES= 3; The names of the statistical tables in this array must correspond the definitions of the tables in the file ../scripts/mysql_system_tables.sql */ -static const LEX_STRING stat_table_name[STATISTICS_TABLES]= +static const LEX_CSTRING stat_table_name[STATISTICS_TABLES]= { - { C_STRING_WITH_LEN("table_stats") }, - { C_STRING_WITH_LEN("column_stats") }, - { C_STRING_WITH_LEN("index_stats") } + { STRING_WITH_LEN("table_stats") }, + { STRING_WITH_LEN("column_stats") }, + { STRING_WITH_LEN("index_stats") } }; -/* Name of database to which the statistical tables belong */ -static const LEX_STRING stat_tables_db_name= { C_STRING_WITH_LEN("mysql") }; - /** @details @@ -93,10 +90,9 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write) for (i= 0; i < STATISTICS_TABLES; i++) { - tables[i].db= stat_tables_db_name.str; - tables[i].db_length= stat_tables_db_name.length; - tables[i].alias= tables[i].table_name= stat_table_name[i].str; - tables[i].table_name_length= stat_table_name[i].length; + tables[i].db= MYSQL_SCHEMA_NAME; + tables[i].table_name= stat_table_name[i]; + tables[i].alias= stat_table_name[i]; tables[i].lock_type= for_write ? TL_WRITE : TL_READ; if (i < STATISTICS_TABLES - 1) tables[i].next_global= tables[i].next_local= @@ -115,17 +111,16 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write) otherwise it is set to TL_WRITE. */ -static -inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl, - const LEX_STRING *stat_tab_name, - bool for_write) +static inline +void init_table_list_for_single_stat_table(TABLE_LIST *tbl, + const LEX_CSTRING *stat_tab_name, + bool for_write) { memset((char *) tbl, 0, sizeof(TABLE_LIST)); - tbl->db= stat_tables_db_name.str; - tbl->db_length= stat_tables_db_name.length; - tbl->alias= tbl->table_name= stat_tab_name->str; - tbl->table_name_length= stat_tab_name->length; + tbl->db= MYSQL_SCHEMA_NAME; + tbl->table_name= *stat_tab_name; + tbl->alias= *stat_tab_name; tbl->lock_type= for_write ? TL_WRITE : TL_READ; } @@ -136,18 +131,18 @@ static const TABLE_FIELD_TYPE table_stat_fields[TABLE_STAT_N_FIELDS] = { { - { C_STRING_WITH_LEN("db_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("table_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("table_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("cardinality") }, - { C_STRING_WITH_LEN("bigint(21)") }, + { STRING_WITH_LEN("cardinality") }, + { STRING_WITH_LEN("bigint(21)") }, { NULL, 0 } }, }; @@ -159,58 +154,58 @@ static const TABLE_FIELD_TYPE column_stat_fields[COLUMN_STAT_N_FIELDS] = { { - { C_STRING_WITH_LEN("db_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("table_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("table_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("column_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("column_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("min_value") }, - { C_STRING_WITH_LEN("varbinary(255)") }, + { STRING_WITH_LEN("min_value") }, + { STRING_WITH_LEN("varbinary(255)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("max_value") }, - { C_STRING_WITH_LEN("varbinary(255)") }, + { STRING_WITH_LEN("max_value") }, + { STRING_WITH_LEN("varbinary(255)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("nulls_ratio") }, - { C_STRING_WITH_LEN("decimal(12,4)") }, + { STRING_WITH_LEN("nulls_ratio") }, + { STRING_WITH_LEN("decimal(12,4)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("avg_length") }, - { C_STRING_WITH_LEN("decimal(12,4)") }, + { STRING_WITH_LEN("avg_length") }, + { STRING_WITH_LEN("decimal(12,4)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("avg_frequency") }, - { C_STRING_WITH_LEN("decimal(12,4)") }, + { STRING_WITH_LEN("avg_frequency") }, + { STRING_WITH_LEN("decimal(12,4)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("hist_size") }, - { C_STRING_WITH_LEN("tinyint(3)") }, + { STRING_WITH_LEN("hist_size") }, + { STRING_WITH_LEN("tinyint(3)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("hist_type") }, - { C_STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("hist_type") }, + { STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("histogram") }, - { C_STRING_WITH_LEN("varbinary(255)") }, + { STRING_WITH_LEN("histogram") }, + { STRING_WITH_LEN("varbinary(255)") }, { NULL, 0 } } }; @@ -222,28 +217,28 @@ static const TABLE_FIELD_TYPE index_stat_fields[INDEX_STAT_N_FIELDS] = { { - { C_STRING_WITH_LEN("db_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("db_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("table_name") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("table_name") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("index") }, - { C_STRING_WITH_LEN("varchar(64)") }, - { C_STRING_WITH_LEN("utf8") } + { STRING_WITH_LEN("index") }, + { STRING_WITH_LEN("varchar(64)") }, + { STRING_WITH_LEN("utf8") } }, { - { C_STRING_WITH_LEN("prefix_arity") }, - { C_STRING_WITH_LEN("int(11)") }, + { STRING_WITH_LEN("prefix_arity") }, + { STRING_WITH_LEN("int(11)") }, { NULL, 0 } }, { - { C_STRING_WITH_LEN("avg_frequency") }, - { C_STRING_WITH_LEN("decimal(12,4)") }, + { STRING_WITH_LEN("avg_frequency") }, + { STRING_WITH_LEN("decimal(12,4)") }, { NULL, 0 } } }; @@ -294,7 +289,7 @@ inline int open_stat_tables(THD *thd, TABLE_LIST *tables, */ static inline int open_single_stat_table(THD *thd, TABLE_LIST *table, - const LEX_STRING *stat_tab_name, + const LEX_CSTRING *stat_tab_name, Open_tables_backup *backup, bool for_write) { @@ -472,8 +467,8 @@ protected: /* Table for which statistical data is read / updated */ TABLE *table; TABLE_SHARE *table_share; /* Table share for 'table */ - LEX_CSTRING *db_name; /* Name of the database containing 'table' */ - LEX_CSTRING *table_name; /* Name of the table 'table' */ + const LEX_CSTRING *db_name; /* Name of the database containing 'table' */ + const LEX_CSTRING *table_name; /* Name of the table 'table' */ void store_record_for_update() { @@ -528,12 +523,10 @@ public: by the database name 'db' and the table name 'tab'. */ - Stat_table(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) - :stat_table(stat), table_share(NULL) + Stat_table(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab) + :stat_table(stat), table_share(NULL),db_name(db), table_name(tab) { common_init_stat_table(); - db_name= db; - table_name= tab; } @@ -553,7 +546,7 @@ public: The method is called by the update_table_name_key_parts function. */ - virtual void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab)= 0; + virtual void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab)= 0; /** @@ -666,16 +659,22 @@ public: { if (find_stat()) { + bool res; store_record_for_update(); store_stat_fields(); - return update_record(); + res= update_record(); + DBUG_ASSERT(res == 0); + return res; } else { int err; store_stat_fields(); if ((err= stat_file->ha_write_row(record[0]))) + { + DBUG_ASSERT(0); return TRUE; + } /* Make change permanent and avoid 'table is marked as crashed' errors */ stat_file->extra(HA_EXTRA_FLUSH); } @@ -703,7 +702,7 @@ public: to store the new names in the record buffer used for updates. */ - bool update_table_name_key_parts(LEX_CSTRING *db, LEX_CSTRING *tab) + bool update_table_name_key_parts(const LEX_CSTRING *db, const LEX_CSTRING *tab) { store_record_for_update(); change_full_table_name(db, tab); @@ -765,7 +764,7 @@ private: table_name_field= stat_table->field[TABLE_STAT_TABLE_NAME]; } - void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) + void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -795,7 +794,7 @@ public: from the database 'db'. */ - Table_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) + Table_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_table_stat(); @@ -909,7 +908,7 @@ private: column_name_field= stat_table->field[COLUMN_STAT_COLUMN_NAME]; } - void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) + void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -939,7 +938,7 @@ public: from the database 'db'. */ - Column_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) + Column_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_column_stat_table(); @@ -1244,7 +1243,7 @@ private: prefix_arity_field= stat_table->field[INDEX_STAT_PREFIX_ARITY]; } - void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) + void change_full_table_name(const LEX_CSTRING *db, const LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -1276,7 +1275,7 @@ public: from the database 'db'. */ - Index_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) + Index_stat(TABLE *stat, const LEX_CSTRING *db, const LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_index_stat_table(); @@ -1319,8 +1318,8 @@ public: void set_index_prefix_key_fields(KEY *index_info) { set_full_table_name(); - const char *index_name= index_info->name; - index_name_field->store(index_name, strlen(index_name), + const char *index_name= index_info->name.str; + index_name_field->store(index_name, index_info->name.length, system_charset_info); table_key_info= index_info; } @@ -3520,8 +3519,8 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info, The function is called when executing any statement that renames a table */ -int rename_table_in_stat_tables(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab, - LEX_CSTRING *new_db, LEX_CSTRING *new_tab) +int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab, + const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab) { int err; enum_binlog_format save_binlog_format; @@ -3529,7 +3528,6 @@ int rename_table_in_stat_tables(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab, TABLE_LIST tables[STATISTICS_TABLES]; Open_tables_backup open_tables_backup; int rc= 0; - DBUG_ENTER("rename_table_in_stat_tables"); if (open_stat_tables(thd, tables, &open_tables_backup, TRUE)) @@ -3978,18 +3976,17 @@ double Histogram::point_selectivity(double pos, double avg_sel) /* Check whether the table is one of the persistent statistical tables. */ -bool is_stat_table(const char *db, const char *table) +bool is_stat_table(const LEX_CSTRING *db, LEX_CSTRING *table) { - DBUG_ASSERT(db && table); + DBUG_ASSERT(db->str && table->str); - if (!memcmp(db, stat_tables_db_name.str, stat_tables_db_name.length)) + if (!cmp(db, &MYSQL_SCHEMA_NAME)) { for (uint i= 0; i < STATISTICS_TABLES; i ++) { - if (!memcmp(table, stat_table_name[i].str, stat_table_name[i].length)) + if (cmp(table, &stat_table_name[i]) == 0) return true; } } return false; } - diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index eb59b149753..7ec742b70db 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -98,8 +98,8 @@ int delete_statistics_for_table(THD *thd, LEX_CSTRING *db, 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); -int rename_table_in_stat_tables(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab, - LEX_CSTRING *new_db, LEX_CSTRING *new_tab); +int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab, + const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab); int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col, const char *new_name); void set_statistics_for_table(THD *thd, TABLE *table); @@ -110,7 +110,7 @@ double get_column_range_cardinality(Field *field, key_range *min_endp, key_range *max_endp, uint range_flag); -bool is_stat_table(const char *db, const char *table); +bool is_stat_table(const LEX_CSTRING *db, LEX_CSTRING *table); class Histogram { @@ -255,18 +255,6 @@ public: class Columns_statistics; class Index_statistics; -static inline -int rename_table_in_stat_tables(THD *thd, const char *db, const char *tab, - const char *new_db, const char *new_tab) -{ - LEX_CSTRING od= { db, strlen(db) }; - LEX_CSTRING ot= { tab, strlen(tab) }; - LEX_CSTRING nd= { new_db, strlen(new_db) }; - LEX_CSTRING nt= { new_tab, strlen(new_tab) }; - return rename_table_in_stat_tables(thd, &od, &ot, &nd, &nt); -} - - /* Statistical data on a table */ class Table_statistics diff --git a/sql/sql_string.cc b/sql/sql_string.cc index a24eaaa6fde..390abe36b6c 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -20,8 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> -#include <my_sys.h> +#include "mariadb.h" #include <m_string.h> #include <m_ctype.h> #include <mysql_com.h> @@ -241,8 +240,8 @@ bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs) { if (alloc(arg_length)) return TRUE; - DBUG_ASSERT(arg_length < UINT_MAX32); - if ((str_length=arg_length)) + DBUG_ASSERT(arg_length <= UINT_MAX32); + if ((str_length=(uint32)arg_length)) memcpy(Ptr,str,arg_length); Ptr[arg_length]=0; str_charset=cs; @@ -272,7 +271,7 @@ bool String::copy(const char *str,size_t arg_length, CHARSET_INFO *cs) character_set_results is NULL. */ -bool String::needs_conversion(uint32 arg_length, +bool String::needs_conversion(size_t arg_length, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint32 *offset) @@ -283,7 +282,7 @@ bool String::needs_conversion(uint32 arg_length, (to_cs == from_cs) || my_charset_same(from_cs, to_cs) || ((from_cs == &my_charset_bin) && - (!(*offset=(arg_length % to_cs->mbminlen))))) + (!(*offset=(uint32)(arg_length % to_cs->mbminlen))))) return FALSE; return TRUE; } @@ -301,7 +300,7 @@ bool String::needs_conversion(uint32 arg_length, @return conversion needed */ -bool String::needs_conversion_on_storage(uint32 arg_length, +bool String::needs_conversion_on_storage(size_t arg_length, CHARSET_INFO *cs_from, CHARSET_INFO *cs_to) { @@ -350,14 +349,14 @@ bool String::needs_conversion_on_storage(uint32 arg_length, 1 error */ -bool String::copy_aligned(const char *str,uint32 arg_length, uint32 offset, +bool String::copy_aligned(const char *str, size_t arg_length, size_t offset, CHARSET_INFO *cs) { /* How many bytes are in incomplete character */ offset= cs->mbminlen - offset; /* How many zeros we should prepend */ DBUG_ASSERT(offset && offset != cs->mbminlen); - uint32 aligned_length= arg_length + offset; + size_t aligned_length= arg_length + offset; if (alloc(aligned_length)) return TRUE; @@ -370,17 +369,17 @@ bool String::copy_aligned(const char *str,uint32 arg_length, uint32 offset, memcpy(Ptr + offset, str, arg_length); Ptr[aligned_length]=0; /* str_length is always >= 0 as arg_length is != 0 */ - str_length= aligned_length; + str_length= (uint32)aligned_length; str_charset= cs; return FALSE; } -bool String::set_or_copy_aligned(const char *str,uint32 arg_length, +bool String::set_or_copy_aligned(const char *str, size_t arg_length, CHARSET_INFO *cs) { /* How many bytes are in incomplete character */ - uint32 offset= (arg_length % cs->mbminlen); + size_t offset= (arg_length % cs->mbminlen); if (!offset) /* All characters are complete, just copy */ { @@ -401,7 +400,7 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length, */ -bool String::copy(const char *str, uint32 arg_length, +bool String::copy(const char *str, size_t arg_length, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors) { uint32 offset; @@ -418,7 +417,7 @@ bool String::copy(const char *str, uint32 arg_length, *errors= 0; return copy_aligned(str, arg_length, offset, to_cs); } - uint32 new_length= to_cs->mbmaxlen*arg_length; + size_t new_length= to_cs->mbmaxlen*arg_length; if (alloc(new_length)) return TRUE; str_length=copy_and_convert((char*) Ptr, new_length, to_cs, @@ -447,7 +446,7 @@ bool String::copy(const char *str, uint32 arg_length, */ -bool String::set_ascii(const char *str, uint32 arg_length) +bool String::set_ascii(const char *str, size_t arg_length) { if (str_charset->mbminlen == 1) { @@ -455,7 +454,7 @@ bool String::set_ascii(const char *str, uint32 arg_length) return 0; } uint dummy_errors; - return copy(str, arg_length, &my_charset_latin1, str_charset, &dummy_errors); + return copy(str, (uint32)arg_length, &my_charset_latin1, str_charset, &dummy_errors); } @@ -564,13 +563,13 @@ bool String::append_ulonglong(ulonglong val) with character set recoding */ -bool String::append(const char *s, uint arg_length, CHARSET_INFO *cs) +bool String::append(const char *s, size_t arg_length, CHARSET_INFO *cs) { uint32 offset; - if (needs_conversion(arg_length, cs, str_charset, &offset)) + if (needs_conversion((uint32)arg_length, cs, str_charset, &offset)) { - uint32 add_length; + size_t add_length; if ((cs == &my_charset_bin) && offset) { DBUG_ASSERT(str_charset->mbminlen > offset); @@ -580,7 +579,7 @@ bool String::append(const char *s, uint arg_length, CHARSET_INFO *cs) return TRUE; bzero((char*) Ptr + str_length, offset); memcpy(Ptr + str_length + offset, s, arg_length); - str_length+= add_length; + str_length+= (uint32)add_length; return FALSE; } @@ -588,15 +587,15 @@ bool String::append(const char *s, uint arg_length, CHARSET_INFO *cs) uint dummy_errors; if (realloc_with_extra_if_needed(str_length + add_length)) return TRUE; - str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset, - s, arg_length, cs, &dummy_errors); + str_length+= copy_and_convert(Ptr+str_length, (uint32)add_length, str_charset, + s, (uint32)arg_length, cs, &dummy_errors); } else { if (realloc_with_extra_if_needed(str_length + arg_length)) return TRUE; memcpy(Ptr + str_length, s, arg_length); - str_length+= arg_length; + str_length+= (uint32)arg_length; } return FALSE; } @@ -761,7 +760,7 @@ bool String::replace(uint32 offset,uint32 arg_length, // added by Holyfoot for "geometry" needs -int String::reserve(uint32 space_needed, uint32 grow_by) +int String::reserve(size_t space_needed, size_t grow_by) { if (Alloced_length < str_length + space_needed) { @@ -771,10 +770,10 @@ int String::reserve(uint32 space_needed, uint32 grow_by) return FALSE; } -void String::qs_append(const char *str, uint32 len) +void String::qs_append(const char *str, size_t len) { memcpy(Ptr + str_length, str, len + 1); - str_length += len; + str_length += (uint32)len; } void String::qs_append(double d) @@ -1073,10 +1072,9 @@ my_copy_with_hex_escaping(CHARSET_INFO *cs, */ uint String_copier::well_formed_copy(CHARSET_INFO *to_cs, - char *to, uint to_length, + char *to, size_t to_length, CHARSET_INFO *from_cs, - const char *from, uint from_length, - uint nchars) + const char *from, size_t from_length, size_t nchars) { if ((to_cs == &my_charset_bin) || (from_cs == &my_charset_bin) || @@ -1099,7 +1097,7 @@ String_copier::well_formed_copy(CHARSET_INFO *to_cs, Does not add the enclosing quotes, this is left up to caller. */ #define APPEND(X) if (append(X)) return 1; else break -bool String::append_for_single_quote(const char *st, uint len) +bool String::append_for_single_quote(const char *st, size_t len) { const char *end= st+len; for (; st < end; st++) diff --git a/sql/sql_string.h b/sql/sql_string.h index 98c87f45d8f..5cabcc02aae 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -3,7 +3,7 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB Corporation. + Copyright (c) 2008, 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 @@ -25,7 +25,7 @@ #endif #include "m_ctype.h" /* my_charset_bin */ -#include "my_sys.h" /* alloc_root, my_free, my_realloc */ +#include <my_sys.h> /* alloc_root, my_free, my_realloc */ #include "m_string.h" /* TRASH */ class String; @@ -35,12 +35,12 @@ typedef struct st_mem_root MEM_ROOT; #include "pack.h" int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); -inline uint32 copy_and_convert(char *to, uint32 to_length, +inline uint32 copy_and_convert(char *to, size_t to_length, CHARSET_INFO *to_cs, - const char *from, uint32 from_length, + const char *from, size_t from_length, CHARSET_INFO *from_cs, uint *errors) { - return my_convert(to, to_length, to_cs, from, from_length, from_cs, errors); + return my_convert(to, (uint)to_length, to_cs, from, (uint)from_length, from_cs, errors); } @@ -97,9 +97,8 @@ public: Convert a string between character sets. "dstcs" and "srccs" cannot be &my_charset_bin. */ - size_t convert_fix(CHARSET_INFO *dstcs, char *dst, uint dst_length, - CHARSET_INFO *srccs, const char *src, uint src_length, - uint nchars) + size_t convert_fix(CHARSET_INFO *dstcs, char *dst, size_t dst_length, + CHARSET_INFO *srccs, const char *src, size_t src_length, size_t nchars) { return my_convert_fix(dstcs, dst, dst_length, srccs, src, src_length, nchars, this, this); @@ -107,13 +106,11 @@ public: /* Copy a string. Fix bad bytes/characters to '?'. */ - uint well_formed_copy(CHARSET_INFO *to_cs, char *to, uint to_length, - CHARSET_INFO *from_cs, const char *from, - uint from_length, uint nchars); + uint well_formed_copy(CHARSET_INFO *to_cs, char *to, size_t to_length, + CHARSET_INFO *from_cs, const char *from, size_t from_length, size_t nchars); // Same as above, but without the "nchars" limit. - uint well_formed_copy(CHARSET_INFO *to_cs, char *to, uint to_length, - CHARSET_INFO *from_cs, const char *from, - uint from_length) + uint well_formed_copy(CHARSET_INFO *to_cs, char *to, size_t to_length, + CHARSET_INFO *from_cs, const char *from, size_t from_length) { return well_formed_copy(to_cs, to, to_length, from_cs, from, from_length, @@ -142,7 +139,7 @@ public: alloced= thread_specific= 0; str_charset= &my_charset_bin; } - String(uint32 length_arg) + String(size_t length_arg) { alloced= thread_specific= 0; Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg); @@ -160,15 +157,15 @@ public: contructors need the size of memory for STR to be at least LEN+1 (to make room for zero termination). */ - String(const char *str,uint32 len, CHARSET_INFO *cs) + String(const char *str,size_t len, CHARSET_INFO *cs) { - Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0; + Ptr=(char*) str; str_length=(uint32)len; Alloced_length= extra_alloc=0; alloced= thread_specific= 0; str_charset=cs; } - String(char *str,uint32 len, CHARSET_INFO *cs) + String(char *str,size_t len, CHARSET_INFO *cs) { - Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0; + Ptr=(char*) str; Alloced_length=str_length=(uint32)len; extra_alloc= 0; alloced= thread_specific= 0; str_charset=cs; } @@ -180,7 +177,9 @@ public: str_charset=str.str_charset; } static void *operator new(size_t size, MEM_ROOT *mem_root) throw () - { return (void*) alloc_root(mem_root, (uint) size); } + { return (void*) alloc_root(mem_root, size); } + static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } static void operator delete(void *ptr_arg, size_t size) { (void) ptr_arg; @@ -189,6 +188,11 @@ public: } static void operator delete(void *, MEM_ROOT *) { /* never called */ } + static void operator delete[](void *ptr, size_t size) + { TRASH_FREE(ptr, size); } + static void operator delete[](void *ptr, MEM_ROOT *mem_root) + { /* never called */ } + ~String() { free(); } /* Mark variable thread specific it it's not allocated already */ @@ -203,9 +207,9 @@ public: inline uint32 length() const { return str_length;} inline uint32 alloced_length() const { return Alloced_length;} inline uint32 extra_allocation() const { return extra_alloc;} - inline char& operator [] (uint32 i) const { return Ptr[i]; } - inline void length(uint32 len) { str_length=len ; } - inline void extra_allocation(uint32 len) { extra_alloc= len; } + inline char& operator [] (size_t i) const { return Ptr[i]; } + inline void length(size_t len) { str_length=(uint32)len ; } + inline void extra_allocation(size_t len) { extra_alloc= (uint32)len; } inline bool is_empty() const { return (str_length == 0); } inline void mark_as_const() { Alloced_length= 0;} inline const char *ptr() const { return Ptr; } @@ -244,13 +248,13 @@ public: return skr; } - void set(String &str,uint32 offset,uint32 arg_length) + void set(String &str,size_t offset,size_t arg_length) { DBUG_ASSERT(&str != this); free(); - Ptr=(char*) str.ptr()+offset; str_length=arg_length; + Ptr=(char*) str.ptr()+offset; str_length=(uint32)arg_length; if (str.Alloced_length) - Alloced_length=str.Alloced_length-offset; + Alloced_length=(uint32)(str.Alloced_length-offset); str_charset=str.str_charset; } @@ -263,24 +267,24 @@ public: @param cs Character set to use for interpreting string data. @note The new buffer will not be null terminated. */ - inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs) + inline void set(char *str,size_t arg_length, CHARSET_INFO *cs) { free(); - Ptr=(char*) str; str_length=Alloced_length=arg_length; + Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length; str_charset=cs; } - inline void set(const char *str,uint32 arg_length, CHARSET_INFO *cs) + inline void set(const char *str,size_t arg_length, CHARSET_INFO *cs) { free(); - Ptr=(char*) str; str_length=arg_length; + Ptr=(char*) str; str_length=(uint32)arg_length; str_charset=cs; } - bool set_ascii(const char *str, uint32 arg_length); - inline void set_quick(char *str,uint32 arg_length, CHARSET_INFO *cs) + bool set_ascii(const char *str, size_t arg_length); + inline void set_quick(char *str,size_t arg_length, CHARSET_INFO *cs) { if (!alloced) { - Ptr=(char*) str; str_length=Alloced_length=arg_length; + Ptr=(char*) str; str_length=Alloced_length=(uint32)arg_length; } str_charset=cs; } @@ -297,13 +301,13 @@ public: bool set_hex(const char *str, uint32 len); /* Take over handling of buffer from some other object */ - void reset(char *ptr_arg, uint32 length_arg, uint32 alloced_length_arg, + void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg, CHARSET_INFO *cs) { free(); Ptr= ptr_arg; - str_length= length_arg; - Alloced_length= alloced_length_arg; + str_length= (uint32)length_arg; + Alloced_length= (uint32)alloced_length_arg; str_charset= cs; alloced= ptr_arg != 0; } @@ -432,29 +436,29 @@ 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 - static bool needs_conversion(uint32 arg_length, + static bool needs_conversion(size_t arg_length, CHARSET_INFO *cs_from, CHARSET_INFO *cs_to, uint32 *offset); - static bool needs_conversion_on_storage(uint32 arg_length, + static bool needs_conversion_on_storage(size_t arg_length, CHARSET_INFO *cs_from, CHARSET_INFO *cs_to); - bool copy_aligned(const char *s, uint32 arg_length, uint32 offset, + bool copy_aligned(const char *s, size_t arg_length, size_t offset, CHARSET_INFO *cs); - bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs); - bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom, + bool set_or_copy_aligned(const char *s, size_t arg_length, CHARSET_INFO *cs); + bool copy(const char*s, size_t arg_length, CHARSET_INFO *csfrom, CHARSET_INFO *csto, uint *errors); bool copy(const String *str, CHARSET_INFO *tocs, uint *errors) { return copy(str->ptr(), str->length(), str->charset(), tocs, errors); } bool copy(CHARSET_INFO *tocs, - CHARSET_INFO *fromcs, const char *src, uint32 src_length, - uint32 nchars, String_copier *copier) + CHARSET_INFO *fromcs, const char *src, size_t src_length, + size_t nchars, String_copier *copier) { if (alloc(tocs->mbmaxlen * src_length)) return true; str_length= copier->well_formed_copy(tocs, Ptr, Alloced_length, - fromcs, src, src_length, nchars); + fromcs, src, (uint)src_length, (uint)nchars); str_charset= tocs; return false; } @@ -471,21 +475,20 @@ public: bool append(const char *s); bool append(const LEX_STRING *ls) { - DBUG_ASSERT(ls->length < UINT_MAX32); + DBUG_ASSERT(ls->length < UINT_MAX32 && + ((ls->length == 0 && !ls->str) || + ls->length == strlen(ls->str))); return append(ls->str, (uint32) ls->length); } bool append(const LEX_CSTRING *ls) { - DBUG_ASSERT(ls->length < UINT_MAX32); + DBUG_ASSERT(ls->length < UINT_MAX32 && + ((ls->length == 0 && !ls->str) || + ls->length == strlen(ls->str))); return append(ls->str, (uint32) ls->length); } - bool append(const LEX_CSTRING &ls) - { - DBUG_ASSERT(ls.length < UINT_MAX32); - return append(ls.str, (uint32) ls.length); - } bool append(const char *s, size_t size); - bool append(const char *s, uint arg_length, CHARSET_INFO *cs); + bool append(const char *s, size_t arg_length, CHARSET_INFO *cs); bool append_ulonglong(ulonglong val); bool append_longlong(longlong val); bool append(IO_CACHE* file, uint32 arg_length); @@ -533,11 +536,11 @@ public: uint32 numchars() const; int charpos(longlong i,uint32 offset=0); - int reserve(uint32 space_needed) + int reserve(size_t space_needed) { return realloc(str_length + space_needed); } - int reserve(uint32 space_needed, uint32 grow_by); + int reserve(size_t space_needed, size_t grow_by); /* The following append operations do NOT check alloced memory @@ -576,7 +579,9 @@ public: } void q_append(const LEX_CSTRING *ls) { - DBUG_ASSERT(ls->length < UINT_MAX32); + DBUG_ASSERT(ls->length < UINT_MAX32 && + ((ls->length == 0 && !ls->str) || + ls->length == strlen(ls->str))); q_append(ls->str, (uint32) ls->length); } @@ -589,7 +594,14 @@ public: { qs_append(str, (uint32)strlen(str)); } - void qs_append(const char *str, uint32 len); + void qs_append(const LEX_CSTRING *ls) + { + DBUG_ASSERT(ls->length < UINT_MAX32 && + ((ls->length == 0 && !ls->str) || + ls->length == strlen(ls->str))); + qs_append(ls->str, (uint32)ls->length); + } + void qs_append(const char *str, size_t len); void qs_append_hex(const char *str, uint32 len); void qs_append(double d); void qs_append(double *d); @@ -650,7 +662,7 @@ public: print_with_conversion(to, cs); } - bool append_for_single_quote(const char *st, uint len); + bool append_for_single_quote(const char *st, size_t len); bool append_for_single_quote(const String *s) { return append_for_single_quote(s->ptr(), s->length()); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 5d5c372e303..1b066cd2df4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -18,7 +18,7 @@ /* drop and alter of tables */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "debug_sync.h" @@ -55,6 +55,9 @@ #include "transaction.h" #include "sql_audit.h" #include "sql_sequence.h" +#include "tztime.h" +#include "vtmd.h" // System Versioning + #ifdef __WIN__ #include <io.h> @@ -62,12 +65,16 @@ const char *primary_key_name="PRIMARY"; -static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); +static int check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start, KEY *end); static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, List<Virtual_column_info> *vcol, uint *nr); +static const +char * make_unique_invisible_field_name(THD *thd, const char *field_name, + List<Create_field> *fields); + static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, List<Create_field> &create, bool ignore, uint order_num, ORDER *order, @@ -88,7 +95,7 @@ static uint blob_length_by_type(enum_field_types type); @param name_len Length of the name, in bytes */ static char* add_identifier(THD* thd, char *to_p, const char * end_p, - const char* name, uint name_len) + const char* name, size_t name_len) { uint res; uint errors; @@ -208,13 +215,13 @@ uint explain_filename(THD* thd, char *to_p= to; char *end_p= to_p + to_length; const char *db_name= NULL; - int db_name_len= 0; + size_t db_name_len= 0; const char *table_name; - int table_name_len= 0; + size_t table_name_len= 0; const char *part_name= NULL; - int part_name_len= 0; + size_t part_name_len= 0; const char *subpart_name= NULL; - int subpart_name_len= 0; + size_t subpart_name_len= 0; uint part_type= NORMAL_PART_NAME; const char *tmp_p; @@ -374,7 +381,7 @@ uint explain_filename(THD* thd, Table name length. */ -uint filename_to_tablename(const char *from, char *to, uint to_length, +uint filename_to_tablename(const char *from, char *to, size_t to_length, bool stay_quiet) { uint errors; @@ -393,7 +400,7 @@ uint filename_to_tablename(const char *from, char *to, uint to_length, } DBUG_PRINT("exit", ("to '%s'", to)); - DBUG_RETURN(res); + DBUG_RETURN((uint)res); } @@ -429,7 +436,7 @@ bool check_mysql50_prefix(const char *name) non-0 result string length */ -uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length) +uint check_n_cut_mysql50_prefix(const char *from, char *to, size_t to_length) { if (check_mysql50_prefix(from)) return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH, @@ -438,6 +445,13 @@ uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length) } +static bool check_if_frm_exists(char *path, const char *db, const char *table) +{ + fn_format(path, table, db, reg_ext, MYF(0)); + return !access(path, F_OK); +} + + /* Translate a table name to a file name (WL #1324). @@ -451,7 +465,7 @@ uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length) File name length. */ -uint tablename_to_filename(const char *from, char *to, uint to_length) +uint tablename_to_filename(const char *from, char *to, size_t to_length) { uint errors, length; DBUG_ENTER("tablename_to_filename"); @@ -528,13 +542,18 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, DBUG_PRINT("enter", ("db: '%s' table_name: '%s' ext: '%s' flags: %x", db, table_name, ext, flags)); + (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff)); + + /* Check if this is a temporary table name. Allow it if a corresponding .frm file exists */ + if (is_prefix(table_name, tmp_file_prefix) && strlen(table_name) < NAME_CHAR_LEN && + check_if_frm_exists(tbbuff, dbbuff, table_name)) + flags|= FN_IS_TMP; + if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP strmake(tbbuff, table_name, sizeof(tbbuff)-1); else (void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff)); - (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff)); - char *end = buff + bufflen; /* Don't add FN_ROOTDIR if mysql_data_home already includes it */ char *pos = strnmov(buff, mysql_data_home, bufflen); @@ -589,7 +608,7 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen) size_t length= unpack_filename(buff, buff); DBUG_PRINT("exit", ("buff: '%s'", buff)); - DBUG_RETURN(length); + DBUG_RETURN((uint)length); } /* @@ -1750,9 +1769,10 @@ uint build_table_shadow_filename(char *buff, size_t bufflen, ALTER_PARTITION_PARAM_TYPE *lpt) { char tmp_name[FN_REFLEN]; - my_snprintf (tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix, - lpt->table_name); - return build_table_filename(buff, bufflen, lpt->db, tmp_name, "", FN_IS_TMP); + my_snprintf(tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix, + lpt->table_name.str); + return build_table_filename(buff, bufflen, lpt->db.str, tmp_name, "", + FN_IS_TMP); } @@ -1834,8 +1854,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) #endif /* Write shadow frm file */ lpt->create_info->table_options= lpt->db_options; - LEX_CUSTRING frm= build_frm_image(lpt->thd, - lpt->table_name, + LEX_CUSTRING frm= build_frm_image(lpt->thd, &lpt->table_name, lpt->create_info, lpt->alter_info->create_list, lpt->key_count, lpt->key_info_buffer, @@ -1846,7 +1865,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) goto end; } - int error= writefrm(shadow_path, lpt->db, lpt->table_name, + int error= writefrm(shadow_path, lpt->db.str, lpt->table_name.str, lpt->create_info->tmp_table(), frm.str, frm.length); my_free(const_cast<uchar*>(frm.str)); @@ -1866,8 +1885,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) /* Build frm file name */ - build_table_filename(path, sizeof(path) - 1, lpt->db, - lpt->table_name, "", 0); + build_table_filename(path, sizeof(path) - 1, lpt->db.str, + lpt->table_name.str, "", 0); strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS); /* When we are changing to use new frm file we need to ensure that we @@ -2030,8 +2049,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, { for (table= tables; table; table= table->next_local) { - LEX_CSTRING db_name= { table->db, table->db_length }; - LEX_CSTRING table_name= { table->table_name, table->table_name_length }; + LEX_CSTRING db_name= table->db; + LEX_CSTRING table_name= table->table_name; if (table->open_type == OT_BASE_ONLY || !thd->find_temporary_table(table)) (void) delete_statistics_for_table(thd, &db_name, &table_name); @@ -2093,8 +2112,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, by parser) it is safe to cache pointer to the TABLE instances in its elements. */ - table->table= find_table_for_mdl_upgrade(thd, table->db, - table->table_name, false); + table->table= find_table_for_mdl_upgrade(thd, table->db.str, + table->table_name.str, false); if (!table->table) DBUG_RETURN(true); table->mdl_request.ticket= table->table->mdl_ticket; @@ -2201,7 +2220,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { TABLE_LIST *table; char path[FN_REFLEN + 1], wrong_tables_buff[160]; - const char *alias= NULL; + LEX_CSTRING alias= null_clex_str; String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1, system_charset_info); uint path_length= 0, errors= 0; @@ -2292,12 +2311,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { bool is_trans= 0; bool table_creation_was_logged= 1; - const char *db= table->db; - size_t db_length= table->db_length; + LEX_CSTRING db= table->db; handlerton *table_type= 0; + VTMD_drop vtmd(*table); DBUG_PRINT("table", ("table_l: '%s'.'%s' table: %p s: %p", - table->db, table->table_name, table->table, + table->db.str, table->table_name.str, table->table, table->table ? table->table->s : NULL)); /* @@ -2356,14 +2375,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->db is NULL or 'IF EXISTS' clause is present in 'DROP TEMPORARY' query. */ - if (thd->db == NULL || strcmp(db,thd->db) != 0 - || is_drop_tmp_if_exists_added ) + if (thd->db.str == NULL || cmp(&db, &thd->db) || + is_drop_tmp_if_exists_added ) { - append_identifier(thd, built_ptr_query, db, db_length); + append_identifier(thd, built_ptr_query, &db); built_ptr_query->append("."); } - append_identifier(thd, built_ptr_query, table->table_name, - table->table_name_length); + append_identifier(thd, built_ptr_query, &table->table_name); built_ptr_query->append(","); } /* @@ -2377,13 +2395,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { non_temp_tables_count++; - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, - table->table_name, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str, + table->table_name.str, MDL_SHARED)); alias= (lower_case_table_names == 2) ? table->alias : table->table_name; /* remove .frm file and engine files */ - path_length= build_table_filename(path, sizeof(path) - 1, db, alias, + path_length= build_table_filename(path, sizeof(path) - 1, db.str, alias.str, reg_ext, 0); /* @@ -2405,21 +2423,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, Don't write the database name if it is the current one (or if thd->db is NULL). */ - if (thd->db == NULL || strcmp(db,thd->db) != 0) + if (thd->db.str == NULL || cmp(&db, &thd->db) != 0) { - append_identifier(thd, &built_query, db, db_length); + append_identifier(thd, &built_query, &db); built_query.append("."); } - append_identifier(thd, &built_query, table->table_name, - table->table_name_length); + append_identifier(thd, &built_query, &table->table_name); built_query.append(","); } } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; if (drop_temporary || - (ha_table_exists(thd, db, alias, &table_type, &is_sequence) == 0 && + (ha_table_exists(thd, &db, &alias, &table_type, &is_sequence) == 0 && table_type == 0) || (!drop_view && (was_view= (table_type == view_pseudo_hton))) || (drop_sequence && !is_sequence)) @@ -2439,9 +2456,9 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ER_BAD_TABLE_ERROR); String tbl_name(buff, sizeof(buff), system_charset_info); tbl_name.length(0); - tbl_name.append(db); + tbl_name.append(&db); tbl_name.append('.'); - tbl_name.append(table->table_name); + tbl_name.append(&table->table_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, err, ER_THD(thd, err), tbl_name.c_ptr_safe()); @@ -2455,6 +2472,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, else { char *end; + int frm_delete_error= 0; /* It could happen that table's share in the table definition cache is the only thing that keeps the engine plugin loaded @@ -2482,45 +2500,70 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, table->table= 0; } else - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str, table->table_name.str, false); /* Check that we have an exclusive lock on the table to be dropped. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, - table->table_name, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str, + table->table_name.str, MDL_EXCLUSIVE)); // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - error= ha_delete_table(thd, table_type, path, db, table->table_name, - !dont_log_query); - - if (!error) + if ((thd->lex->sql_command == SQLCOM_DROP_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_TABLE) && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && + table_type && table_type != view_pseudo_hton) { - int frm_delete_error, trigger_drop_error= 0; - /* Delete the table definition file */ - strmov(end,reg_ext); - if (table_type && table_type != view_pseudo_hton && - table_type->discover_table) + error= vtmd.check_exists(thd); + if (error) + goto non_tmp_err; + if (!vtmd.exists) + goto drop_table; { - /* - Table type is using discovery and may not need a .frm file. - Delete it silently if it exists - */ - (void) mysql_file_delete(key_file_frm, path, MYF(0)); - frm_delete_error= 0; + const char *name= vtmd.archive_name(thd); + LEX_CSTRING new_name= { name, strlen(name) }; + error= mysql_rename_table(table_type, &table->db, &table->table_name, + &table->db, &new_name, NO_FK_CHECKS); } - else - frm_delete_error= mysql_file_delete(key_file_frm, path, - MYF(MY_WME)); - if (frm_delete_error) - frm_delete_error= my_errno; - else + } + else + { + drop_table: + error= ha_delete_table(thd, table_type, path, &db, &table->table_name, + !dont_log_query); + if (!error) + { + /* Delete the table definition file */ + strmov(end,reg_ext); + if (table_type && table_type != view_pseudo_hton && + table_type->discover_table) + { + /* + Table type is using discovery and may not need a .frm file. + Delete it silently if it exists + */ + (void) mysql_file_delete(key_file_frm, path, MYF(0)); + } + else if (mysql_file_delete(key_file_frm, path, + MYF(MY_WME))) + { + frm_delete_error= my_errno; + DBUG_ASSERT(frm_delete_error); + } + } + } + + if (!error) + { + int trigger_drop_error= 0; + + if (!frm_delete_error) { non_tmp_table_deleted= TRUE; trigger_drop_error= - Table_triggers_list::drop_all_triggers(thd, db, table->table_name); + Table_triggers_list::drop_all_triggers(thd, &db, &table->table_name); } if (trigger_drop_error || @@ -2529,21 +2572,39 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, else if (frm_delete_error && if_exists) thd->clear_error(); } + non_tmp_err: non_tmp_error|= MY_TEST(error); } + + if (!error && vtmd.exists) + { + enum_sql_command sql_command= thd->lex->sql_command; + thd->lex->sql_command= SQLCOM_DROP_TABLE; + error= vtmd.update(thd); + thd->lex->sql_command= sql_command; + if (error) + { + LEX_CSTRING archive_name; + archive_name.str= vtmd.archive_name(); + archive_name.length= strlen(archive_name.str); + mysql_rename_table(table_type, &table->db, &archive_name, + &table->db, &table->table_name, NO_FK_CHECKS); + } + } + if (error) { if (wrong_tables.length()) wrong_tables.append(','); - wrong_tables.append(db); + wrong_tables.append(&db); wrong_tables.append('.'); - wrong_tables.append(table->table_name); + wrong_tables.append(&table->table_name); errors++; } else { - PSI_CALL_drop_table_share(false, table->db, table->db_length, - table->table_name, table->table_name_length); + PSI_CALL_drop_table_share(false, table->db.str, (uint)table->db.length, + table->table_name.str, (uint)table->table_name.length); mysql_audit_drop_table(thd, table); } @@ -2696,8 +2757,8 @@ end: when the original table was dropped but we could not create the new one. */ -bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, - const char *table_name, size_t table_name_length, +bool log_drop_table(THD *thd, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, bool temporary_table) { char buff[NAME_LEN*2 + 80]; @@ -2713,9 +2774,9 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, if (temporary_table) query.append(STRING_WITH_LEN("TEMPORARY ")); query.append(STRING_WITH_LEN("TABLE IF EXISTS ")); - append_identifier(thd, &query, db_name, db_name_length); + append_identifier(thd, &query, db_name); query.append("."); - append_identifier(thd, &query, table_name, table_name_length); + append_identifier(thd, &query, table_name); query.append(STRING_WITH_LEN("/* Generated to handle " "failed CREATE OR REPLACE */")); error= thd->binlog_query(THD::STMT_QUERY_TYPE, @@ -2738,16 +2799,16 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, @return False in case of success, True otherwise. */ -bool quick_rm_table(THD *thd, handlerton *base, const char *db, - const char *table_name, uint flags, const char *table_path) +bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, + const LEX_CSTRING *table_name, uint flags, const char *table_path) { char path[FN_REFLEN + 1]; - bool error= 0; + int error= 0; DBUG_ENTER("quick_rm_table"); size_t path_length= table_path ? (strxnmov(path, sizeof(path) - 1, table_path, reg_ext, NullS) - path) : - build_table_filename(path, sizeof(path)-1, db, table_name, reg_ext, flags); + build_table_filename(path, sizeof(path)-1, db->str, table_name->str, reg_ext, flags); if (mysql_file_delete(key_file_frm, path, MYF(0))) error= 1; /* purecov: inspected */ path[path_length - reg_ext_length]= '\0'; // Remove reg_ext @@ -2764,8 +2825,8 @@ bool quick_rm_table(THD *thd, handlerton *base, const char *db, if (likely(error == 0)) { - PSI_CALL_drop_table_share(flags & FN_IS_TMP, db, strlen(db), - table_name, strlen(table_name)); + PSI_CALL_drop_table_share(flags & FN_IS_TMP, db->str, (uint)db->length, + table_name->str, (uint)table_name->length); } DBUG_RETURN(error); @@ -2798,9 +2859,9 @@ static int sort_keys(KEY *a, KEY *b) /* Sort NOT NULL keys before other keys */ return (a_flags & HA_NULL_PART_KEY) ? 1 : -1; } - if (a->name == primary_key_name) + if (a->name.str == primary_key_name) return -1; - if (b->name == primary_key_name) + if (b->name.str == primary_key_name) return 1; /* Sort keys don't containing partial segments before others */ if ((a_flags ^ b_flags) & HA_KEY_HAS_PART_KEY_SEG) @@ -3024,10 +3085,12 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) if (column_definition->is_timestamp_type() || // TIMESTAMP column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { + DBUG_PRINT("info", ("field-ptr:%p", column_definition->field)); if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, column_definition->default_value == NULL && // no constant default, column_definition->unireg_check == Field::NONE && // no function default - column_definition->vcol_info == NULL) + column_definition->vcol_info == NULL && + !(column_definition->flags & VERS_SYSTEM_FIELD)) // column isn't generated { DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " @@ -3089,34 +3152,29 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, */ List_iterator_fast<Key_part_spec> k_column_iterator(k->columns); - - bool all_columns_are_identical= true; - + uint i; key_column_iterator.rewind(); - for (uint i= 0; i < key->columns.elements; ++i) + for (i= 0; i < key->columns.elements; ++i) { Key_part_spec *c1= key_column_iterator++; Key_part_spec *c2= k_column_iterator++; DBUG_ASSERT(c1 && c2); - if (my_strcasecmp(system_charset_info, - c1->field_name.str, c2->field_name.str) || + if (lex_string_cmp(system_charset_info, + &c1->field_name, &c2->field_name) || (c1->length != c2->length)) - { - all_columns_are_identical= false; break; - } } // Report a warning if we have two identical keys. - if (all_columns_are_identical) + if (i == key->columns.elements) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), - key_info->name); + key_info->name.str); break; } } @@ -3258,8 +3316,70 @@ bool Column_definition::prepare_stage1_check_typelib_default() } return false; } +/* + This function adds a invisible field to field_list + SYNOPSIS + mysql_add_invisible_field() + thd Thread Object + field_list list of all table fields + field_name name/prefix of invisible field + ( Prefix in the case when it is + *INVISIBLE_FULL* + and given name is duplicate) + type_handler field data type + invisible + default value + RETURN VALUE + Create_field pointer +*/ +int mysql_add_invisible_field(THD *thd, List<Create_field> * field_list, + const char *field_name, Type_handler *type_handler, + field_visibility_t invisible, Item* default_value) +{ + Create_field *fld= new(thd->mem_root)Create_field(); + const char *new_name= NULL; + /* Get unique field name if invisible == INVISIBLE_FULL */ + if (invisible == INVISIBLE_FULL) + { + if ((new_name= make_unique_invisible_field_name(thd, field_name, + field_list))) + { + fld->field_name.str= new_name; + fld->field_name.length= strlen(new_name); + } + else + return 1; //Should not happen + } + else + { + fld->field_name.str= thd->strmake(field_name, strlen(field_name)); + fld->field_name.length= strlen(field_name); + } + fld->set_handler(type_handler); + fld->invisible= invisible; + if (default_value) + { + Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); + v->expr= default_value; + v->utf8= 0; + fld->default_value= v; + } + field_list->push_front(fld, thd->mem_root); + return 0; +} - +Key * +mysql_add_invisible_index(THD *thd, List<Key> *key_list, + LEX_CSTRING* field_name, enum Key::Keytype type) +{ + Key *key= NULL; + key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, + false, DDL_options(DDL_options::OPT_NONE)); + key->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0), + thd->mem_root); + key_list->push_back(key, thd->mem_root); + return key; +} /* Preparation for table creation @@ -3307,6 +3427,23 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); + DBUG_EXECUTE_IF("test_pseudo_invisible",{ + mysql_add_invisible_field(thd, &alter_info->create_list, + "invisible", &type_handler_long, INVISIBLE_SYSTEM, + new (thd->mem_root)Item_int(thd, 9)); + }); + DBUG_EXECUTE_IF("test_completely_invisible",{ + mysql_add_invisible_field(thd, &alter_info->create_list, + "invisible", &type_handler_long, INVISIBLE_FULL, + new (thd->mem_root)Item_int(thd, 9)); + }); + DBUG_EXECUTE_IF("test_invisible_index",{ + LEX_CSTRING temp; + temp.str= "invisible"; + temp.length= strlen("invisible"); + mysql_add_invisible_index(thd, &alter_info->key_list + , &temp, Key::MULTIPLE); + }); LEX_CSTRING* connect_string = &create_info->connect_string; if (connect_string->length != 0 && connect_string->length > CONNECT_STRING_MAXLEN && @@ -3343,6 +3480,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } select_field_pos= alter_info->create_list.elements - select_field_count; + for (field_no=0; (sql_field=it++) ; field_no++) { /* @@ -3377,9 +3515,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Check if we have used the same field name before */ for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, - dup_field->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &dup_field->field_name) == 0) { /* If this was a CREATE ... SELECT statement, accept a field @@ -3435,7 +3573,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, while ((sql_field=it++)) { DBUG_ASSERT(sql_field->charset != 0); - if (sql_field->prepare_stage2(file, file->ha_table_flags())) DBUG_RETURN(TRUE); if (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) @@ -3455,8 +3592,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; + if (sql_field->flags & VERS_SYSTEM_FIELD) + continue; } - /* Update virtual fields' offset*/ + /* Update virtual fields' offset and give error if + All fields are invisible */ + bool is_all_invisible= true; it.rewind(); while ((sql_field=it++)) { @@ -3465,6 +3606,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->offset= record_offset; record_offset+= sql_field->pack_length; } + if (sql_field->invisible == VISIBLE) + is_all_invisible= false; + } + if (is_all_invisible) + { + my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0)); + DBUG_RETURN(TRUE); } if (auto_increment > 1) { @@ -3711,24 +3859,42 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, it.rewind(); field=0; while ((sql_field=it++) && - my_strcasecmp(system_charset_info, - column->field_name.str, - sql_field->field_name.str)) + lex_string_cmp(system_charset_info, + &column->field_name, + &sql_field->field_name)) field++; + /* + Either field is not present or field visibility is > INVISIBLE_USER + */ if (!sql_field) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } + if (sql_field->invisible > INVISIBLE_USER && + !(sql_field->flags & VERS_SYSTEM_FIELD) && + !key->invisible && DBUG_EVALUATE_IF("test_invisible_index", 0, 1)) + { + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); + DBUG_RETURN(TRUE); + } while ((dup_column= cols2++) != column) { - if (!my_strcasecmp(system_charset_info, - column->field_name.str, dup_column->field_name.str)) + if (!lex_string_cmp(system_charset_info, + &column->field_name, &dup_column->field_name)) { my_error(ER_DUP_FIELDNAME, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } } + + if (sql_field->compression_method()) + { + my_error(ER_COMPRESSED_COLUMN_USED_AS_KEY, MYF(0), + column->field_name.str); + DBUG_RETURN(TRUE); + } + cols2.rewind(); if (key->type == Key::FULLTEXT) { @@ -3972,12 +4138,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_DUP_KEYNAME, MYF(0), key_name); DBUG_RETURN(TRUE); } - key_info->name=(char*) key_name; + key_info->name.str= (char*) key_name; + key_info->name.length= strlen(key_name); } } - if (!key_info->name || check_column_name(key_info->name)) + if (!key_info->name.str || check_column_name(key_info->name.str)) { - my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name); + my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name.str); DBUG_RETURN(TRUE); } if (key->type == Key::UNIQUE && !(key_info->flags & HA_NULL_PART_KEY)) @@ -3992,7 +4159,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (validate_comment_length(thd, &key->key_create_info.comment, INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT, - key_info->name)) + key_info->name.str)) DBUG_RETURN(TRUE); key_info->comment.length= key->key_create_info.comment.length; @@ -4040,7 +4207,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, !sql_field->has_default_function() && (sql_field->flags & NOT_NULL_FLAG) && (!sql_field->is_timestamp_type() || - opt_explicit_defaults_for_timestamp)) + opt_explicit_defaults_for_timestamp)&& + !sql_field->vers_sys_field()) { sql_field->flags|= NO_DEFAULT_VALUE_FLAG; sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; @@ -4070,6 +4238,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name.str); DBUG_RETURN(TRUE); } + if (sql_field->invisible == INVISIBLE_USER && + sql_field->flags & NOT_NULL_FLAG && + sql_field->flags & NO_DEFAULT_VALUE_FLAG) + { + my_error(ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT, MYF(0), + sql_field->field_name.str); + DBUG_RETURN(TRUE); + } } /* Check table level constraints */ @@ -4091,9 +4267,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Virtual_column_info *dup_check; while ((dup_check= dup_it++) && dup_check != check) { - if (check->name.length == dup_check->name.length && - my_strcasecmp(system_charset_info, - check->name.str, dup_check->name.str) == 0) + if (!lex_string_cmp(system_charset_info, + &check->name, &dup_check->name)) { my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str); DBUG_RETURN(TRUE); @@ -4154,7 +4329,7 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len, uint err_code, const char *name) { DBUG_ENTER("validate_comment_length"); - uint tmp_len= my_charpos(system_charset_info, comment->str, + size_t tmp_len= my_charpos(system_charset_info, comment->str, comment->str + comment->length, max_len); if (tmp_len < comment->length) { @@ -4187,7 +4362,7 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len, static void set_table_default_charset(THD *thd, HA_CREATE_INFO *create_info, - const char *db) + const LEX_CSTRING *db) { /* If the table character set was not given explicitly, @@ -4198,7 +4373,7 @@ static void set_table_default_charset(THD *thd, { Schema_specification_st db_info; - load_db_opt_by_name(thd, db, &db_info); + load_db_opt_by_name(thd, db->str, &db_info); create_info->default_table_charset= db_info.default_table_charset; } @@ -4280,8 +4455,53 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) } +static bool +vers_prepare_keys(THD *thd, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, + KEY **key_info, + uint key_count) +{ + DBUG_ASSERT(create_info->versioned()); + + const char *row_start_field= create_info->vers_info.as_row.start; + DBUG_ASSERT(row_start_field); + const char *row_end_field= create_info->vers_info.as_row.end; + DBUG_ASSERT(row_end_field); + + List_iterator<Key> key_it(alter_info->key_list); + Key *key= NULL; + while ((key=key_it++)) + { + if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) + continue; + + Key_part_spec *key_part= NULL; + List_iterator<Key_part_spec> part_it(key->columns); + while ((key_part=part_it++)) + { + if (!my_strcasecmp(system_charset_info, + row_start_field, + key_part->field_name.str) || + + !my_strcasecmp(system_charset_info, + row_end_field, + key_part->field_name.str)) + break; + } + if (key_part) + continue; // Key already contains Sys_start or Sys_end + + Key_part_spec *key_part_sys_end_col= + new (thd->mem_root) Key_part_spec(&create_info->vers_info.as_row.end, 0); + key->columns.push_back(key_part_sys_end_col); + } + + return false; +} + handler *mysql_create_frm_image(THD *thd, - const char *db, const char *table_name, + const LEX_CSTRING *db, const LEX_CSTRING *table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, int create_table_mode, KEY **key_info, @@ -4298,7 +4518,7 @@ handler *mysql_create_frm_image(THD *thd, DBUG_RETURN(NULL); } - set_table_default_charset(thd, create_info, (char*) db); + set_table_default_charset(thd, create_info, db); db_options= create_info->table_options_with_row_type(); @@ -4435,7 +4655,10 @@ handler *mysql_create_frm_image(THD *thd, part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; if ((!(engine_type->partition_flags && - engine_type->partition_flags() & HA_CAN_PARTITION)) || + ((engine_type->partition_flags() & HA_CAN_PARTITION) || + (part_info->part_type == VERSIONING_PARTITION && + engine_type->partition_flags() & HA_ONLY_VERS_PARTITION)) + )) || create_info->db_type == partition_hton) { /* @@ -4516,6 +4739,13 @@ handler *mysql_create_frm_image(THD *thd, } #endif + if (create_info->versioned()) + { + if(vers_prepare_keys(thd, create_info, alter_info, key_info, + *key_count)) + goto err; + } + if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, file, key_info, key_count, create_table_mode)) @@ -4571,8 +4801,9 @@ err: static int create_table_impl(THD *thd, - const char *orig_db, const char *orig_table_name, - const char *db, const char *table_name, + const LEX_CSTRING *orig_db, + const LEX_CSTRING *orig_table_name, + const LEX_CSTRING *db, const LEX_CSTRING *table_name, const char *path, const DDL_options_st options, HA_CREATE_INFO *create_info, @@ -4583,14 +4814,14 @@ int create_table_impl(THD *thd, uint *key_count, LEX_CUSTRING *frm) { - const char *alias; + LEX_CSTRING *alias; handler *file= 0; int error= 1; bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; DBUG_ENTER("mysql_create_table_no_lock"); DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s", - db, table_name, internal_tmp_table, path)); + db->str, table_name->str, internal_tmp_table, path)); if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) { @@ -4612,7 +4843,7 @@ int create_table_impl(THD *thd, check_partition_dirs(thd->lex->part_info)) goto err; - alias= table_case_name(create_info, table_name); + alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, table_name)); /* Check if table exists */ if (create_info->tmp_table()) @@ -4621,7 +4852,7 @@ int create_table_impl(THD *thd, If a table exists, it must have been pre-opened. Try looking for one in-use in THD::all_temp_tables list of TABLE_SHAREs. */ - TABLE *tmp_table= thd->find_temporary_table(db, table_name); + TABLE *tmp_table= thd->find_temporary_table(db->str, table_name->str); if (tmp_table) { @@ -4639,7 +4870,7 @@ int create_table_impl(THD *thd, goto warn; else { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias->str); goto err; } /* @@ -4661,9 +4892,7 @@ int create_table_impl(THD *thd, if (options.or_replace()) { TABLE_LIST table_list; - table_list.init_one_table(db, strlen(db), table_name, - strlen(table_name), table_name, - TL_WRITE_ALLOW_WRITE); + table_list.init_one_table(db, table_name, 0, TL_WRITE_ALLOW_WRITE); table_list.table= create_info->table; if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) @@ -4698,7 +4927,7 @@ int create_table_impl(THD *thd, goto warn; else { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name->str); goto err; } } @@ -4706,7 +4935,7 @@ int create_table_impl(THD *thd, THD_STAGE_INFO(thd, stage_creating_table); - if (check_engine(thd, orig_db, orig_table_name, create_info)) + if (check_engine(thd, orig_db->str, orig_table_name->str, create_info)) goto err; if (create_table_mode == C_ASSISTED_DISCOVERY) @@ -4726,7 +4955,7 @@ int create_table_impl(THD *thd, goto err; } - init_tmp_table_share(thd, &share, db, 0, table_name, path); + init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path); /* prepare everything for discovery */ share.field= &no_fields; @@ -4763,7 +4992,7 @@ int create_table_impl(THD *thd, key_count, frm); if (!file) goto err; - if (rea_create_table(thd, frm, path, db, table_name, create_info, + if (rea_create_table(thd, frm, path, db->str, table_name->str, create_info, file, frm_only)) goto err; } @@ -4772,7 +5001,9 @@ int create_table_impl(THD *thd, if (!frm_only && create_info->tmp_table()) { TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm, - path, db, table_name, true); + path, db->str, + table_name->str, true, + false); if (!table) { @@ -4802,11 +5033,11 @@ int create_table_impl(THD *thd, TABLE table; TABLE_SHARE share; - init_tmp_table_share(thd, &share, db, 0, table_name, path); + init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path); bool result= (open_table_def(thd, &share, GTS_TABLE) || - open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, - 0, &table, true)); + open_table_from_share(thd, &share, &empty_clex_str, 0, + (uint) READ_ALL, 0, &table, true)); if (!result) (void) closefrm(&table); @@ -4835,7 +5066,7 @@ warn: push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER_THD(thd, ER_TABLE_EXISTS_ERROR), - alias); + alias->str); goto err; } @@ -4845,7 +5076,8 @@ warn: */ int mysql_create_table_no_lock(THD *thd, - const char *db, const char *table_name, + const LEX_CSTRING *db, + const LEX_CSTRING *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, int create_table_mode, @@ -4862,9 +5094,8 @@ int mysql_create_table_no_lock(THD *thd, else { int length; - const char *alias= table_case_name(create_info, table_name); - length= build_table_filename(path, sizeof(path) - 1, db, alias, - "", 0); + const LEX_CSTRING *alias= table_case_name(create_info, table_name); + length= build_table_filename(path, sizeof(path) - 1, db->str, alias->str, "", 0); // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) { @@ -4907,8 +5138,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, Table_specification_st *create_info, Alter_info *alter_info) { - const char *db= create_table->db; - const char *table_name= create_table->table_name; bool is_trans= FALSE; bool result; int create_table_mode; @@ -4950,7 +5179,9 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); - if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, + if (mysql_create_table_no_lock(thd, &create_table->db, + &create_table->table_name, create_info, + alter_info, &is_trans, create_table_mode, create_table) > 0) { @@ -4974,6 +5205,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, { thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); result= 1; + goto err; } else { @@ -4982,6 +5214,20 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } } + if (create_info->versioned() && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + VTMD_table vtmd(*create_table); + if (vtmd.update(thd)) + { + thd->variables.vers_alter_history = VERS_ALTER_HISTORY_KEEP; + mysql_rm_table_no_locks(thd, create_table, 0, 0, 0, 0, 1, 1); + thd->variables.vers_alter_history = VERS_ALTER_HISTORY_SURVIVE; + result= 1; + goto err; + } + } + err: /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) @@ -5019,17 +5265,36 @@ err: /* ** Give the key name after the first field with an optional '_#' after + @returns + 0 if keyname does not exists + [1..) index + 1 of duplicate key name **/ -static bool +static int check_if_keyname_exists(const char *name, KEY *start, KEY *end) { - for (KEY *key=start ; key != end ; key++) - if (!my_strcasecmp(system_charset_info,name,key->name)) - return 1; + uint i= 1; + for (KEY *key=start; key != end ; key++, i++) + if (!my_strcasecmp(system_charset_info, name, key->name.str)) + return i; return 0; } +/** + Returns 1 if field name exists otherwise 0 +*/ +static bool +check_if_field_name_exists(const char *name, List<Create_field> * fields) +{ + Create_field *fld; + List_iterator<Create_field>it(*fields); + while ((fld = it++)) + { + if (!my_strcasecmp(system_charset_info, fld->field_name.str, name)) + return 1; + } + return 0; +} static char * make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) @@ -5087,11 +5352,98 @@ static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, } } +/** + INVISIBLE_FULL are internally created. They are completely invisible + to Alter command (Opposite of SYSTEM_INVISIBLE which throws an + error when same name column is added by Alter). So in the case of when + user added a same column name as of INVISIBLE_FULL , we change + INVISIBLE_FULL column name. +*/ +static const +char * make_unique_invisible_field_name(THD *thd, const char *field_name, + List<Create_field> *fields) +{ + if (!check_if_field_name_exists(field_name, fields)) + return field_name; + char buff[MAX_FIELD_NAME], *buff_end; + buff_end= strmake_buf(buff, field_name); + if (buff_end - buff < 5) + return NULL; // Should not happen + + for (uint i=1 ; i < 10000; i++) + { + char *real_end= int10_to_str(i, buff_end, 10); + if (check_if_field_name_exists(buff, fields)) + continue; + return (const char *)thd->strmake(buff, real_end - buff); + } + return NULL; //Should not happen +} /**************************************************************************** ** Alter a table definition ****************************************************************************/ +bool operator!=(const MYSQL_TIME &lhs, const MYSQL_TIME &rhs) +{ + return lhs.year != rhs.year || lhs.month != rhs.month || lhs.day != rhs.day || + lhs.hour != rhs.hour || lhs.minute != rhs.minute || + lhs.second_part != rhs.second_part || lhs.neg != rhs.neg || + lhs.time_type != rhs.time_type; +} + +// Sets row_end=MAX for rows with row_end=now(6) +static bool vers_reset_alter_copy(THD *thd, TABLE *table) +{ + const MYSQL_TIME query_start= thd->query_start_TIME(); + + READ_RECORD info; + int error= 0; + bool will_batch= false; + ha_rows dup_key_found= 0; + if (init_read_record(&info, thd, table, NULL, NULL, 0, 1, true)) + goto err; + + will_batch= !table->file->start_bulk_update(); + + while (!(error= info.read_record())) + { + MYSQL_TIME current; + if (table->vers_end_field()->get_date(¤t, 0)) + goto err_read_record; + if (current != query_start) + { + continue; + } + + store_record(table, record[1]); + table->vers_end_field()->set_max(); + if (will_batch) + error= table->file->ha_bulk_update_row(table->record[1], table->record[0], + &dup_key_found); + else + error= table->file->ha_update_row(table->record[1], table->record[0]); + if (error && table->file->is_fatal_error(error, HA_CHECK_ALL)) + { + table->file->print_error(error, MYF(ME_FATALERROR)); + goto err_read_record; + } + } + + if (will_batch && (error= table->file->exec_bulk_update(&dup_key_found))) + table->file->print_error(error, MYF(ME_FATALERROR)); + if (will_batch) + table->file->end_bulk_update(); + +err_read_record: + end_read_record(&info); + +err: + if (table->file->ha_external_lock(thd, F_UNLCK)) + return true; + + return error ? true : false; +} /** Rename a table. @@ -5114,9 +5466,9 @@ static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, */ bool -mysql_rename_table(handlerton *base, const char *old_db, - const char *old_name, const char *new_db, - const char *new_name, uint flags) +mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, + const LEX_CSTRING *old_name, const LEX_CSTRING *new_db, + const LEX_CSTRING *new_name, uint flags) { THD *thd= current_thd; char from[FN_REFLEN + 1], to[FN_REFLEN + 1], @@ -5130,7 +5482,7 @@ mysql_rename_table(handlerton *base, const char *old_db, DBUG_ENTER("mysql_rename_table"); DBUG_ASSERT(base); DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'", - old_db, old_name, new_db, new_name)); + old_db->str, old_name->str, new_db->str, new_name->str)); // Temporarily disable foreign key checks if (flags & NO_FK_CHECKS) @@ -5138,9 +5490,9 @@ mysql_rename_table(handlerton *base, const char *old_db, file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base); - build_table_filename(from, sizeof(from) - 1, old_db, old_name, "", + build_table_filename(from, sizeof(from) - 1, old_db->str, old_name->str, "", flags & FN_FROM_IS_TMP); - length= build_table_filename(to, sizeof(to) - 1, new_db, new_name, "", + length= build_table_filename(to, sizeof(to) - 1, new_db->str, new_name->str, "", flags & FN_TO_IS_TMP); // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) @@ -5157,18 +5509,18 @@ mysql_rename_table(handlerton *base, const char *old_db, if (lower_case_table_names == 2 && file && !(file->ha_table_flags() & HA_FILE_BASED)) { - strmov(tmp_name, old_name); + strmov(tmp_name, old_name->str); my_casedn_str(files_charset_info, tmp_name); - strmov(tmp_db_name, old_db); + strmov(tmp_db_name, old_db->str); my_casedn_str(files_charset_info, tmp_db_name); build_table_filename(lc_from, sizeof(lc_from) - 1, tmp_db_name, tmp_name, "", flags & FN_FROM_IS_TMP); from_base= lc_from; - strmov(tmp_name, new_name); + strmov(tmp_name, new_name->str); my_casedn_str(files_charset_info, tmp_name); - strmov(tmp_db_name, new_db); + strmov(tmp_db_name, new_db->str); my_casedn_str(files_charset_info, tmp_db_name); build_table_filename(lc_to, sizeof(lc_to) - 1, tmp_db_name, tmp_name, "", @@ -5211,8 +5563,8 @@ mysql_rename_table(handlerton *base, const char *old_db, if (likely(error == 0)) { PSI_CALL_drop_table_share(flags & FN_FROM_IS_TMP, - old_db, strlen(old_db), - old_name, strlen(old_name)); + old_db->str, (uint)old_db->length, + old_name->str, (uint)old_name->length); } // Restore options bits to the original value @@ -5326,7 +5678,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, local_create_info.max_rows= 0; /* Replace type of source table with one specified in the statement. */ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; - local_create_info.options|= create_info->tmp_table(); + local_create_info.options|= create_info->options; /* Reset auto-increment counter for the new table. */ local_create_info.auto_increment_value= 0; /* @@ -5335,12 +5687,19 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, */ local_create_info.data_file_name= local_create_info.index_file_name= NULL; + if (src_table->table->versioned() && + local_create_info.vers_info.fix_create_like(local_alter_info, local_create_info, + *src_table, *table)) + { + goto err; + } + /* The following is needed only in case of lock tables */ if ((local_create_info.table= thd->lex->query_tables->table)) pos_in_locked_tables= local_create_info.table->pos_in_locked_tables; res= ((create_res= - mysql_create_table_no_lock(thd, table->db, table->table_name, + mysql_create_table_no_lock(thd, &table->db, &table->table_name, &local_create_info, &local_alter_info, &is_trans, C_ORDINARY_CREATE, table)) > 0); @@ -5383,8 +5742,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, non-temporary table. */ DBUG_ASSERT((create_info->tmp_table()) || - thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, - table->table_name, + thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str, + table->table_name.str, MDL_EXCLUSIVE)); } @@ -5541,9 +5900,7 @@ err: Table was not deleted. Original table was deleted. We have to log it. */ - log_drop_table(thd, table->db, table->db_length, - table->table_name, table->table_name_length, - create_info->tmp_table()); + log_drop_table(thd, &table->db, &table->table_name, create_info->tmp_table()); } else if (write_bin_log(thd, res ? FALSE : TRUE, thd->query(), thd->query_length(), is_trans)) @@ -5688,8 +6045,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) */ for (f_ptr=table->field; *f_ptr; f_ptr++) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, (*f_ptr)->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &(*f_ptr)->field_name) == 0) goto drop_create_field; } { @@ -5701,8 +6059,9 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) Create_field *chk_field; while ((chk_field= chk_it++) && chk_field != sql_field) { - if (my_strcasecmp(system_charset_info, - sql_field->field_name.str, chk_field->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->field_name, + &chk_field->field_name) == 0) goto drop_create_field; } } @@ -5737,8 +6096,9 @@ drop_create_field: */ for (f_ptr=table->field; *f_ptr; f_ptr++) { - if (my_strcasecmp(system_charset_info, - sql_field->change.str, (*f_ptr)->field_name.str) == 0) + if (lex_string_cmp(system_charset_info, + &sql_field->change, + &(*f_ptr)->field_name) == 0) { break; } @@ -5761,6 +6121,40 @@ drop_create_field: } } + /* Handle ALTER COLUMN IF EXISTS SET/DROP DEFAULT. */ + { + List_iterator<Alter_column> it(alter_info->alter_list); + Alter_column *acol; + + while ((acol=it++)) + { + if (!acol->alter_if_exists) + continue; + /* + If there is NO field with the same name in the table already, + remove the acol from the list. + */ + for (f_ptr=table->field; *f_ptr; f_ptr++) + { + if (my_strcasecmp(system_charset_info, + acol->name, (*f_ptr)->field_name.str) == 0) + break; + } + if (*f_ptr == NULL) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_BAD_FIELD_ERROR, + ER_THD(thd, ER_BAD_FIELD_ERROR), + acol->name, table->s->table_name.str); + it.remove(); + if (alter_info->alter_list.is_empty()) + { + alter_info->flags&= ~(Alter_info::ALTER_CHANGE_COLUMN_DEFAULT); + } + } + } + } + /* Handle DROP COLUMN/KEY IF EXISTS. */ { List_iterator<Alter_drop> drop_it(alter_info->drop_list); @@ -5789,7 +6183,9 @@ drop_create_field: } else if (drop->type == Alter_drop::CHECK_CONSTRAINT) { - for (uint i=table->s->field_check_constraints; i < table->s->table_check_constraints; i++) + for (uint i=table->s->field_check_constraints; + i < table->s->table_check_constraints; + i++) { if (my_strcasecmp(system_charset_info, drop->name, table->check_constraints[i]->name.str) == 0) @@ -5807,7 +6203,8 @@ drop_create_field: for (n_key=0; n_key < table->s->keys; n_key++) { if (my_strcasecmp(system_charset_info, - drop->name, table->key_info[n_key].name) == 0) + drop->name, + table->key_info[n_key].name.str) == 0) { remove_drop= FALSE; break; @@ -5905,7 +6302,7 @@ drop_create_field: for (n_key=0; n_key < table->s->keys; n_key++) { if (my_strcasecmp(system_charset_info, - keyname, table->key_info[n_key].name) == 0) + keyname, table->key_info[n_key].name.str) == 0) { goto remove_key; } @@ -6057,8 +6454,8 @@ remove_key: { Virtual_column_info *dup= table->check_constraints[c]; if (dup->name.length == check->name.length && - my_strcasecmp(system_charset_info, - check->name.str, dup->name.str) == 0) + lex_string_cmp(system_charset_info, + &check->name, &dup->name) == 0) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_CONSTRAINT_NAME, ER_THD(thd, ER_DUP_CONSTRAINT_NAME), @@ -6208,6 +6605,14 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CHECK_CONSTRAINT; if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT) ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CHECK_CONSTRAINT; + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_HISTORICAL; + if (alter_info->flags & Alter_info::ALTER_COLUMN_UNVERSIONED) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_UNVERSIONED; + if (alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_SYSTEM_VERSIONING; + if (alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_SYSTEM_VERSIONING; /* If we altering table with old VARCHAR fields we will be automatically @@ -6365,8 +6770,8 @@ static bool fill_alter_inplace_info(THD *thd, } /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, field->field_name.str, - new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, &field->field_name, + &new_field->field_name)) { field->flags|= FIELD_IS_RENAMED; ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME; @@ -6497,7 +6902,8 @@ static bool fill_alter_inplace_info(THD *thd, new_key < new_key_end; new_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (new_key >= new_key_end) @@ -6506,7 +6912,7 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->index_drop_buffer [ha_alter_info->index_drop_count++]= table_key; - DBUG_PRINT("info", ("index dropped: '%s'", table_key->name)); + DBUG_PRINT("info", ("index dropped: '%s'", table_key->name.str)); continue; } @@ -6574,7 +6980,7 @@ static bool fill_alter_inplace_info(THD *thd, [ha_alter_info->index_add_count++]= (uint)(new_key - ha_alter_info->key_info_buffer); /* Mark all old fields which are used in newly created index. */ - DBUG_PRINT("info", ("index changed: '%s'", table_key->name)); + DBUG_PRINT("info", ("index changed: '%s'", table_key->name.str)); } /*end of for (; table_key < table_key_end;) */ @@ -6588,7 +6994,8 @@ static bool fill_alter_inplace_info(THD *thd, /* Search an old key with the same name. */ for (table_key= table->key_info; table_key < table_key_end; table_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (table_key >= table_key_end) @@ -6597,7 +7004,7 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->index_add_buffer [ha_alter_info->index_add_count++]= (uint)(new_key - ha_alter_info->key_info_buffer); - DBUG_PRINT("info", ("index added: '%s'", new_key->name)); + DBUG_PRINT("info", ("index added: '%s'", new_key->name.str)); } else ha_alter_info->create_info->indexes_option_struct[table_key - table->key_info]= @@ -6669,7 +7076,8 @@ static bool fill_alter_inplace_info(THD *thd, if (new_key->flags & HA_NOSAME) { - bool is_pk= !my_strcasecmp(system_charset_info, new_key->name, primary_key_name); + bool is_pk= !my_strcasecmp(system_charset_info, + new_key->name.str, primary_key_name); if ((!(new_key->flags & HA_KEY_HAS_PART_KEY_SEG) && !(new_key->flags & HA_NULL_PART_KEY)) || @@ -6821,9 +7229,9 @@ bool mysql_compare_tables(TABLE *table, create_info->table_options|= HA_OPTION_PACK_RECORD; /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, - field->field_name.str, - tmp_new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, + &field->field_name, + &tmp_new_field->field_name)) DBUG_RETURN(false); /* Evaluate changes bitmap and send to check_if_incompatible_data() */ @@ -6850,7 +7258,8 @@ bool mysql_compare_tables(TABLE *table, /* Search a key with the same name. */ for (new_key= key_info_buffer; new_key < new_key_end; new_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (new_key >= new_key_end) @@ -6889,7 +7298,8 @@ bool mysql_compare_tables(TABLE *table, /* Search a key with the same name. */ for (table_key= table->key_info; table_key < table_key_end; table_key++) { - if (! strcmp(table_key->name, new_key->name)) + if (!lex_string_cmp(system_charset_info, &table_key->name, + &new_key->name)) break; } if (table_key >= table_key_end) @@ -7235,11 +7645,34 @@ static bool mysql_inplace_alter_table(THD *thd, DEBUG_SYNC(thd, "alter_table_inplace_before_commit"); THD_STAGE_INFO(thd, stage_alter_inplace_commit); - if (table->file->ha_commit_inplace_alter_table(altered_table, - ha_alter_info, - true)) { - goto rollback; + TR_table trt(thd, true); + if (trt != *table_list && table->file->ht->prepare_commit_versioned) + { + ulonglong trx_start_id= 0; + ulonglong trx_end_id= table->file->ht->prepare_commit_versioned(thd, &trx_start_id); + if (trx_end_id) + { + if (!TR_table::use_transaction_registry) + { + my_error(ER_VERS_TRT_IS_DISABLED, MYF(0)); + goto rollback; + } + if (trt.update(trx_start_id, trx_end_id)) + { + goto rollback; + } + } + } + + if (table->file->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, + true)) + { + goto rollback; + } + + thd->drop_temporary_table(altered_table, NULL, false); } close_all_tables_for_name(thd, table->s, @@ -7249,19 +7682,17 @@ static bool mysql_inplace_alter_table(THD *thd, NULL); table_list->table= table= NULL; - thd->drop_temporary_table(altered_table, NULL, false); - /* Replace the old .FRM with the new .FRM, but keep the old name for now. Rename to the new name (if needed) will be handled separately below. */ - if (mysql_rename_table(db_type, alter_ctx->new_db, alter_ctx->tmp_name, - alter_ctx->db, alter_ctx->alias, + if (mysql_rename_table(db_type, &alter_ctx->new_db, &alter_ctx->tmp_name, + &alter_ctx->db, &alter_ctx->alias, FN_FROM_IS_TMP | NO_HA_TABLE)) { // Since changes were done in-place, we can't revert them. (void) quick_rm_table(thd, db_type, - alter_ctx->new_db, alter_ctx->tmp_name, + &alter_ctx->new_db, &alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE); DBUG_RETURN(true); } @@ -7289,10 +7720,10 @@ static bool mysql_inplace_alter_table(THD *thd, { // Remove TABLE and TABLE_SHARE for old name from TDC. tdc_remove_table(thd, TDC_RT_REMOVE_ALL, - alter_ctx->db, alter_ctx->table_name, false); + alter_ctx->db.str, alter_ctx->table_name.str, false); - if (mysql_rename_table(db_type, alter_ctx->db, alter_ctx->table_name, - alter_ctx->new_db, alter_ctx->new_alias, 0)) + if (mysql_rename_table(db_type, &alter_ctx->db, &alter_ctx->table_name, + &alter_ctx->new_db, &alter_ctx->new_alias, 0)) { /* If the rename fails we will still have a working table @@ -7301,23 +7732,23 @@ static bool mysql_inplace_alter_table(THD *thd, DBUG_RETURN(true); } if (Table_triggers_list::change_table_name(thd, - alter_ctx->db, - alter_ctx->alias, - alter_ctx->table_name, - alter_ctx->new_db, - alter_ctx->new_alias)) + &alter_ctx->db, + &alter_ctx->alias, + &alter_ctx->table_name, + &alter_ctx->new_db, + &alter_ctx->new_alias)) { /* If the rename of trigger files fails, try to rename the table back so we at least have matching table and trigger files. */ (void) mysql_rename_table(db_type, - alter_ctx->new_db, alter_ctx->new_alias, - alter_ctx->db, alter_ctx->alias, NO_FK_CHECKS); + &alter_ctx->new_db, &alter_ctx->new_alias, + &alter_ctx->db, &alter_ctx->alias, NO_FK_CHECKS); DBUG_RETURN(true); } - rename_table_in_stat_tables(thd, alter_ctx->db,alter_ctx->alias, - alter_ctx->new_db, alter_ctx->new_alias); + rename_table_in_stat_tables(thd, &alter_ctx->db, &alter_ctx->alias, + &alter_ctx->new_db, &alter_ctx->new_alias); } DBUG_RETURN(false); @@ -7341,8 +7772,8 @@ static bool mysql_inplace_alter_table(THD *thd, } thd->drop_temporary_table(altered_table, NULL, false); // Delete temporary .frm/.par - (void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db, - alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE); + (void) quick_rm_table(thd, create_info->db_type, &alter_ctx->new_db, + &alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE); DBUG_RETURN(true); } @@ -7439,7 +7870,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, uint db_create_options= (table->s->db_create_options & ~(HA_OPTION_PACK_RECORD)); Item::func_processor_rename column_rename_param; - uint used_fields; + uint used_fields, dropped_sys_vers_fields= 0; KEY *key_info=table->key_info; bool rc= TRUE; bool modified_primary_key= FALSE; @@ -7513,6 +7944,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, bitmap_clear_all(&table->tmp_set); for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++) { + if (field->invisible == INVISIBLE_FULL) + continue; Alter_drop *drop; if (field->type() == MYSQL_TYPE_VARCHAR) create_info->varchar= TRUE; @@ -7524,7 +7957,15 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, !my_strcasecmp(system_charset_info,field->field_name.str, drop->name)) break; } - if (drop) + /* + DROP COLULMN xxx + 1. it does not see INVISIBLE_SYSTEM columns + 2. otherwise, normally a column is dropped + 3. unless it's a system versioning column (but see below). + */ + if (drop && field->invisible < INVISIBLE_SYSTEM && + !(field->flags & VERS_SYSTEM_FIELD && + !(alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING))) { /* Reset auto_increment value if it was dropped */ if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER && @@ -7541,6 +7982,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, continue; } + /* invisible versioning column is dropped automatically on DROP SYSTEM VERSIONING */ + if (!drop && field->invisible >= INVISIBLE_SYSTEM && + field->flags & VERS_SYSTEM_FIELD && + alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING) + { + if (table->s->tmp_table == NO_TMP_TABLE) + (void) delete_statistics_for_column(thd, table, field); + continue; + } + /* If we are doing a rename of a column, update all references in virtual column expressions, constraints and defaults to use the new column name @@ -7563,11 +8014,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, while ((def=def_it++)) { if (def->change.str && - !my_strcasecmp(system_charset_info,field->field_name.str, - def->change.str)) + !lex_string_cmp(system_charset_info, &field->field_name, + &def->change)) break; } - if (def) + if (def && field->invisible < INVISIBLE_SYSTEM) { // Field is changed def->field=field; /* @@ -7593,6 +8044,29 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def_it.remove(); } } + else if (alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING && + field->flags & VERS_SYSTEM_FIELD && + field->invisible < INVISIBLE_SYSTEM) + { + my_error(ER_VERS_SYS_FIELD_EXISTS, MYF(0), field->field_name.str); + goto err; + } + else if (drop && field->invisible < INVISIBLE_SYSTEM && + field->flags & VERS_SYSTEM_FIELD && + !(alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING)) + { + /* "dropping" a versioning field only hides it from the user */ + def= new (thd->mem_root) Create_field(thd, field, field); + def->invisible= INVISIBLE_SYSTEM; + dropped_sys_vers_fields|= field->flags; + alter_info->flags|= Alter_info::ALTER_CHANGE_COLUMN; + if (field->flags & VERS_SYS_START_FLAG) + create_info->vers_info.as_row.start= def->field_name= Vers_parse_info::default_start; + else + create_info->vers_info.as_row.end= def->field_name= Vers_parse_info::default_end; + new_create_list.push_back(def, thd->mem_root); + drop_it.remove(); + } else { /* @@ -7619,15 +8093,27 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } } + if (dropped_sys_vers_fields && + ((dropped_sys_vers_fields & VERS_SYSTEM_FIELD) != VERS_SYSTEM_FIELD)) + { + StringBuffer<NAME_LEN*2> tmp; + tmp.append(STRING_WITH_LEN("DROP COLUMN ")); + if (dropped_sys_vers_fields & VERS_SYS_START_FLAG) + append_identifier(thd, &tmp, &table->vers_end_field()->field_name); + else + append_identifier(thd, &tmp, &table->vers_start_field()->field_name); + my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr()); + goto err; + } def_it.rewind(); while ((def=def_it++)) // Add new columns { + Create_field *find; if (def->change.str && ! def->field) { /* Check if there is modify for newly added field. */ - Create_field *find; find_it.rewind(); while((find=find_it++)) { @@ -7663,11 +8149,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, alter_ctx->datetime_field= def; alter_ctx->error_if_not_empty= TRUE; } + if (def->flags & VERS_SYSTEM_FIELD && + !(alter_info->flags & Alter_info::ALTER_ADD_SYSTEM_VERSIONING)) + { + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table->s->table_name.str); + goto err; + } if (!def->after.str) new_create_list.push_back(def, thd->mem_root); else { - Create_field *find; if (def->change.str) { find_it.rewind(); @@ -7698,8 +8189,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, find_it.rewind(); while ((find=find_it++)) { - if (!my_strcasecmp(system_charset_info, def->after.str, - find->field_name.str)) + if (!lex_string_cmp(system_charset_info, &def->after, + &find->field_name)) break; } if (!find) @@ -7726,7 +8217,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { if (def->real_field_type() == MYSQL_TYPE_BLOB) { - my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change); + my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change.str); goto err; } if ((def->default_value= alter->default_value)) // Use new default @@ -7754,10 +8245,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Collect all keys which isn't in drop list. Add only those for which some fields exists. */ - + for (uint i=0 ; i < table->s->keys ; i++,key_info++) { - const char *key_name= key_info->name; + if (key_info->flags & HA_INVISIBLE_KEY) + continue; + const char *key_name= key_info->name.str; Alter_drop *drop; drop_it.rewind(); while ((drop=drop_it++)) @@ -7793,9 +8286,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, bool delete_index_stat= FALSE; for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) { - if (!key_part->field) + Field *kfield= key_part->field; + if (!kfield) continue; // Wrong field (from UNIREG) - const char *key_part_name=key_part->field->field_name.str; + const char *key_part_name=kfield->field_name.str; Create_field *cfield; uint key_part_length; @@ -7817,7 +8311,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (table->s->primary_key == i) modified_primary_key= TRUE; delete_index_stat= TRUE; - dropped_key_part= key_part_name; + if (!(kfield->flags & VERS_SYSTEM_FIELD)) + dropped_key_part= key_part_name; continue; // Field is removed } key_part_length= key_part->length; @@ -7854,10 +8349,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, cfield->real_field_type() <= MYSQL_TYPE_BLOB) ? blob_length_by_type(cfield->real_field_type()) : cfield->length) < - key_part_length / key_part->field->charset()->mbmaxlen))) + key_part_length / kfield->charset()->mbmaxlen))) key_part_length= 0; // Use whole field } - key_part_length /= key_part->field->charset()->mbmaxlen; + key_part_length /= kfield->charset()->mbmaxlen; key_parts.push_back(new Key_part_spec(&cfield->field_name, key_part_length), thd->mem_root); @@ -8002,6 +8497,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } + if (table->versioned() && !(alter_info->flags & Alter_info::ALTER_DROP_SYSTEM_VERSIONING) && + new_create_list.elements == VERSIONING_FIELDS) + { + my_error(ER_VERS_TABLE_MUST_HAVE_COLUMNS, MYF(0), table->s->table_name.str); + goto err; + } + if (!create_info->comment.str) { create_info->comment.str= table->s->comment.str; @@ -8116,8 +8618,8 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info, { Field *old_field= new_field->field; - if (my_strcasecmp(system_charset_info, old_field->field_name.str, - new_field->field_name.str)) + if (lex_string_cmp(system_charset_info, &old_field->field_name, + &new_field->field_name)) { /* Copy algorithm doesn't support proper renaming of columns in @@ -8230,10 +8732,10 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, if ((drop->type == Alter_drop::FOREIGN_KEY) && (my_strcasecmp(system_charset_info, f_key->foreign_id->str, drop->name) == 0) && - (my_strcasecmp(table_alias_charset, f_key->foreign_db->str, - table->s->db.str) == 0) && - (my_strcasecmp(table_alias_charset, f_key->foreign_table->str, - table->s->table_name.str) == 0)) + (lex_string_cmp(table_alias_charset, f_key->foreign_db, + &table->s->db) == 0) && + (lex_string_cmp(table_alias_charset, f_key->foreign_table, + &table->s->table_name) == 0)) fk_parent_key_it.remove(); } } @@ -8283,9 +8785,9 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, StringBuffer<NAME_LEN*2+2> buff(system_charset_info); LEX_CSTRING *db= f_key->foreign_db, *tbl= f_key->foreign_table; - append_identifier(thd, &buff, db->str, db->length); + append_identifier(thd, &buff, db); buff.append('.'); - append_identifier(thd, &buff, tbl->str,tbl->length); + append_identifier(thd, &buff, tbl); my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD, MYF(0), bad_column_name, f_key->foreign_id->str, buff.c_ptr()); DBUG_RETURN(true); @@ -8401,8 +8903,8 @@ simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list, back to the original name (unlike the case for non-temporary tables), as it was an allocation error and the table was not renamed. */ - error= thd->rename_temporary_table(table, alter_ctx->new_db, - alter_ctx->new_alias); + error= thd->rename_temporary_table(table, &alter_ctx->new_db, + &alter_ctx->new_alias); } if (!error) @@ -8481,32 +8983,40 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, */ if (wait_while_table_is_used(thd, table, extra_func)) DBUG_RETURN(true); - close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL); - - LEX_CSTRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) }; - LEX_CSTRING old_table_name= - { alter_ctx->table_name, strlen(alter_ctx->table_name) }; - LEX_CSTRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) }; - LEX_CSTRING new_table_name= - { alter_ctx->new_alias, strlen(alter_ctx->new_alias) }; - (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name, - &new_db_name, &new_table_name); - - if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name, - alter_ctx->new_db, alter_ctx->new_alias, 0)) - error= -1; - else if (Table_triggers_list::change_table_name(thd, - alter_ctx->db, - alter_ctx->alias, - alter_ctx->table_name, - alter_ctx->new_db, - alter_ctx->new_alias)) - { - (void) mysql_rename_table(old_db_type, - alter_ctx->new_db, alter_ctx->new_alias, - alter_ctx->db, alter_ctx->table_name, - NO_FK_CHECKS); + close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, + NULL); + + (void) rename_table_in_stat_tables(thd, &alter_ctx->db, + &alter_ctx->table_name, + &alter_ctx->new_db, + &alter_ctx->new_alias); + + if (mysql_rename_table(old_db_type, &alter_ctx->db, &alter_ctx->table_name, + &alter_ctx->new_db, &alter_ctx->new_alias, 0)) error= -1; + else + { + VTMD_rename vtmd(*table_list); + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && + vtmd.try_rename(thd, alter_ctx->new_db.str, alter_ctx->new_alias.str)) + goto revert_table_name; + + if (Table_triggers_list::change_table_name(thd, + &alter_ctx->db, + &alter_ctx->alias, + &alter_ctx->table_name, + &alter_ctx->new_db, + &alter_ctx->new_alias)) + { + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + vtmd.revert_rename(thd, alter_ctx->new_db.str); + revert_table_name: + (void) mysql_rename_table(old_db_type, + &alter_ctx->new_db, &alter_ctx->new_alias, + &alter_ctx->db, &alter_ctx->table_name, + NO_FK_CHECKS); + error= -1; + } } } @@ -8575,7 +9085,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, based on information about the table changes from fill_alter_inplace_info(). */ -bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, +bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, @@ -8638,6 +9148,57 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, &alter_prelocking_strategy); thd->open_options&= ~HA_OPEN_FOR_ALTER; + TABLE *table= table_list->table; + bool versioned= table && table->versioned(); + bool vers_survival_mod= false; + + if (versioned) + { + if (handlerton *hton1= create_info->db_type) + { + handlerton *hton2= table->file->partition_ht(); + if (hton1 != hton2 && + (ha_check_storage_engine_flag(hton1, HTON_NATIVE_SYS_VERSIONING) || + ha_check_storage_engine_flag(hton2, HTON_NATIVE_SYS_VERSIONING))) + { + my_error(ER_VERS_ALTER_ENGINE_PROHIBITED, MYF(0), table_list->db.str, + table_list->table_name.str); + DBUG_RETURN(true); + } + } + bool vers_data_mod= alter_info->data_modifying(); + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + vers_survival_mod= alter_info->data_modifying() || alter_info->partition_modifying(); + } + else if (vers_data_mod && thd->variables.vers_alter_history == VERS_ALTER_HISTORY_ERROR) + { + my_error(ER_VERS_ALTER_NOT_ALLOWED, MYF(0), + table_list->db.str, table_list->table_name.str); + DBUG_RETURN(true); + } + } + + if (vers_survival_mod) + { + table_list->set_lock_type(thd, TL_WRITE); + if (thd->mdl_context.upgrade_shared_lock(table_list->table->mdl_ticket, + MDL_EXCLUSIVE, + thd->variables.lock_wait_timeout)) + { + DBUG_RETURN(true); + } + + if (table_list->table->versioned(VERS_TRX_ID) && + alter_info->requested_algorithm == + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT && + !table_list->table->s->partition_info_str) + { + // Changle default ALGORITHM to COPY for INNODB + alter_info->requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; + } + } + DEBUG_SYNC(thd, "alter_opened_table"); #ifdef WITH_WSREP @@ -8654,7 +9215,6 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (error) DBUG_RETURN(true); - TABLE *table= table_list->table; table->use_all_columns(); MDL_ticket *mdl_ticket= table->mdl_ticket; @@ -8687,9 +9247,9 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, If such table exists, there must be a corresponding TABLE_SHARE in THD::all_temp_tables list. */ - if (thd->find_tmp_table_share(alter_ctx.new_db, alter_ctx.new_name)) + if (thd->find_tmp_table_share(alter_ctx.new_db.str, alter_ctx.new_name.str)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str); DBUG_RETURN(true); } } @@ -8699,7 +9259,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, MDL_request target_db_mdl_request; target_mdl_request.init(MDL_key::TABLE, - alter_ctx.new_db, alter_ctx.new_name, + alter_ctx.new_db.str, alter_ctx.new_name.str, MDL_EXCLUSIVE, MDL_TRANSACTION); mdl_requests.push_front(&target_mdl_request); @@ -8710,7 +9270,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, */ if (alter_ctx.is_database_changed()) { - target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db, "", + target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db.str, "", MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION); mdl_requests.push_front(&target_db_mdl_request); @@ -8733,10 +9293,10 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. */ - if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name)) + if (ha_table_exists(thd, &alter_ctx.new_db, &alter_ctx.new_name)) { /* Table will be closed in do_command() */ - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias.str); DBUG_RETURN(true); } } @@ -8762,8 +9322,13 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, create_info->db_type= table->s->db_type(); } - if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info)) + if (check_engine(thd, alter_ctx.new_db.str, alter_ctx.new_name.str, create_info)) + DBUG_RETURN(true); + + if (create_info->vers_info.fix_alter_info(thd, alter_info, create_info, table)) + { DBUG_RETURN(true); + } if ((create_info->db_type != table->s->db_type() || alter_info->flags & Alter_info::ALTER_PARTITION) && @@ -8782,7 +9347,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, till this point for the alter operation. */ if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) && - check_fk_parent_table_access(thd, create_info, alter_info, new_db)) + check_fk_parent_table_access(thd, create_info, alter_info, new_db->str)) DBUG_RETURN(true); /* @@ -8808,7 +9373,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, { DBUG_PRINT("info", ("doesn't support alter")); my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str, - alter_ctx.db, alter_ctx.table_name); + alter_ctx.db.str, alter_ctx.table_name.str); DBUG_RETURN(true); } @@ -8817,7 +9382,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, { DBUG_PRINT("info", ("doesn't support alter")); my_error(ER_ILLEGAL_HA, MYF(0), hton_name(create_info->db_type)->str, - alter_ctx.new_db, alter_ctx.new_name); + alter_ctx.new_db.str, alter_ctx.new_name.str); DBUG_RETURN(true); } @@ -8835,10 +9400,10 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, */ if (alter_info->flags == 0) { - my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name), + my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff), ER_THD(thd, ER_INSERT_INFO), 0L, 0L, thd->get_stmt_da()->current_statement_warn_count()); - my_ok(thd, 0L, 0L, alter_ctx.tmp_name); + my_ok(thd, 0L, 0L, alter_ctx.tmp_buff); /* We don't replicate alter table statement on temporary tables */ if (table->s->tmp_table == NO_TMP_TABLE || @@ -8906,7 +9471,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, DBUG_RETURN(true); } - set_table_default_charset(thd, create_info, alter_ctx.db); + set_table_default_charset(thd, create_info, &alter_ctx.db); if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); @@ -8943,9 +9508,11 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE. Afterwards it's safe to take the table level lock. */ - if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE, - thd->variables.lock_wait_timeout) - || lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + if ((!vers_survival_mod && + thd->mdl_context.upgrade_shared_lock( + mdl_ticket, MDL_SHARED_NO_WRITE, + thd->variables.lock_wait_timeout)) || + lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) { DBUG_RETURN(true); } @@ -8953,8 +9520,8 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, // In-place execution of ALTER TABLE for partitioning. DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, - alter_ctx.db, - alter_ctx.table_name)); + &alter_ctx.db, + &alter_ctx.table_name)); } #endif @@ -9007,6 +9574,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, handlerton *new_db_type= create_info->db_type; handlerton *old_db_type= table->s->db_type(); TABLE *new_table= NULL; + bool new_versioned= false; ha_rows copied=0,deleted=0; /* @@ -9040,7 +9608,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (create_info->index_file_name) { /* Fix index_file_name to have 'tmp_name' as basename */ - strmov(index_file, alter_ctx.tmp_name); + strmov(index_file, alter_ctx.tmp_name.str); create_info->index_file_name=fn_same(index_file, create_info->index_file_name, 1); @@ -9048,7 +9616,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (create_info->data_file_name) { /* Fix data_file_name to have 'tmp_name' as basename */ - strmov(data_file, alter_ctx.tmp_name); + strmov(data_file, alter_ctx.tmp_name.str); create_info->data_file_name=fn_same(data_file, create_info->data_file_name, 1); @@ -9085,8 +9653,8 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, tmp_disable_binlog(thd); create_info->options|=HA_CREATE_TMP_ALTER; error= create_table_impl(thd, - alter_ctx.db, alter_ctx.table_name, - alter_ctx.new_db, alter_ctx.tmp_name, + &alter_ctx.db, &alter_ctx.table_name, + &alter_ctx.new_db, &alter_ctx.tmp_name, alter_ctx.get_tmp_path(), thd->lex->create_info, create_info, alter_info, C_ALTER_TABLE_FRM_ONLY, NULL, @@ -9144,8 +9712,9 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (!(altered_table= thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, - false))) + alter_ctx.new_db.str, + alter_ctx.tmp_name.str, + false, true))) goto err_new_table_cleanup; /* Set markers for fields in TABLE object for altered table. */ @@ -9160,10 +9729,12 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, &altered_table->s->all_set); restore_record(altered_table, s->default_values); // Create empty record /* Check that we can call default functions with default field values */ + thd->count_cuted_fields= CHECK_FIELD_EXPRESSION; altered_table->reset_default_fields(); if (altered_table->default_field && altered_table->update_default_fields(0, 1)) goto err_new_table_cleanup; + thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Ask storage engine whether to use copy or in-place enum_alter_inplace_result inplace_supported= @@ -9288,11 +9859,12 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, } // It's now safe to take the table level lock. - if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + if (lock_tables(thd, table_list, alter_ctx.tables_opened, + MYSQL_LOCK_USE_MALLOC)) goto err_new_table_cleanup; if (ha_create_table(thd, alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, + alter_ctx.new_db.str, alter_ctx.tmp_name.str, create_info, &frm)) goto err_new_table_cleanup; @@ -9304,8 +9876,9 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, TABLE *tmp_table= thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, - true); + alter_ctx.new_db.str, + alter_ctx.tmp_name.str, + true, true); if (!tmp_table) { goto err_new_table_cleanup; @@ -9319,9 +9892,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, if (table->s->tmp_table != NO_TMP_TABLE) { TABLE_LIST tbl; - tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db), - alter_ctx.tmp_name, strlen(alter_ctx.tmp_name), - alter_ctx.tmp_name, TL_READ_NO_INSERT); + tbl.init_one_table(&alter_ctx.new_db, &alter_ctx.tmp_name, 0, TL_READ_NO_INSERT); /* Table can be found in the list of open tables in THD::all_temp_tables list. @@ -9338,11 +9909,14 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, new_table= thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, - true); + alter_ctx.new_db.str, + alter_ctx.tmp_name.str, + true, true); } if (!new_table) goto err_new_table_cleanup; + new_table->s->orig_table_name= table->s->table_name.str; + new_versioned= new_table->versioned(); /* Note: In case of MERGE table, we do not attach children. We do not copy data for MERGE tables. Only the children have data. @@ -9369,7 +9943,14 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, order_num, order, &copied, &deleted, alter_info->keys_onoff, &alter_ctx)) + { + if (vers_survival_mod && new_versioned && table->versioned(VERS_TIMESTAMP)) + { + // Failure of this function may result in corruption of an original table. + vers_reset_alter_copy(thd, table); + } goto err_new_table_cleanup; + } } else { @@ -9409,8 +9990,8 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, /* Remove link to old table and rename the new one */ thd->drop_temporary_table(table, NULL, true); /* Should pass the 'new_name' as we store table name in the cache */ - if (thd->rename_temporary_table(new_table, alter_ctx.new_db, - alter_ctx.new_name)) + if (thd->rename_temporary_table(new_table, &alter_ctx.new_db, + &alter_ctx.new_name)) goto err_new_table_cleanup; /* We don't replicate alter table statement on temporary tables */ if (!thd->is_current_stmt_binlog_format_row() && @@ -9464,61 +10045,85 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, Rename the old table to temporary name to have a backup in case anything goes wrong while renaming the new table. */ - char backup_name[32]; - my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix, - current_pid, (long) thd->thread_id); + char backup_name_buff[FN_LEN]; + LEX_CSTRING backup_name; + backup_name.str= backup_name_buff; + + if (vers_survival_mod) + { + VTMD_table::archive_name(thd, alter_ctx.table_name.str, backup_name_buff, + sizeof(backup_name_buff)); + backup_name.length= strlen(backup_name_buff); + } + else + backup_name.length= my_snprintf(backup_name_buff, sizeof(backup_name_buff), + "%s2-%lx-%lx", tmp_file_prefix, + current_pid, (long) thd->thread_id); if (lower_case_table_names) - my_casedn_str(files_charset_info, backup_name); - if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name, - alter_ctx.db, backup_name, FN_TO_IS_TMP)) + my_casedn_str(files_charset_info, backup_name_buff); + if (mysql_rename_table(old_db_type, &alter_ctx.db, &alter_ctx.table_name, + &alter_ctx.db, &backup_name, FN_TO_IS_TMP)) { // Rename to temporary name failed, delete the new table, abort ALTER. - (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db, - alter_ctx.tmp_name, FN_IS_TMP); + (void) quick_rm_table(thd, new_db_type, &alter_ctx.new_db, + &alter_ctx.tmp_name, FN_IS_TMP); goto err_with_mdl; } // Rename the new table to the correct name. - if (mysql_rename_table(new_db_type, alter_ctx.new_db, alter_ctx.tmp_name, - alter_ctx.new_db, alter_ctx.new_alias, + if (mysql_rename_table(new_db_type, &alter_ctx.new_db, &alter_ctx.tmp_name, + &alter_ctx.new_db, &alter_ctx.new_alias, FN_FROM_IS_TMP)) { // Rename failed, delete the temporary table. - (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db, - alter_ctx.tmp_name, FN_IS_TMP); + (void) quick_rm_table(thd, new_db_type, &alter_ctx.new_db, + &alter_ctx.tmp_name, FN_IS_TMP); // Restore the backup of the original table to the old name. - (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name, - alter_ctx.db, alter_ctx.alias, + (void) mysql_rename_table(old_db_type, &alter_ctx.db, &backup_name, + &alter_ctx.db, &alter_ctx.alias, FN_FROM_IS_TMP | NO_FK_CHECKS); goto err_with_mdl; } + if (vers_survival_mod && new_versioned) + { + DBUG_ASSERT(alter_info && table_list); + VTMD_rename vtmd(*table_list); + bool rc= alter_info->flags & Alter_info::ALTER_RENAME ? + vtmd.try_rename(thd, alter_ctx.new_db.str, alter_ctx.new_alias.str, backup_name.str) : + vtmd.update(thd, backup_name.str); + if (rc) + goto err_after_rename; + } + // Check if we renamed the table and if so update trigger files. if (alter_ctx.is_table_renamed()) { if (Table_triggers_list::change_table_name(thd, - alter_ctx.db, - alter_ctx.alias, - alter_ctx.table_name, - alter_ctx.new_db, - alter_ctx.new_alias)) + &alter_ctx.db, + &alter_ctx.alias, + &alter_ctx.table_name, + &alter_ctx.new_db, + &alter_ctx.new_alias)) { +err_after_rename: // Rename succeeded, delete the new table. (void) quick_rm_table(thd, new_db_type, - alter_ctx.new_db, alter_ctx.new_alias, 0); + &alter_ctx.new_db, &alter_ctx.new_alias, 0); // Restore the backup of the original table to the old name. - (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name, - alter_ctx.db, alter_ctx.alias, + (void) mysql_rename_table(old_db_type, &alter_ctx.db, &backup_name, + &alter_ctx.db, &alter_ctx.alias, FN_FROM_IS_TMP | NO_FK_CHECKS); goto err_with_mdl; } - rename_table_in_stat_tables(thd, alter_ctx.db,alter_ctx.alias, - alter_ctx.new_db, alter_ctx.new_alias); + rename_table_in_stat_tables(thd, &alter_ctx.db, &alter_ctx.alias, + &alter_ctx.new_db, &alter_ctx.new_alias); } // ALTER TABLE succeeded, delete the backup of the old table. - if (quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP)) + if (!(vers_survival_mod && new_versioned) && + quick_rm_table(thd, old_db_type, &alter_ctx.db, &backup_name, FN_IS_TMP)) { /* The fact that deletion of the backup failed is not critical @@ -9556,11 +10161,11 @@ end_inplace: } end_temporary: - my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name), + my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff), ER_THD(thd, ER_INSERT_INFO), (ulong) (copied + deleted), (ulong) deleted, (ulong) thd->get_stmt_da()->current_statement_warn_count()); - my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_name); + my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_buff); DBUG_RETURN(false); err_new_table_cleanup: @@ -9571,7 +10176,7 @@ err_new_table_cleanup: } else (void) quick_rm_table(thd, new_db_type, - alter_ctx.new_db, alter_ctx.tmp_name, + &alter_ctx.new_db, &alter_ctx.tmp_name, (FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)), alter_ctx.get_tmp_path()); @@ -9705,6 +10310,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, sql_mode_t save_sql_mode= thd->variables.sql_mode; ulonglong prev_insert_id, time_to_report_progress; Field **dfield_ptr= to->default_field; + bool make_versioned= !from->versioned() && to->versioned(); + bool make_unversioned= from->versioned() && !to->versioned(); + bool keep_versioned= from->versioned() && to->versioned(); + Field *to_row_start= NULL, *to_row_end= NULL, *from_row_end= NULL; + MYSQL_TIME query_start; DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ @@ -9713,7 +10323,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (mysql_trans_prepare_alter_copy_data(thd)) DBUG_RETURN(-1); - if (!(copy= new Copy_field[to->s->fields])) + if (!(copy= new (thd->mem_root) Copy_field[to->s->fields])) DBUG_RETURN(-1); /* purecov: inspected */ /* We need external lock before we can disable/enable keys */ @@ -9787,8 +10397,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, { bzero((char *) &tables, sizeof(tables)); tables.table= from; - tables.alias= tables.table_name= from->s->table_name.str; - tables.db= from->s->db.str; + tables.alias= tables.table_name= from->s->table_name; + tables.db= from->s->db; THD_STAGE_INFO(thd, stage_sorting); Filesort_tracker dummy_tracker(false); @@ -9805,6 +10415,29 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd_progress_next_stage(thd); } + if (make_versioned) + { + query_start= thd->query_start_TIME(); + to_row_start= to->vers_start_field(); + to_row_end= to->vers_end_field(); + } + else if (make_unversioned) + { + from_row_end= from->vers_end_field(); + } + else if (keep_versioned) + { + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + query_start= thd->query_start_TIME(); + from_row_end= from->vers_end_field(); + to_row_start= to->vers_start_field(); + } else if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP) + { + from_row_end= from->vers_end_field(); + } + } + THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); @@ -9827,7 +10460,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (!ignore) /* for now, InnoDB needs the undo log for ALTER IGNORE */ to->file->extra(HA_EXTRA_BEGIN_ALTER_COPY); - while (!(error=info.read_record(&info))) + while (!(error= info.read_record())) { if (thd->killed) { @@ -9860,6 +10493,36 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, { copy_ptr->do_copy(copy_ptr); } + + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP && + from_row_end && !from_row_end->is_max()) + { + continue; + } + + if (make_versioned) + { + to_row_start->set_notnull(); + to_row_start->store_time(&query_start); + to_row_end->set_max(); + } + else if (make_unversioned) + { + if (!from_row_end->is_max()) + continue; // Drop history rows. + } + else if (keep_versioned && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + if (!from_row_end->is_max()) + continue; // Do not copy history rows. + + store_record(from, record[1]); + from->vers_end_field()->store_time(&query_start); + from->file->ha_update_row(from->record[1], from->record[0]); + to_row_start->store_time(&query_start); + } + prev_insert_id= to->file->next_insert_id; if (to->default_field) to->update_default_fields(0, ignore); @@ -9874,7 +10537,12 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, error= 1; break; } - error=to->file->ha_write_row(to->record[0]); + if (keep_versioned && to->versioned(VERS_TRX_ID) && + thd->variables.vers_alter_history != VERS_ALTER_HISTORY_SURVIVE) + { + to->vers_write= false; + } + error= to->file->ha_write_row(to->record[0]); to->auto_increment_field_not_null= FALSE; if (error) { @@ -9999,8 +10667,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy) HA_CREATE_INFO create_info; Alter_info alter_info; TABLE_LIST *next_table= table_list->next_global; - DBUG_ENTER("mysql_recreate_table"); + /* Set lock type which is appropriate for ALTER TABLE. */ table_list->lock_type= TL_READ_NO_INSERT; /* Same applies to MDL request. */ @@ -10018,7 +10686,9 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy) if (table_copy) alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; - bool res= mysql_alter_table(thd, NullS, NullS, &create_info, + thd->prepare_logs_for_admin_command(); + + bool res= mysql_alter_table(thd, &null_clex_str, &null_clex_str, &create_info, table_list, &alter_info, 0, (ORDER *) 0, 0); table_list->next_global= next_table; @@ -10081,7 +10751,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, TABLE *t; TABLE_LIST *save_next_global; - strxmov(table_name, table->db ,".", table->table_name, NullS); + strxmov(table_name, table->db.str ,".", table->table_name.str, NullS); /* Remember old 'next' pointer and break the list. */ save_next_global= table->next_global; diff --git a/sql/sql_table.h b/sql/sql_table.h index 2e080462deb..9958a56958e 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -17,8 +17,7 @@ #ifndef SQL_TABLE_INCLUDED #define SQL_TABLE_INCLUDED -#include "my_global.h" /* my_bool */ -#include "my_sys.h" // pthread_mutex_t +#include <my_sys.h> // pthread_mutex_t #include "m_string.h" // LEX_CUSTRING class Alter_info; @@ -142,10 +141,10 @@ static const uint SKIP_SYMDIR_ACCESS= 1 << 5; /** Don't check foreign key constraints while renaming table */ static const uint NO_FK_CHECKS= 1 << 6; -uint filename_to_tablename(const char *from, char *to, uint to_length, +uint filename_to_tablename(const char *from, char *to, size_t to_length, bool stay_quiet = false); -uint tablename_to_filename(const char *from, char *to, uint to_length); -uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length); +uint tablename_to_filename(const char *from, char *to, size_t to_length); +uint check_n_cut_mysql50_prefix(const char *from, char *to, size_t to_length); bool check_mysql50_prefix(const char *name); uint build_table_filename(char *buff, size_t bufflen, const char *db, const char *table, const char *ext, uint flags); @@ -192,14 +191,15 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, #define C_ALTER_TABLE_FRM_ONLY -2 #define C_ASSISTED_DISCOVERY -3 -int mysql_create_table_no_lock(THD *thd, const char *db, - const char *table_name, +int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, int create_table_mode, TABLE_LIST *table); handler *mysql_create_frm_image(THD *thd, - const char *db, const char *table_name, + const LEX_CSTRING *db, + const LEX_CSTRING *table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, int create_table_mode, @@ -217,7 +217,7 @@ bool mysql_prepare_alter_table(THD *thd, TABLE *table, Alter_table_ctx *alter_ctx); bool mysql_trans_prepare_alter_copy_data(THD *thd); bool mysql_trans_commit_alter_copy_data(THD *thd); -bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, +bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, @@ -230,9 +230,9 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy); bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table, Table_specification_st *create_info); -bool mysql_rename_table(handlerton *base, const char *old_db, - const char * old_name, const char *new_db, - const char * new_name, uint flags); +bool mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, + const LEX_CSTRING *old_name, const LEX_CSTRING *new_db, + const LEX_CSTRING *new_name, uint flags); bool mysql_backup_table(THD* thd, TABLE_LIST* table_list); bool mysql_restore_table(THD* thd, TABLE_LIST* table_list); @@ -245,11 +245,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, bool drop_sequence, bool log_query, bool dont_free_locks); -bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, - const char *table_name, size_t table_name_length, - bool temporary_table); -bool quick_rm_table(THD *thd, handlerton *base, const char *db, - const char *table_name, uint flags, +bool log_drop_table(THD *thd, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, bool temporary_table); +bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, + const LEX_CSTRING *table_name, uint flags, const char *table_path=0); void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Column_definition *sql_field); diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 8b9e14e5a18..93a3007d1ea 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -15,7 +15,7 @@ /* drop and alter of tablespaces */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_tablespace.h" diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 56d10ddaa13..3d43c35177d 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -16,7 +16,7 @@ /* Write some debug info */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_test.h" diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 986be049810..95435c698f8 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -17,7 +17,7 @@ /* Functions to handle date and time */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_time.h" #include "tztime.h" // struct Time_zone @@ -256,8 +256,8 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) */ static uint to_ascii(CHARSET_INFO *cs, - const char *src, uint src_length, - char *dst, uint dst_length) + const char *src, size_t src_length, + char *dst, size_t dst_length) { int cnvres; @@ -280,7 +280,7 @@ to_ascii(CHARSET_INFO *cs, /* Character set-aware version of str_to_time() */ bool -str_to_time(CHARSET_INFO *cs, const char *str,uint length, +str_to_time(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) { char cnv[32]; @@ -294,7 +294,7 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length, /* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length, +bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time, ulonglong flags, MYSQL_TIME_STATUS *status) { @@ -318,7 +318,7 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length, bool str_to_datetime_with_warn(CHARSET_INFO *cs, - const char *str, uint length, MYSQL_TIME *l_time, + const char *str, size_t length, MYSQL_TIME *l_time, ulonglong flags) { MYSQL_TIME_STATUS status; @@ -474,6 +474,7 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) to->second= (int) from->tm_sec; } + void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) { long t_seconds; @@ -757,7 +758,7 @@ DATE_TIME_FORMAT !parse_date_time_format(format_type, format_str, format_length, &tmp)) { - tmp.format.str= (char*) format_str; + tmp.format.str= format_str; tmp.format.length= format_length; return date_time_format_copy((THD *)0, &tmp); } @@ -784,7 +785,8 @@ DATE_TIME_FORMAT DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format) { DATE_TIME_FORMAT *new_format; - ulong length= sizeof(*format) + format->format.length + 1; + size_t length= sizeof(*format) + format->format.length + 1; + char *format_pos; if (thd) new_format= (DATE_TIME_FORMAT *) thd->alloc(length); @@ -793,14 +795,13 @@ DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format) if (new_format) { /* Put format string after current pos */ - new_format->format.str= (char*) (new_format+1); + new_format->format.str= format_pos= (char*) (new_format+1); memcpy((char*) new_format->positions, (char*) format->positions, sizeof(format->positions)); new_format->time_separator= format->time_separator; /* We make the string null terminated for easy printf in SHOW VARIABLES */ - memcpy((char*) new_format->format.str, format->format.str, - format->format.length); - new_format->format.str[format->format.length]= 0; + memcpy(format_pos, format->format.str, format->format.length); + format_pos[format->format.length]= 0; new_format->format.length= format->format.length; } return new_format; @@ -905,7 +906,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, { long period, sign; - sign= (interval.neg == ltime->neg ? 1 : -1); + sign= (interval.neg == (bool)ltime->neg ? 1 : -1); switch (int_type) { case INTERVAL_SECOND: diff --git a/sql/sql_time.h b/sql/sql_time.h index 4e8f280514f..94f24be3748 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -17,7 +17,6 @@ #ifndef SQL_TIME_INCLUDED #define SQL_TIME_INCLUDED -#include "my_global.h" /* ulong */ #include "my_time.h" #include "mysql_time.h" /* timestamp_type */ #include "sql_error.h" /* Sql_condition */ @@ -39,8 +38,7 @@ 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, - uint length, MYSQL_TIME *l_time, +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, @@ -123,8 +121,7 @@ void make_truncated_value_warning(THD *thd, const char *field_name); static inline void make_truncated_value_warning(THD *thd, - Sql_condition::enum_warning_level level, const char *str_val, - uint str_length, timestamp_type time_type, + Sql_condition::enum_warning_level level, const char *str_val, size_t str_length, timestamp_type time_type, const char *field_name) { const ErrConvString str(str_val, str_length, &my_charset_bin); @@ -171,6 +168,7 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, int lsign, MYSQL_TIME *l_time3, ulonglong 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, long seconds, long microseconds); uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); @@ -179,12 +177,12 @@ 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,uint length, +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, uint length, + const char *str, size_t length, MYSQL_TIME *l_time, ulonglong flags, MYSQL_TIME_STATUS *status); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index a919994c90c..29bf7aeb205 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -16,7 +16,7 @@ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sp_head.h" @@ -35,7 +35,7 @@ #include <mysys_err.h> LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, - const char* str, uint length, + const char* str, size_t length, MEM_ROOT *mem_root) { if (!(lex_str->str= strmake_root(mem_root, str, length))) @@ -164,7 +164,7 @@ Trigger_creation_ctx::create(THD *thd, /*************************************************************************/ static const LEX_CSTRING triggers_file_type= - { C_STRING_WITH_LEN("TRIGGERS") }; + { STRING_WITH_LEN("TRIGGERS") }; const char * const TRG_EXT= ".TRG"; @@ -421,7 +421,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) need second part of condition below, since check_access() function also checks that db is specified. */ - if (!thd->lex->spname->m_db.length || (create && !tables->db_length)) + if (!thd->lex->spname->m_db.length || (create && !tables->db.length)) { my_error(ER_NO_DB_ERROR, MYF(0)); DBUG_RETURN(TRUE); @@ -430,7 +430,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* We don't allow creating triggers on tables in the 'mysql' schema */ - if (create && !my_strcasecmp(system_charset_info, "mysql", tables->db)) + if (create && !my_strcasecmp(system_charset_info, "mysql", tables->db.str)) { my_error(ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA, MYF(0)); DBUG_RETURN(TRUE); @@ -522,7 +522,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* We do not allow creation of triggers on temporary tables. */ if (create && thd->find_tmp_table_share(tables)) { - my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias.str); goto end; } @@ -540,8 +540,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (thd->locked_tables_mode) { /* Under LOCK TABLES we must only accept write locked tables. */ - if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db, - tables->table_name, + if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db.str, + tables->table_name.str, FALSE))) goto end; } @@ -654,7 +654,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, { LEX_CSTRING stmt_definition; LEX *lex= thd->lex; - uint prefix_trimmed, suffix_trimmed; + size_t prefix_trimmed, suffix_trimmed; size_t original_length; /* @@ -682,7 +682,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, /* Create statement for binary logging */ - stmt_definition.str= (char*) lex->stmt_definition_begin; + stmt_definition.str= lex->stmt_definition_begin; stmt_definition.length= (lex->stmt_definition_end - lex->stmt_definition_begin); original_length= stmt_definition.length; @@ -693,7 +693,13 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, /* Create statement for storing trigger (without trigger order) */ if (lex->trg_chistics.ordering_clause == TRG_ORDER_NONE) + { + /* + Not that here stmt_definition doesn't end with a \0, which is + normally expected from a LEX_CSTRING + */ trigger_def->append(stmt_definition.str, stmt_definition.length); + } else { /* Copy data before FOLLOWS/PRECEDES trigger_name */ @@ -755,8 +761,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, DBUG_RETURN(true); /* Trigger must be in the same schema as target table. */ - if (my_strcasecmp(table_alias_charset, table->s->db.str, - lex->spname->m_db.str)) + if (lex_string_cmp(table_alias_charset, &table->s->db, &lex->spname->m_db)) { my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); DBUG_RETURN(true); @@ -814,11 +819,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, versions */ file.length= build_table_filename(file_buff, FN_REFLEN - 1, - tables->db, tables->table_name, + tables->db.str, tables->table_name.str, TRG_EXT, 0); file.str= file_buff; trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, - tables->db, + tables->db.str, lex->spname->m_name.str, TRN_EXT, 0); trigname_file.str= trigname_buff; @@ -839,7 +844,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } else if (lex->create_info.if_not_exists()) { - strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db, ".", + strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db.str, ".", lex->spname->m_name.str, NullS); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TRG_ALREADY_EXISTS, @@ -858,15 +863,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } else { - strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db, ".", + strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db.str, ".", lex->spname->m_name.str, NullS); my_error(ER_TRG_ALREADY_EXISTS, MYF(0), trigname_buff); DBUG_RETURN(true); } } - trigname.trigger_table.str= tables->table_name; - trigname.trigger_table.length= tables->table_name_length; + trigname.trigger_table= tables->table_name; /* We are not using lex->sphead here as an argument to Trigger() as we are @@ -901,7 +905,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, lex_string_set(&trigger->connection_cl_name, thd->variables.collation_connection->name); lex_string_set(&trigger->db_cl_name, - get_default_db_collation(thd, tables->db)->name); + get_default_db_collation(thd, tables->db.str)->name); /* Add trigger in it's correct place */ add_trigger(lex->trg_chistics.event, @@ -928,8 +932,8 @@ err_without_cleanup: if (trigger_dropped) { String drop_trg_query; - drop_trg_query.append("DROP TRIGGER /* generated by failed CREATE TRIGGER */ "); - drop_trg_query.append(lex->spname->m_name.str); + drop_trg_query.append(STRING_WITH_LEN("DROP TRIGGER /* generated by failed CREATE TRIGGER */ ")); + drop_trg_query.append(&lex->spname->m_name); /* We dropped an existing trigger and was not able to recreate it because of an internal error. Ensure it's also dropped on the slave. @@ -1009,10 +1013,10 @@ bool Trigger::add_to_file_list(void* param_arg) True error */ -static bool rm_trigger_file(char *path, const char *db, - const char *table_name) +static bool rm_trigger_file(char *path, const LEX_CSTRING *db, + const LEX_CSTRING *table_name) { - build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0); + build_table_filename(path, FN_REFLEN-1, db->str, table_name->str, TRG_EXT, 0); return mysql_file_delete(key_file_trg, path, MYF(MY_WME)); } @@ -1031,10 +1035,10 @@ static bool rm_trigger_file(char *path, const char *db, True error */ -static bool rm_trigname_file(char *path, const char *db, - const char *trigger_name) +static bool rm_trigname_file(char *path, const LEX_CSTRING *db, + const LEX_CSTRING *trigger_name) { - build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0); + build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str, TRN_EXT, 0); return mysql_file_delete(key_file_trn, path, MYF(MY_WME)); } @@ -1052,8 +1056,8 @@ static bool rm_trigname_file(char *path, const char *db, TRUE Error */ -bool Table_triggers_list::save_trigger_file(THD *thd, const char *db, - const char *table_name) +bool Table_triggers_list::save_trigger_file(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *table_name) { char file_buff[FN_REFLEN]; LEX_CSTRING file; @@ -1061,7 +1065,7 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const char *db, if (create_lists_needed_for_files(thd->mem_root)) return true; - file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name, + file.length= build_table_filename(file_buff, FN_REFLEN - 1, db->str, table_name->str, TRG_EXT, 0); file.str= file_buff; return sql_create_definition_file(NULL, &file, &triggers_file_type, @@ -1089,8 +1093,8 @@ Trigger *Table_triggers_list::find_trigger(const LEX_CSTRING *name, (trigger= *parent); parent= &trigger->next) { - if (my_strcasecmp(table_alias_charset, - trigger->name.str, name->str) == 0) + if (lex_string_cmp(table_alias_charset, + &trigger->name, name) == 0) { if (remove_from_list) { @@ -1153,16 +1157,16 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, parse_file.cc functionality (because we will need it elsewhere). */ - if (rm_trigger_file(path, tables->db, tables->table_name)) + if (rm_trigger_file(path, &tables->db, &tables->table_name)) return 1; } else { - if (save_trigger_file(thd, tables->db, tables->table_name)) + if (save_trigger_file(thd, &tables->db, &tables->table_name)) return 1; } - if (rm_trigname_file(path, tables->db, sp_name->str)) + if (rm_trigname_file(path, &tables->db, sp_name)) return 1; delete trigger; @@ -1228,7 +1232,7 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table) uchar null_bit= 1; for (fld= table->field, trg_fld= record0_field; *fld; fld++, trg_fld++) { - if (!(*fld)->null_ptr && !(*fld)->vcol_info) + if (!(*fld)->null_ptr && !(*fld)->vcol_info && !(*fld)->vers_sys_field()) { Field *f; if (!(f= *trg_fld= (*fld)->make_new_field(&table->mem_root, table, @@ -1251,7 +1255,9 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table) bzero(extra_null_bitmap, null_bytes); } else + { record0_field= table->field; + } if (has_triggers(TRG_EVENT_UPDATE,TRG_ACTION_BEFORE) || has_triggers(TRG_EVENT_UPDATE,TRG_ACTION_AFTER) || @@ -1299,18 +1305,18 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table) True error */ -bool Table_triggers_list::check_n_load(THD *thd, const char *db, - const char *table_name, TABLE *table, +bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *table_name, TABLE *table, bool names_only) { char path_buff[FN_REFLEN]; LEX_CSTRING path; File_parser *parser; - LEX_STRING save_db; + LEX_CSTRING save_db; DBUG_ENTER("Table_triggers_list::check_n_load"); path.length= build_table_filename(path_buff, FN_REFLEN - 1, - db, table_name, TRG_EXT, 0); + db->str, table_name->str, TRG_EXT, 0); path.str= path_buff; // QQ: should we analyze errno somehow ? @@ -1349,8 +1355,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRG_NO_CREATION_CTX, ER_THD(thd, ER_TRG_NO_CREATION_CTX), - (const char*) db, - (const char*) table_name); + db->str, + table_name->str); } table->triggers= trigger_list; @@ -1369,9 +1375,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->lex= thd->stmt_lex= &lex; - save_db.str= thd->db; - save_db.length= thd->db_length; - thd->reset_db((char*) db, strlen(db)); + save_db= thd->db; + thd->reset_db(db); while ((trg_create_str= it++)) { sp_head *sp; @@ -1399,8 +1404,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!trigger_list->client_cs_names.is_empty()) creation_ctx= Trigger_creation_ctx::create(thd, - db, - table_name, + db->str, + table_name->str, it_client_cs_name++, it_connection_cl_name++, it_db_cl_name++); @@ -1509,8 +1514,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRG_NO_DEFINER, ER_THD(thd, ER_TRG_NO_DEFINER), - (const char*) db, - (const char*) sp->m_name.str); + db->str, sp->m_name.str); /* Set definer to the '' to correct displaying in the information @@ -1546,12 +1550,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, */ char fname[SAFE_NAME_LEN + 1]; - DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) || - (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) && - !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname)))); - DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, table_name) || - (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) && - !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname)))); + DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db.str, db->str) || + (check_n_cut_mysql50_prefix(db->str, fname, sizeof(fname)) && + !my_strcasecmp(table_alias_charset, lex.query_tables->db.str, fname)))); + DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->table_name.str, table_name->str) || + (check_n_cut_mysql50_prefix(table_name->str, fname, sizeof(fname)) && + !my_strcasecmp(table_alias_charset, lex.query_tables->table_name.str, fname)))); #endif if (names_only) { @@ -1584,7 +1588,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_end(&lex); } - thd->reset_db(save_db.str, save_db.length); + thd->reset_db(&save_db); thd->lex= old_lex; thd->stmt_lex= old_stmt_lex; thd->spcont= save_spcont; @@ -1603,8 +1607,8 @@ err_with_lex_cleanup: thd->stmt_lex= old_stmt_lex; thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; - thd->reset_db(save_db.str, save_db.length); - /* Fall through to error */ + thd->reset_db(&save_db); + /* Fall trough to error */ } } @@ -1616,7 +1620,7 @@ error: be merged into .FRM anyway. */ my_error(ER_WRONG_OBJECT, MYF(0), - table_name, TRG_EXT + 1, "TRIGGER"); + table_name->str, TRG_EXT + 1, "TRIGGER"); } DBUG_RETURN(1); } @@ -1641,8 +1645,8 @@ void Table_triggers_list::add_trigger(trg_event_type event, for ( ; *parent ; parent= &(*parent)->next, position++) { if (ordering_clause != TRG_ORDER_NONE && - !my_strcasecmp(table_alias_charset, anchor_trigger_name->str, - (*parent)->name.str)) + !lex_string_cmp(table_alias_charset, anchor_trigger_name, + &(*parent)->name)) { if (ordering_clause == TRG_ORDER_FOLLOWS) { @@ -1671,8 +1675,8 @@ void Table_triggers_list::add_trigger(trg_event_type event, /** Obtains and returns trigger metadata. - @param thd current thread context @param trigger_stmt returns statement of trigger + @param body returns body of trigger @param definer returns definer/creator of trigger. The caller is responsible to allocate enough space for storing definer information. @@ -1736,12 +1740,12 @@ bool add_table_for_trigger(THD *thd, { LEX *lex= thd->lex; char trn_path_buff[FN_REFLEN]; - LEX_STRING trn_path= { trn_path_buff, 0 }; + LEX_CSTRING trn_path= { trn_path_buff, 0 }; LEX_CSTRING tbl_name= null_clex_str; DBUG_ENTER("add_table_for_trigger"); - build_trn_path(thd, trg_name, &trn_path); + build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path); if (check_trn_exists(&trn_path)) { @@ -1761,12 +1765,11 @@ bool add_table_for_trigger(THD *thd, DBUG_RETURN(TRUE); } - if (load_table_name_for_trigger(thd, trg_name, (LEX_CSTRING*) &trn_path, - &tbl_name)) + if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) DBUG_RETURN(TRUE); - *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str, - tbl_name.str, TL_IGNORE, + *table= sp_add_to_query_tables(thd, lex, &trg_name->m_db, + &tbl_name, TL_IGNORE, MDL_SHARED_NO_WRITE); DBUG_RETURN(*table ? FALSE : TRUE); @@ -1786,8 +1789,8 @@ bool add_table_for_trigger(THD *thd, True error */ -bool Table_triggers_list::drop_all_triggers(THD *thd, const char *db, - const char *name) +bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *name) { TABLE table; char path[FN_REFLEN]; @@ -1820,7 +1823,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const char *db, Such triggers have zero-length name and are skipped here. */ if (trigger->name.length && - rm_trigname_file(path, db, trigger->name.str)) + rm_trigname_file(path, db, &trigger->name)) { /* Instead of immediately bailing out with error if we were unable @@ -1860,26 +1863,27 @@ end: struct change_table_name_param { THD *thd; - const char *old_db_name; - const char *new_db_name; + LEX_CSTRING *old_db_name; + LEX_CSTRING *new_db_name; LEX_CSTRING *new_table_name; Trigger *stopper; }; bool -Table_triggers_list::change_table_name_in_triggers(THD *thd, - const char *old_db_name, - const char *new_db_name, - LEX_CSTRING *old_table_name, - LEX_CSTRING *new_table_name) +Table_triggers_list:: +change_table_name_in_triggers(THD *thd, + const LEX_CSTRING *old_db_name, + const LEX_CSTRING *new_db_name, + const LEX_CSTRING *old_table_name, + const LEX_CSTRING *new_table_name) { struct change_table_name_param param; sql_mode_t save_sql_mode= thd->variables.sql_mode; char path_buff[FN_REFLEN]; param.thd= thd; - param.new_table_name= new_table_name; + param.new_table_name= const_cast<LEX_CSTRING*>(new_table_name); for_all_triggers(&Trigger::change_table_name, ¶m); @@ -1888,12 +1892,12 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, if (thd->is_fatal_error) return TRUE; /* OOM */ - if (save_trigger_file(thd, new_db_name, new_table_name->str)) + if (save_trigger_file(thd, new_db_name, new_table_name)) return TRUE; - if (rm_trigger_file(path_buff, old_db_name, old_table_name->str)) + if (rm_trigger_file(path_buff, old_db_name, old_table_name)) { - (void) rm_trigger_file(path_buff, new_db_name, new_table_name->str); + (void) rm_trigger_file(path_buff, new_db_name, new_table_name); return TRUE; } return FALSE; @@ -1921,7 +1925,7 @@ bool Trigger::change_table_name(void* param_arg) buff.append(def->str, before_on_len); buff.append(STRING_WITH_LEN("ON ")); - append_identifier(thd, &buff, new_table_name->str, new_table_name->length); + append_identifier(thd, &buff, new_table_name); buff.append(STRING_WITH_LEN(" ")); on_q_table_name_len= buff.length() - before_on_len; buff.append(on_table_name.str + on_table_name.length, @@ -1958,15 +1962,16 @@ bool Trigger::change_table_name(void* param_arg) */ Trigger * -Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, - const char *new_db_name, - LEX_CSTRING *new_table_name, - Trigger *trigger) +Table_triggers_list:: +change_table_name_in_trignames(const LEX_CSTRING *old_db_name, + const LEX_CSTRING *new_db_name, + const LEX_CSTRING *new_table_name, + Trigger *trigger) { struct change_table_name_param param; - param.old_db_name= old_db_name; - param.new_db_name= new_db_name; - param.new_table_name= new_table_name; + param.old_db_name= const_cast<LEX_CSTRING*>(old_db_name); + param.new_db_name= const_cast<LEX_CSTRING*>(new_db_name); + param.new_table_name= const_cast<LEX_CSTRING*>(new_table_name); param.stopper= trigger; return for_all_triggers(&Trigger::change_on_table_name, ¶m); @@ -1985,7 +1990,7 @@ bool Trigger::change_on_table_name(void* param_arg) return 0; // Stop processing trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, - param->new_db_name, name.str, + param->new_db_name->str, name.str, TRN_EXT, 0); trigname_file.str= trigname_buff; @@ -2001,9 +2006,9 @@ bool Trigger::change_on_table_name(void* param_arg) /* Remove stale .TRN file in case of database upgrade */ if (param->old_db_name) { - if (rm_trigname_file(trigname_buff, param->old_db_name, name.str)) + if (rm_trigname_file(trigname_buff, param->old_db_name, &name)) { - (void) rm_trigname_file(trigname_buff, param->new_db_name, name.str); + (void) rm_trigname_file(trigname_buff, param->new_db_name, &name); return 1; } } @@ -2032,11 +2037,11 @@ bool Trigger::change_on_table_name(void* param_arg) @retval TRUE Error */ -bool Table_triggers_list::change_table_name(THD *thd, const char *db, - const char *old_alias, - const char *old_table, - const char *new_db, - const char *new_table) +bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *old_alias, + const LEX_CSTRING *old_table, + const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table) { TABLE table; bool result= 0; @@ -2052,11 +2057,12 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, This method interfaces the mysql server code protected by an exclusive metadata lock. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, old_table, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db->str, + old_table->str, MDL_EXCLUSIVE)); - DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) || - my_strcasecmp(table_alias_charset, old_alias, new_table)); + DBUG_ASSERT(my_strcasecmp(table_alias_charset, db->str, new_db->str) || + my_strcasecmp(table_alias_charset, old_alias->str, new_table->str)); if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE)) { @@ -2070,8 +2076,6 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, result= 1; goto end; } - LEX_CSTRING old_table_name= { (char *) old_alias, strlen(old_alias) }; - LEX_CSTRING new_table_name= { (char *) new_table, strlen(new_table) }; /* Since triggers should be in the same schema as their subject tables moving table with them between two schemas raises too many questions. @@ -2082,11 +2086,11 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, we will be given table name with "#mysql50#" prefix To remove this prefix we use check_n_cut_mysql50_prefix(). */ - if (my_strcasecmp(table_alias_charset, db, new_db)) + if (my_strcasecmp(table_alias_charset, db->str, new_db->str)) { char dbname[SAFE_NAME_LEN + 1]; - if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) && - !my_strcasecmp(table_alias_charset, dbname, new_db)) + if (check_n_cut_mysql50_prefix(db->str, dbname, sizeof(dbname)) && + !my_strcasecmp(table_alias_charset, dbname, new_db->str)) { upgrading50to51= TRUE; } @@ -2098,15 +2102,15 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, } } if (table.triggers->change_table_name_in_triggers(thd, db, new_db, - &old_table_name, - &new_table_name)) + old_alias, + new_table)) { result= 1; goto end; } if ((err_trigger= table.triggers-> change_table_name_in_trignames( upgrading50to51 ? db : NULL, - new_db, &new_table_name, 0))) + new_db, new_table, 0))) { /* If we were unable to update one of .TRN files properly we will @@ -2116,10 +2120,10 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, */ (void) table.triggers->change_table_name_in_trignames( upgrading50to51 ? new_db : NULL, db, - &old_table_name, err_trigger); + old_alias, err_trigger); (void) table.triggers->change_table_name_in_triggers( thd, db, new_db, - &new_table_name, &old_table_name); + new_table, old_alias); result= 1; goto end; } @@ -2461,7 +2465,7 @@ void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path) @retval FALSE if TRN-file exists. */ -bool check_trn_exists(const LEX_STRING *trn_path) +bool check_trn_exists(const LEX_CSTRING *trn_path) { return access(trn_path->str, F_OK) != 0; } diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 8847680c7b2..0fddb94fde1 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -223,17 +223,17 @@ public: bool old_row_is_record1); void empty_lists(); bool create_lists_needed_for_files(MEM_ROOT *root); - bool save_trigger_file(THD *thd, const char *db, const char *table_name); + bool save_trigger_file(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name); - static bool check_n_load(THD *thd, const char *db, const char *table_name, + static bool check_n_load(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, TABLE *table, bool names_only); - static bool drop_all_triggers(THD *thd, const char *db, - const char *table_name); - static bool change_table_name(THD *thd, const char *db, - const char *old_alias, - const char *old_table, - const char *new_db, - const char *new_table); + static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *table_name); + static bool change_table_name(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *old_alias, + const LEX_CSTRING *old_table, + const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table); void add_trigger(trg_event_type event_type, trg_action_time_type action_time, trigger_order_type ordering_clause, @@ -286,15 +286,15 @@ public: private: bool prepare_record_accessors(TABLE *table); - Trigger *change_table_name_in_trignames(const char *old_db_name, - const char *new_db_name, - LEX_CSTRING *new_table_name, + Trigger *change_table_name_in_trignames(const LEX_CSTRING *old_db_name, + const LEX_CSTRING *new_db_name, + const LEX_CSTRING *new_table_name, Trigger *trigger); bool change_table_name_in_triggers(THD *thd, - const char *old_db_name, - const char *new_db_name, - LEX_CSTRING *old_table_name, - LEX_CSTRING *new_table_name); + const LEX_CSTRING *old_db_name, + const LEX_CSTRING *new_db_name, + const LEX_CSTRING *old_table_name, + const LEX_CSTRING *new_table_name); bool check_for_broken_triggers() { @@ -307,13 +307,6 @@ private: } }; -inline Field **TABLE::field_to_fill() -{ - return triggers && triggers->nullable_fields() ? triggers->nullable_fields() - : field; -} - - bool add_table_for_trigger(THD *thd, const sp_name *trg_name, bool continue_if_not_exist, @@ -321,7 +314,7 @@ bool add_table_for_trigger(THD *thd, void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path); -bool check_trn_exists(const LEX_STRING *trn_path); +bool check_trn_exists(const LEX_CSTRING *trn_path); bool load_table_name_for_trigger(THD *thd, const sp_name *trg_name, diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index d4859efc7af..2ddb4bc042c 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "debug_sync.h" // DEBUG_SYNC #include "table.h" // TABLE, FOREIGN_KEY_INFO #include "sql_class.h" // THD @@ -26,7 +27,8 @@ #include "sql_truncate.h" #include "wsrep_mysqld.h" #include "sql_show.h" //append_identifier() - +#include "sql_select.h" +#include "sql_delete.h" /** Append a list of field names to a string. @@ -46,7 +48,7 @@ static bool fk_info_append_fields(THD *thd, String *str, while ((field= it++)) { - res|= append_identifier(thd, str, field->str, field->length); + res|= append_identifier(thd, str, field); res|= str->append(", "); } @@ -78,22 +80,17 @@ static const char *fk_info_str(THD *thd, FOREIGN_KEY_INFO *fk_info) `db`.`tbl`, CONSTRAINT `id` FOREIGN KEY (`fk`) REFERENCES `db`.`tbl` (`fk`) */ - res|= append_identifier(thd, &str, fk_info->foreign_db->str, - fk_info->foreign_db->length); + res|= append_identifier(thd, &str, fk_info->foreign_db); res|= str.append("."); - res|= append_identifier(thd, &str, fk_info->foreign_table->str, - fk_info->foreign_table->length); + res|= append_identifier(thd, &str, fk_info->foreign_table); res|= str.append(", CONSTRAINT "); - res|= append_identifier(thd, &str, fk_info->foreign_id->str, - fk_info->foreign_id->length); + res|= append_identifier(thd, &str, fk_info->foreign_id); res|= str.append(" FOREIGN KEY ("); res|= fk_info_append_fields(thd, &str, &fk_info->foreign_fields); res|= str.append(") REFERENCES "); - res|= append_identifier(thd, &str, fk_info->referenced_db->str, - fk_info->referenced_db->length); + res|= append_identifier(thd, &str, fk_info->referenced_db); res|= str.append("."); - res|= append_identifier(thd, &str, fk_info->referenced_table->str, - fk_info->referenced_table->length); + res|= append_identifier(thd, &str, fk_info->referenced_table); res|= str.append(" ("); res|= fk_info_append_fields(thd, &str, &fk_info->referenced_fields); res|= str.append(')'); @@ -150,18 +147,18 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table) /* Loop over the set of foreign keys for which this table is a parent. */ while ((fk_info= it++)) { - DBUG_ASSERT(!my_strcasecmp(system_charset_info, - fk_info->referenced_db->str, - table->s->db.str)); - - DBUG_ASSERT(!my_strcasecmp(system_charset_info, - fk_info->referenced_table->str, - table->s->table_name.str)); - - if (my_strcasecmp(system_charset_info, fk_info->foreign_db->str, - table->s->db.str) || - my_strcasecmp(system_charset_info, fk_info->foreign_table->str, - table->s->table_name.str)) + DBUG_ASSERT(!lex_string_cmp(system_charset_info, + fk_info->referenced_db, + &table->s->db)); + + DBUG_ASSERT(!lex_string_cmp(system_charset_info, + fk_info->referenced_table, + &table->s->table_name)); + + if (lex_string_cmp(system_charset_info, fk_info->foreign_db, + &table->s->db) || + lex_string_cmp(system_charset_info, fk_info->foreign_table, + &table->s->table_name)) break; } @@ -301,8 +298,8 @@ 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, - table_ref->table_name, FALSE))) + if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db.str, + table_ref->table_name.str, FALSE))) DBUG_RETURN(TRUE); *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(), @@ -320,11 +317,12 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); - if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, + if (!ha_table_exists(thd, &table_ref->db, &table_ref->table_name, &hton, &is_sequence) || hton == view_pseudo_hton) { - my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name); + my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db.str, + table_ref->table_name.str); DBUG_RETURN(TRUE); } @@ -362,8 +360,8 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, else { /* Table is already locked exclusively. Remove cached instances. */ - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db, - table_ref->table_name, FALSE); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db.str, + table_ref->table_name.str, FALSE); } DBUG_RETURN(FALSE); @@ -416,7 +414,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool hton_can_recreate; if (WSREP(thd) && - wsrep_to_isolation_begin(thd, table_ref->db, table_ref->table_name, 0)) + wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0)) DBUG_RETURN(TRUE); if (lock_table(thd, table_ref, &hton_can_recreate)) DBUG_RETURN(TRUE); @@ -427,7 +425,7 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) The storage engine can truncate the table by creating an empty table with the same structure. */ - error= dd_recreate_table(thd, table_ref->db, table_ref->table_name); + error= dd_recreate_table(thd, table_ref->db.str, table_ref->table_name.str); if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd)) thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); @@ -480,7 +478,6 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) DBUG_RETURN(error); } - /** Execute a TRUNCATE statement at runtime. @@ -492,13 +489,13 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool Sql_cmd_truncate_table::execute(THD *thd) { bool res= TRUE; - TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; + TABLE_LIST *table= thd->lex->select_lex.table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); - if (check_one_table_access(thd, DROP_ACL, first_table)) + if (check_one_table_access(thd, DROP_ACL, table)) DBUG_RETURN(res); - if (! (res= truncate_table(thd, first_table))) + if (! (res= truncate_table(thd, table))) my_ok(thd); DBUG_RETURN(res); diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc new file mode 100644 index 00000000000..0af5ad3cd11 --- /dev/null +++ b/sql/sql_tvc.cc @@ -0,0 +1,859 @@ +/* Copyright (c) 2017, 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 */ + +#include "mariadb.h" +#include "sql_list.h" +#include "sql_tvc.h" +#include "sql_class.h" +#include "opt_range.h" +#include "sql_select.h" +#include "sql_explain.h" +#include "sql_parse.h" +#include "sql_cte.h" + +/** + @brief + Fix fields for TVC values + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + + @details + Call fix_fields procedure for TVC values. + + @retval + true if an error was reported + false otherwise +*/ + +bool fix_fields_for_tvc(THD *thd, List_iterator_fast<List_item> &li) +{ + DBUG_ENTER("fix_fields_for_tvc"); + List_item *lst; + li.rewind(); + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + + while ((item= it++)) + { + if (item->fix_fields(thd, 0)) + DBUG_RETURN(true); + } + } + DBUG_RETURN(false); +} + + +/** + @brief + Defines types of matrix columns elements where matrix rows are defined by + some lists of values. + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + @param holders The structure where types of matrix columns are stored + @param first_list_el_count Count of the list values. It should be the same + for each list of lists elements. It contains + number of elements of the first list from list of + lists. + + @details + For each list list_a from list of lists the procedure gets its elements + types and aggregates them with the previous ones stored in holders. If + list_a is the first one in the list of lists its elements types are put in + holders. The errors can be reported when count of list_a elements is + different from the first_list_el_count. Also error can be reported whe + n aggregation can't be made. + + @retval + true if an error was reported + false otherwise +*/ + +bool join_type_handlers_for_tvc(THD *thd, List_iterator_fast<List_item> &li, + Type_holder *holders, uint first_list_el_count) +{ + DBUG_ENTER("join_type_handlers_for_tvc"); + List_item *lst; + li.rewind(); + bool first= true; + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + + if (first_list_el_count != lst->elements) + { + my_message(ER_WRONG_NUMBER_OF_VALUES_IN_TVC, + ER_THD(thd, ER_WRONG_NUMBER_OF_VALUES_IN_TVC), + MYF(0)); + DBUG_RETURN(true); + } + for (uint pos= 0; (item=it++); pos++) + { + const Type_handler *item_type_handler= item->real_type_handler(); + if (first) + holders[pos].set_handler(item_type_handler); + else if (holders[pos].aggregate_for_result(item_type_handler)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + holders[pos].type_handler()->name().ptr(), + item_type_handler->name().ptr(), + "TABLE VALUE CONSTRUCTOR"); + DBUG_RETURN(true); + } + } + first= false; + } + DBUG_RETURN(false); +} + + +/** + @brief + Define attributes of matrix columns elements where matrix rows are defined + by some lists of values. + + @param + @param thd The context of the statement + @param li The iterator on the list of lists + @param holders The structure where names of matrix columns are stored + @param count_of_lists Count of list of lists elements + @param first_list_el_count Count of the list values. It should be the same + for each list of lists elements. It contains + number of elements of the first list from list + of lists. + + @details + For each list list_a from list of lists the procedure gets its elements + attributes and aggregates them with the previous ones stored in holders. + The errors can be reported when aggregation can't be made. + + @retval + true if an error was reported + false otherwise +*/ + +bool get_type_attributes_for_tvc(THD *thd, + List_iterator_fast<List_item> &li, + Type_holder *holders, uint count_of_lists, + uint first_list_el_count) +{ + DBUG_ENTER("get_type_attributes_for_tvc"); + List_item *lst; + li.rewind(); + + for (uint pos= 0; pos < first_list_el_count; pos++) + { + if (holders[pos].alloc_arguments(thd, count_of_lists)) + DBUG_RETURN(true); + } + + while ((lst= li++)) + { + List_iterator_fast<Item> it(*lst); + Item *item; + for (uint holder_pos= 0 ; (item= it++); holder_pos++) + { + DBUG_ASSERT(item->fixed); + holders[holder_pos].add_argument(item); + } + } + + for (uint pos= 0; pos < first_list_el_count; pos++) + { + if (holders[pos].aggregate_attributes(thd)) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + @brief + Prepare of TVC + + @param + @param thd The context of the statement + @param sl The select where this TVC is defined + @param tmp_result Structure that contains the information + about where to send the result of the query + @param unit_arg The union where sl is defined + + @details + Gets types and attributes of values of this TVC that will be used + for temporary table creation for this TVC. It creates Item_type_holders + for each element of the first list from list of lists (VALUES from tvc), + using its elements name, defined type and attribute. + + @retval + true if an error was reported + false otherwise +*/ + +bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl, + select_result *tmp_result, + st_select_lex_unit *unit_arg) +{ + DBUG_ENTER("table_value_constr::prepare"); + select_lex->in_tvc= true; + List_iterator_fast<List_item> li(lists_of_values); + + List_item *first_elem= li++; + uint cnt= first_elem->elements; + Type_holder *holders; + + if (fix_fields_for_tvc(thd, li)) + DBUG_RETURN(true); + + if (!(holders= new (thd->mem_root) + Type_holder[cnt]) || + join_type_handlers_for_tvc(thd, li, holders, + cnt) || + get_type_attributes_for_tvc(thd, li, holders, + lists_of_values.elements, cnt)) + DBUG_RETURN(true); + + List_iterator_fast<Item> it(*first_elem); + Item *item; + + sl->item_list.empty(); + for (uint pos= 0; (item= it++); pos++) + { + /* Error's in 'new' will be detected after loop */ + Item_type_holder *new_holder= new (thd->mem_root) + Item_type_holder(thd, + item, + holders[pos].type_handler(), + &holders[pos]/*Type_all_attributes*/, + holders[pos].get_maybe_null()); + new_holder->fix_fields(thd, 0); + sl->item_list.push_back(new_holder); + } + + if (thd->is_fatal_error) + DBUG_RETURN(true); // out of memory + + result= tmp_result; + + if (result && result->prepare(sl->item_list, unit_arg)) + DBUG_RETURN(true); + + select_lex->in_tvc= false; + DBUG_RETURN(false); +} + + +/** + Save Query Plan Footprint +*/ + +int table_value_constr::save_explain_data_intern(THD *thd, + Explain_query *output) +{ + const char *message= "No tables used"; + DBUG_ENTER("table_value_constr::save_explain_data_intern"); + DBUG_PRINT("info", ("Select %p, type %s, message %s", + select_lex, select_lex->type, + message)); + DBUG_ASSERT(have_query_plan == QEP_AVAILABLE); + + /* There should be no attempts to save query plans for merged selects */ + DBUG_ASSERT(!select_lex->master_unit()->derived || + select_lex->master_unit()->derived->is_materialized_derived() || + select_lex->master_unit()->derived->is_with_table()); + + explain= new (output->mem_root) Explain_select(output->mem_root, + thd->lex->analyze_stmt); + if (!explain) + DBUG_RETURN(1); + + select_lex->set_explain_type(true); + + explain->select_id= select_lex->select_number; + explain->select_type= select_lex->type; + explain->linkage= select_lex->linkage; + explain->using_temporary= false; + explain->using_filesort= false; + /* Setting explain->message means that all other members are invalid */ + explain->message= message; + + if (select_lex->master_unit()->derived) + explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; + + output->add_node(explain); + + if (select_lex->is_top_level_node()) + output->query_plan_ready(); + + DBUG_RETURN(0); +} + + +/** + Optimization of TVC +*/ + +bool table_value_constr::optimize(THD *thd) +{ + create_explain_query_if_not_exists(thd->lex, thd->mem_root); + have_query_plan= QEP_AVAILABLE; + + if (select_lex->select_number != UINT_MAX && + select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && + have_query_plan != QEP_NOT_PRESENT_YET && + thd->lex->explain && // for "SET" command in SPs. + (!thd->lex->explain->get_select(select_lex->select_number))) + { + return save_explain_data_intern(thd, thd->lex->explain); + } + return 0; +} + + +/** + Execute of TVC +*/ + +bool table_value_constr::exec(SELECT_LEX *sl) +{ + DBUG_ENTER("table_value_constr::exec"); + List_iterator_fast<List_item> li(lists_of_values); + List_item *elem; + + if (select_options & SELECT_DESCRIBE) + DBUG_RETURN(false); + + if (result->send_result_set_metadata(sl->item_list, + Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + { + DBUG_RETURN(true); + } + + while ((elem= li++)) + { + result->send_data(*elem); + } + + if (result->send_eof()) + DBUG_RETURN(true); + + DBUG_RETURN(false); +} + + +/** + @brief + Print list + + @param str The reference on the string representation of the list + @param list The list that needed to be print + @param query_type The mode of printing + + @details + The method saves a string representation of list in the + string str. +*/ + +void print_list_item(String *str, List_item *list, + enum_query_type query_type) +{ + bool is_first_elem= true; + List_iterator_fast<Item> it(*list); + Item *item; + + str->append('('); + + while ((item= it++)) + { + if (is_first_elem) + is_first_elem= false; + else + str->append(','); + + item->print(str, query_type); + } + + str->append(')'); +} + + +/** + @brief + Print this TVC + + @param thd The context of the statement + @param str The reference on the string representation of this TVC + @param query_type The mode of printing + + @details + The method saves a string representation of this TVC in the + string str. +*/ + +void table_value_constr::print(THD *thd, String *str, + enum_query_type query_type) +{ + DBUG_ASSERT(thd); + + str->append(STRING_WITH_LEN("values ")); + + bool is_first_elem= true; + List_iterator_fast<List_item> li(lists_of_values); + List_item *list; + + while ((list= li++)) + { + if (is_first_elem) + is_first_elem= false; + else + str->append(','); + + print_list_item(str, list, query_type); + } +} + + +/** + @brief + Create list of lists for TVC from the list of this IN predicate + + @param thd The context of the statement + @param values TVC list of values + + @details + The method uses the list of values of this IN predicate to build + an equivalent list of values that can be used in TVC. + + E.g.: + + <value_list> = 5,2,7 + <transformed_value_list> = (5),(2),(7) + + <value_list> = (5,2),(7,1) + <transformed_value_list> = (5,2),(7,1) + + @retval + false if the method succeeds + true otherwise +*/ + +bool Item_func_in::create_value_list_for_tvc(THD *thd, + List< List<Item> > *values) +{ + bool is_list_of_rows= args[1]->type() == Item::ROW_ITEM; + + for (uint i=1; i < arg_count; i++) + { + List<Item> *tvc_value; + if (!(tvc_value= new (thd->mem_root) List<Item>())) + return true; + + if (is_list_of_rows) + { + Item_row *row_list= (Item_row *)(args[i]); + + for (uint j=0; j < row_list->cols(); j++) + { + if (tvc_value->push_back(row_list->element_index(j), + thd->mem_root)) + return true; + } + } + else if (tvc_value->push_back(args[i])) + return true; + + if (values->push_back(tvc_value, thd->mem_root)) + return true; + } + return false; +} + + +/** + @brief + Create name for the derived table defined by TVC + + @param thd The context of the statement + @param parent_select The SELECT where derived table is used + @param alias The returned created name + + @details + Create name for the derived table using current TVC number + for this parent_select stored in parent_select + + @retval + true if creation fails + false otherwise +*/ + +static bool create_tvc_name(THD *thd, st_select_lex *parent_select, + LEX_CSTRING *alias) +{ + char buff[6]; + + alias->length= my_snprintf(buff, sizeof(buff), + "tvc_%u", parent_select->curr_tvc_name); + alias->str= thd->strmake(buff, alias->length); + if (!alias->str) + return true; + + return false; +} + + +bool Item_subselect::wrap_tvc_in_derived_table(THD *thd, + st_select_lex *tvc_sl) +{ + LEX *lex= thd->lex; + /* SELECT_LEX object where the transformation is performed */ + SELECT_LEX *parent_select= lex->current_select; + uint8 save_derived_tables= lex->derived_tables; + + Query_arena backup; + Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + + /* + Create SELECT_LEX of the subquery SQ used in the result of transformation + */ + lex->current_select= tvc_sl; + if (mysql_new_select(lex, 0, NULL)) + goto err; + mysql_init_select(lex); + /* Create item list as '*' for the subquery SQ */ + Item *item; + SELECT_LEX *sq_select; // select for IN subquery; + sq_select= lex->current_select; + sq_select->linkage= tvc_sl->linkage; + sq_select->parsing_place= SELECT_LIST; + item= new (thd->mem_root) Item_field(thd, &sq_select->context, + NULL, NULL, &star_clex_str); + if (item == NULL || add_item_to_list(thd, item)) + goto err; + (sq_select->with_wild)++; + + /* Exclude SELECT with TVC */ + tvc_sl->exclude(); + /* + Create derived table DT that will wrap TVC in the result of transformation + */ + SELECT_LEX *tvc_select; // select for tvc + SELECT_LEX_UNIT *derived_unit; // unit for tvc_select + if (mysql_new_select(lex, 1, tvc_sl)) + goto err; + tvc_select= lex->current_select; + derived_unit= tvc_select->master_unit(); + tvc_select->linkage= DERIVED_TABLE_TYPE; + + lex->current_select= sq_select; + + /* + Create the name of the wrapping derived table and + add it to the FROM list of the subquery SQ + */ + Table_ident *ti; + LEX_CSTRING alias; + TABLE_LIST *derived_tab; + if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) || + create_tvc_name(thd, parent_select, &alias)) + goto err; + if (!(derived_tab= + sq_select->add_table_to_list(thd, + ti, &alias, 0, + TL_READ, MDL_SHARED_READ))) + goto err; + sq_select->add_joined_table(derived_tab); + sq_select->add_where_field(derived_unit->first_select()); + sq_select->context.table_list= sq_select->table_list.first; + sq_select->context.first_name_resolution_table= sq_select->table_list.first; + sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; + lex->derived_tables|= DERIVED_SUBQUERY; + + sq_select->where= 0; + sq_select->set_braces(false); + derived_unit->set_with_clause(0); + + if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE) + ((subselect_single_select_engine *) engine)->change_select(sq_select); + + if (arena) + thd->restore_active_arena(arena, &backup); + lex->current_select= sq_select; + return false; + +err: + if (arena) + thd->restore_active_arena(arena, &backup); + lex->derived_tables= save_derived_tables; + lex->current_select= parent_select; + return true; +} + + +/** + @brief + Transform IN predicate into IN subquery + + @param thd The context of the statement + @param arg Not used + + @details + The method transforms this IN predicate into in equivalent IN subquery: + + <left_expr> IN (<value_list>) + => + <left_expr> IN (SELECT * FROM (VALUES <transformed_value_list>) AS tvc_#) + + E.g.: + + <value_list> = 5,2,7 + <transformed_value_list> = (5),(2),(7) + + <value_list> = (5,2),(7,1) + <transformed_value_list> = (5,2),(7,1) + + If the transformation succeeds the method returns the result IN subquery, + otherwise this IN predicate is returned. + + @retval + pointer to the result of transformation if succeeded + pointer to this IN predicate otherwise +*/ + +Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd, + uchar *arg) +{ + if (!transform_into_subq) + return this; + + transform_into_subq= false; + + List<List_item> values; + + LEX *lex= thd->lex; + /* SELECT_LEX object where the transformation is performed */ + SELECT_LEX *parent_select= lex->current_select; + uint8 save_derived_tables= lex->derived_tables; + + for (uint i=1; i < arg_count; i++) + { + if (!args[i]->const_item()) + return this; + } + + Query_arena backup; + Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + + /* + Create SELECT_LEX of the subquery SQ used in the result of transformation + */ + if (mysql_new_select(lex, 1, NULL)) + goto err; + mysql_init_select(lex); + /* Create item list as '*' for the subquery SQ */ + Item *item; + SELECT_LEX *sq_select; // select for IN subquery; + sq_select= lex->current_select; + sq_select->parsing_place= SELECT_LIST; + item= new (thd->mem_root) Item_field(thd, &sq_select->context, + NULL, NULL, &star_clex_str); + if (item == NULL || add_item_to_list(thd, item)) + goto err; + (sq_select->with_wild)++; + /* + Create derived table DT that will wrap TVC in the result of transformation + */ + SELECT_LEX *tvc_select; // select for tvc + SELECT_LEX_UNIT *derived_unit; // unit for tvc_select + if (mysql_new_select(lex, 1, NULL)) + goto err; + mysql_init_select(lex); + tvc_select= lex->current_select; + derived_unit= tvc_select->master_unit(); + tvc_select->linkage= DERIVED_TABLE_TYPE; + + /* Create TVC used in the transformation */ + if (create_value_list_for_tvc(thd, &values)) + goto err; + if (!(tvc_select->tvc= + new (thd->mem_root) + table_value_constr(values, + tvc_select, + tvc_select->options))) + goto err; + + lex->current_select= sq_select; + + /* + Create the name of the wrapping derived table and + add it to the FROM list of the subquery SQ + */ + Table_ident *ti; + LEX_CSTRING alias; + TABLE_LIST *derived_tab; + if (!(ti= new (thd->mem_root) Table_ident(derived_unit)) || + create_tvc_name(thd, parent_select, &alias)) + goto err; + if (!(derived_tab= + sq_select->add_table_to_list(thd, + ti, &alias, 0, + TL_READ, MDL_SHARED_READ))) + goto err; + sq_select->add_joined_table(derived_tab); + sq_select->add_where_field(derived_unit->first_select()); + sq_select->context.table_list= sq_select->table_list.first; + sq_select->context.first_name_resolution_table= sq_select->table_list.first; + sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE; + lex->derived_tables|= DERIVED_SUBQUERY; + + sq_select->where= 0; + sq_select->set_braces(false); + derived_unit->set_with_clause(0); + + /* Create IN subquery predicate */ + sq_select->parsing_place= parent_select->parsing_place; + Item_in_subselect *in_subs; + Item *sq; + if (!(in_subs= + new (thd->mem_root) Item_in_subselect(thd, args[0], sq_select))) + goto err; + sq= in_subs; + if (negated) + sq= negate_expression(thd, in_subs); + else + in_subs->emb_on_expr_nest= emb_on_expr_nest; + + if (arena) + thd->restore_active_arena(arena, &backup); + thd->lex->current_select= parent_select; + + if (sq->fix_fields(thd, (Item **)&sq)) + goto err; + + parent_select->curr_tvc_name++; + return sq; + +err: + if (arena) + thd->restore_active_arena(arena, &backup); + lex->derived_tables= save_derived_tables; + thd->lex->current_select= parent_select; + return NULL; +} + + +/** + @brief + Check if this IN-predicate can be transformed in IN-subquery + with TVC + + @param thd The context of the statement + + @details + Compare the number of elements in the list of + values in this IN-predicate with the + in_subquery_conversion_threshold special variable + + @retval + true if transformation can be made + false otherwise +*/ + +bool Item_func_in::to_be_transformed_into_in_subq(THD *thd) +{ + uint values_count= arg_count-1; + + if (args[1]->type() == Item::ROW_ITEM) + values_count*= ((Item_row *)(args[1]))->cols(); + + if (values_count < thd->variables.in_subquery_conversion_threshold) + return false; + + return true; +} + + +/** + @brief + Transform IN predicates into IN subqueries in WHERE and ON expressions + + @param thd The context of the statement + + @details + For each IN predicate from AND parts of the WHERE condition and/or + ON expressions of the SELECT for this join the method performs + the intransformation into an equivalent IN sunquery if it's needed. + + @retval + false always +*/ + +bool JOIN::transform_in_predicates_into_in_subq(THD *thd) +{ + DBUG_ENTER("JOIN::transform_in_predicates_into_in_subq"); + if (!select_lex->in_funcs.elements) + DBUG_RETURN(false); + + SELECT_LEX *save_current_select= thd->lex->current_select; + enum_parsing_place save_parsing_place= select_lex->parsing_place; + thd->lex->current_select= select_lex; + if (conds) + { + select_lex->parsing_place= IN_WHERE; + conds= + conds->transform(thd, + &Item::in_predicate_to_in_subs_transformer, + (uchar*) 0); + if (!conds) + DBUG_RETURN(true); + select_lex->prep_where= conds ? conds->copy_andor_structure(thd) : 0; + select_lex->where= conds; + } + + if (join_list) + { + TABLE_LIST *table; + List_iterator<TABLE_LIST> li(*join_list); + select_lex->parsing_place= IN_ON; + + while ((table= li++)) + { + if (table->on_expr) + { + table->on_expr= + table->on_expr->transform(thd, + &Item::in_predicate_to_in_subs_transformer, + (uchar*) 0); + if (!table->on_expr) + DBUG_RETURN(true); + table->prep_on_expr= table->on_expr ? + table->on_expr->copy_andor_structure(thd) : 0; + } + } + } + + select_lex->in_funcs.empty(); + select_lex->parsing_place= save_parsing_place; + thd->lex->current_select= save_current_select; + DBUG_RETURN(false); +} + diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h new file mode 100644 index 00000000000..420311cccb2 --- /dev/null +++ b/sql/sql_tvc.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2017, 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_TVC_INCLUDED +#define SQL_TVC_INCLUDED +#include "sql_type.h" + +typedef List<Item> List_item; +class select_result; +class Explain_select; +class Explain_query; +class Item_func_in; +class st_select_lex_unit; +typedef class st_select_lex SELECT_LEX; + +/** + @class table_value_constr + @brief Definition of a Table Value Construction(TVC) + + It contains a list of lists of values which this TVC is defined by and + reference on SELECT where this TVC is defined. +*/ +class table_value_constr : public Sql_alloc +{ +public: + List<List_item> lists_of_values; + select_result *result; + SELECT_LEX *select_lex; + + enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE} have_query_plan; + + Explain_select *explain; + ulonglong select_options; + + table_value_constr(List<List_item> tvc_values, SELECT_LEX *sl, + ulonglong select_options_arg) : + lists_of_values(tvc_values), result(0), select_lex(sl), + have_query_plan(QEP_NOT_PRESENT_YET), explain(0), + select_options(select_options_arg) + { }; + + bool prepare(THD *thd_arg, SELECT_LEX *sl, + select_result *tmp_result, + st_select_lex_unit *unit_arg); + + int save_explain_data_intern(THD *thd_arg, + Explain_query *output); + bool optimize(THD *thd_arg); + bool exec(SELECT_LEX *sl); + + void print(THD *thd_arg, String *str, enum_query_type query_type); +}; +#endif /* SQL_TVC_INCLUDED */ diff --git a/sql/sql_type.cc b/sql/sql_type.cc index ac9d19594fd..9e4c53b8849 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "mariadb.h" #include "sql_type.h" #include "sql_const.h" #include "sql_class.h" @@ -31,6 +32,7 @@ Type_handler_long type_handler_long; Type_handler_int24 type_handler_int24; Type_handler_longlong type_handler_longlong; Type_handler_longlong type_handler_ulonglong; // Only used for CAST() for now +Type_handler_vers_trx_id type_handler_vers_trx_id; Type_handler_float type_handler_float; Type_handler_double type_handler_double; Type_handler_bit type_handler_bit; @@ -54,11 +56,13 @@ Type_handler_set type_handler_set; Type_handler_string type_handler_string; Type_handler_var_string type_handler_var_string; Type_handler_varchar type_handler_varchar; +static Type_handler_varchar_compressed type_handler_varchar_compressed; Type_handler_tiny_blob type_handler_tiny_blob; Type_handler_medium_blob type_handler_medium_blob; Type_handler_long_blob type_handler_long_blob; Type_handler_blob type_handler_blob; +static Type_handler_blob_compressed type_handler_blob_compressed; #ifdef HAVE_SPATIAL Type_handler_geometry type_handler_geometry; @@ -404,45 +408,45 @@ uint Type_handler_time::m_hires_bytes[MAX_DATETIME_PRECISION + 1]= { 3, 4, 4, 5, 5, 5, 6 }; /***************************************************************************/ -const Name Type_handler_row::m_name_row(C_STRING_WITH_LEN("row")); +const Name Type_handler_row::m_name_row(STRING_WITH_LEN("row")); -const Name Type_handler_null::m_name_null(C_STRING_WITH_LEN("null")); +const Name Type_handler_null::m_name_null(STRING_WITH_LEN("null")); const Name - Type_handler_string::m_name_char(C_STRING_WITH_LEN("char")), - Type_handler_var_string::m_name_var_string(C_STRING_WITH_LEN("varchar")), - Type_handler_varchar::m_name_varchar(C_STRING_WITH_LEN("varchar")), - Type_handler_tiny_blob::m_name_tinyblob(C_STRING_WITH_LEN("tinyblob")), - Type_handler_medium_blob::m_name_mediumblob(C_STRING_WITH_LEN("mediumblob")), - Type_handler_long_blob::m_name_longblob(C_STRING_WITH_LEN("longblob")), - Type_handler_blob::m_name_blob(C_STRING_WITH_LEN("blob")); + Type_handler_string::m_name_char(STRING_WITH_LEN("char")), + Type_handler_var_string::m_name_var_string(STRING_WITH_LEN("varchar")), + Type_handler_varchar::m_name_varchar(STRING_WITH_LEN("varchar")), + Type_handler_tiny_blob::m_name_tinyblob(STRING_WITH_LEN("tinyblob")), + Type_handler_medium_blob::m_name_mediumblob(STRING_WITH_LEN("mediumblob")), + Type_handler_long_blob::m_name_longblob(STRING_WITH_LEN("longblob")), + Type_handler_blob::m_name_blob(STRING_WITH_LEN("blob")); const Name - Type_handler_enum::m_name_enum(C_STRING_WITH_LEN("enum")), - Type_handler_set::m_name_set(C_STRING_WITH_LEN("set")); + Type_handler_enum::m_name_enum(STRING_WITH_LEN("enum")), + Type_handler_set::m_name_set(STRING_WITH_LEN("set")); const Name - Type_handler_tiny::m_name_tiny(C_STRING_WITH_LEN("tinyint")), - Type_handler_short::m_name_short(C_STRING_WITH_LEN("smallint")), - Type_handler_long::m_name_int(C_STRING_WITH_LEN("int")), - Type_handler_longlong::m_name_longlong(C_STRING_WITH_LEN("bigint")), - Type_handler_int24::m_name_mediumint(C_STRING_WITH_LEN("mediumint")), - Type_handler_year::m_name_year(C_STRING_WITH_LEN("year")), - Type_handler_bit::m_name_bit(C_STRING_WITH_LEN("bit")); + Type_handler_tiny::m_name_tiny(STRING_WITH_LEN("tinyint")), + Type_handler_short::m_name_short(STRING_WITH_LEN("smallint")), + Type_handler_long::m_name_int(STRING_WITH_LEN("int")), + Type_handler_longlong::m_name_longlong(STRING_WITH_LEN("bigint")), + Type_handler_int24::m_name_mediumint(STRING_WITH_LEN("mediumint")), + Type_handler_year::m_name_year(STRING_WITH_LEN("year")), + Type_handler_bit::m_name_bit(STRING_WITH_LEN("bit")); const Name - Type_handler_float::m_name_float(C_STRING_WITH_LEN("float")), - Type_handler_double::m_name_double(C_STRING_WITH_LEN("double")); + Type_handler_float::m_name_float(STRING_WITH_LEN("float")), + Type_handler_double::m_name_double(STRING_WITH_LEN("double")); const Name - Type_handler_olddecimal::m_name_decimal(C_STRING_WITH_LEN("decimal")), - Type_handler_newdecimal::m_name_decimal(C_STRING_WITH_LEN("decimal")); + Type_handler_olddecimal::m_name_decimal(STRING_WITH_LEN("decimal")), + Type_handler_newdecimal::m_name_decimal(STRING_WITH_LEN("decimal")); const Name - Type_handler_time_common::m_name_time(C_STRING_WITH_LEN("time")), - Type_handler_date_common::m_name_date(C_STRING_WITH_LEN("date")), - Type_handler_datetime_common::m_name_datetime(C_STRING_WITH_LEN("datetime")), - Type_handler_timestamp_common::m_name_timestamp(C_STRING_WITH_LEN("timestamp")); + Type_handler_time_common::m_name_time(STRING_WITH_LEN("time")), + Type_handler_date_common::m_name_date(STRING_WITH_LEN("date")), + Type_handler_datetime_common::m_name_datetime(STRING_WITH_LEN("datetime")), + Type_handler_timestamp_common::m_name_timestamp(STRING_WITH_LEN("timestamp")); /***************************************************************************/ @@ -664,7 +668,9 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h) Item_result a= cmp_type(); Item_result b= h->cmp_type(); - if (a == STRING_RESULT && b == STRING_RESULT) + if (m_vers_trx_id && (a == STRING_RESULT || b == STRING_RESULT)) + m_type_handler= &type_handler_datetime; + else if (a == STRING_RESULT && b == STRING_RESULT) m_type_handler= &type_handler_long_blob; else if (a == INT_RESULT && b == INT_RESULT) m_type_handler= &type_handler_longlong; @@ -931,6 +937,9 @@ Type_handler::get_handler_by_field_type(enum_field_types type) in field_type() context and add DBUG_ASSERT(0) here. */ return &type_handler_newdate; + case MYSQL_TYPE_VARCHAR_COMPRESSED: + case MYSQL_TYPE_BLOB_COMPRESSED: + break; }; DBUG_ASSERT(0); return &type_handler_string; @@ -954,10 +963,12 @@ Type_handler::get_handler_by_real_type(enum_field_types type) case MYSQL_TYPE_DOUBLE: return &type_handler_double; case MYSQL_TYPE_NULL: return &type_handler_null; case MYSQL_TYPE_VARCHAR: return &type_handler_varchar; + case MYSQL_TYPE_VARCHAR_COMPRESSED: return &type_handler_varchar_compressed; case MYSQL_TYPE_TINY_BLOB: return &type_handler_tiny_blob; case MYSQL_TYPE_MEDIUM_BLOB: return &type_handler_medium_blob; case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob; case MYSQL_TYPE_BLOB: return &type_handler_blob; + case MYSQL_TYPE_BLOB_COMPRESSED: return &type_handler_blob_compressed; case MYSQL_TYPE_VAR_STRING: /* VAR_STRING is actually a field_type(), not a real_type(), @@ -1313,6 +1324,21 @@ Field *Type_handler_varchar::make_conversion_table_field(TABLE *table, } +Field *Type_handler_varchar_compressed::make_conversion_table_field(TABLE *table, + uint metadata, + const Field *target) + const +{ + return new(table->in_use->mem_root) + Field_varstring_compressed(NULL, metadata, + HA_VARCHAR_PACKLENGTH(metadata), + (uchar *) "", 1, Field::NONE, + &empty_clex_str, + table->s, target->charset(), + zlib_compression_method); +} + + Field *Type_handler_tiny_blob::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -1335,6 +1361,19 @@ Field *Type_handler_blob::make_conversion_table_field(TABLE *table, } +Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table, + uint metadata, + const Field *target) + const +{ + return new(table->in_use->mem_root) + Field_blob_compressed(NULL, (uchar *) "", 1, Field::NONE, + &empty_clex_str, + table->s, 2, target->charset(), + zlib_compression_method); +} + + Field *Type_handler_medium_blob::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -1358,7 +1397,7 @@ Field *Type_handler_long_blob::make_conversion_table_field(TABLE *table, #ifdef HAVE_SPATIAL -const Name Type_handler_geometry::m_name_geometry(C_STRING_WITH_LEN("geometry")); +const Name Type_handler_geometry::m_name_geometry(STRING_WITH_LEN("geometry")); const Type_handler *Type_handler_geometry::type_handler_for_comparison() const @@ -1715,6 +1754,7 @@ bool Type_handler_string_result:: const { def->redefine_stage1_common(dup, file, schema); + def->set_compression_method(dup->compression_method()); def->create_length_to_internal_length_string(); return false; } @@ -2035,6 +2075,19 @@ Field *Type_handler_longlong::make_table_field(const LEX_CSTRING *name, } +Field *Type_handler_vers_trx_id::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_vers_trx_id(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + 0/*zerofill*/, attr.unsigned_flag); +} + + Field *Type_handler_float::make_table_field(const LEX_CSTRING *name, const Record_addr &addr, const Type_all_attributes &attr, @@ -2708,7 +2761,7 @@ bool Type_handler_int_result:: Type_all_attributes *func, Item **items, uint nitems) const { - uint unsigned_flag= items[0]->unsigned_flag; + bool unsigned_flag= items[0]->unsigned_flag; for (uint i= 1; i < nitems; i++) { if (unsigned_flag != items[i]->unsigned_flag) @@ -3982,7 +4035,7 @@ String *Type_handler_row:: { CHARSET_INFO *cs= thd->variables.character_set_client; StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs); - str->append(C_STRING_WITH_LEN("ROW(")); + str->append(STRING_WITH_LEN("ROW(")); for (uint i= 0 ; i < item->cols(); i++) { if (i > 0) @@ -3994,7 +4047,7 @@ String *Type_handler_row:: else str->append(STRING_WITH_LEN("NULL")); } - str->append(C_STRING_WITH_LEN(")")); + str->append(STRING_WITH_LEN(")")); return str; } @@ -4058,7 +4111,7 @@ String *Type_handler_time_common:: { StringBuffer<MAX_TIME_FULL_WIDTH+1> buf; return print_item_value_temporal(thd, item, str, - Name(C_STRING_WITH_LEN("TIME")), &buf); + Name(STRING_WITH_LEN("TIME")), &buf); } @@ -4067,7 +4120,7 @@ String *Type_handler_date_common:: { StringBuffer<MAX_DATE_WIDTH+1> buf; return print_item_value_temporal(thd, item, str, - Name(C_STRING_WITH_LEN("DATE")), &buf); + Name(STRING_WITH_LEN("DATE")), &buf); } @@ -4076,7 +4129,7 @@ String *Type_handler_datetime_common:: { StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf; return print_item_value_temporal(thd, item, str, - Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf); + Name(STRING_WITH_LEN("TIMESTAMP")), &buf); } @@ -4085,7 +4138,7 @@ String *Type_handler_timestamp_common:: { StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf; return print_item_value_temporal(thd, item, str, - Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf); + Name(STRING_WITH_LEN("TIMESTAMP")), &buf); } @@ -4463,6 +4516,14 @@ bool Type_handler:: } +bool Type_handler:: + Item_longlong_typecast_fix_length_and_dec(Item_longlong_typecast *item) const +{ + item->fix_length_and_dec_generic(); + return false; +} + + #ifdef HAVE_SPATIAL bool Type_handler_geometry:: diff --git a/sql/sql_type.h b/sql/sql_type.h index f4482c6fbdd..3c4ed054bb6 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -55,6 +55,7 @@ class Item_char_typecast; class Item_time_typecast; class Item_date_typecast; class Item_datetime_typecast; +class Item_longlong_typecast; class Item_func_plus; class Item_func_minus; class Item_func_mul; @@ -1106,6 +1107,8 @@ public: Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const; virtual bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) const; + virtual bool + Item_longlong_typecast_fix_length_and_dec(Item_longlong_typecast *item) const; virtual bool Item_func_plus_fix_length_and_dec(Item_func_plus *func) const= 0; @@ -2024,6 +2027,17 @@ public: }; +class Type_handler_vers_trx_id: public Type_handler_longlong +{ +public: + virtual ~Type_handler_vers_trx_id() {} + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; +}; + + class Type_handler_int24: public Type_handler_general_purpose_int { static const Name m_name_mediumint; @@ -2704,6 +2718,14 @@ public: }; +class Type_handler_varchar_compressed: public Type_handler_varchar +{ +public: + Field *make_conversion_table_field(TABLE *, uint metadata, + const Field *target) const; +}; + + class Type_handler_blob_common: public Type_handler_longstr { public: @@ -2805,6 +2827,14 @@ public: }; +class Type_handler_blob_compressed: public Type_handler_blob +{ +public: + Field *make_conversion_table_field(TABLE *, uint metadata, + const Field *target) const; +}; + + #ifdef HAVE_SPATIAL class Type_handler_geometry: public Type_handler_string_result { @@ -2965,14 +2995,16 @@ public: class Type_handler_hybrid_field_type { const Type_handler *m_type_handler; + bool m_vers_trx_id; bool aggregate_for_min_max(const Type_handler *other); + public: Type_handler_hybrid_field_type(); Type_handler_hybrid_field_type(const Type_handler *handler) - :m_type_handler(handler) + :m_type_handler(handler), m_vers_trx_id(false) { } Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other) - :m_type_handler(other->m_type_handler) + :m_type_handler(other->m_type_handler), m_vers_trx_id(other->m_vers_trx_id) { } void swap(Type_handler_hybrid_field_type &other) { @@ -3061,6 +3093,7 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24; extern MYSQL_PLUGIN_IMPORT Type_handler_long type_handler_long; extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_longlong; extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_ulonglong; +extern MYSQL_PLUGIN_IMPORT Type_handler_vers_trx_id type_handler_vers_trx_id; extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal; extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 70da4d1160b..3eb50d45b42 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -31,7 +31,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" // close_mysql_tables @@ -52,7 +52,7 @@ static bool initialized = 0; static MEM_ROOT mem; static HASH udf_hash; static mysql_rwlock_t THR_LOCK_udf; - +static LEX_CSTRING MYSQL_FUNC_NAME= {STRING_WITH_LEN("func") }; static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl, Item_udftype typ); @@ -142,7 +142,6 @@ void udf_init() TABLE *table; int error; DBUG_ENTER("ufd_init"); - char db[]= "mysql"; /* A subject to casednstr, can't be constant */ if (initialized || opt_noacl) DBUG_VOID_RETURN; @@ -167,9 +166,9 @@ void udf_init() initialized = 1; new_thd->thread_stack= (char*) &new_thd; new_thd->store_globals(); - new_thd->set_db(db, sizeof(db)-1); + new_thd->set_db(&MYSQL_SCHEMA_NAME); - tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ); + tables.init_one_table(&new_thd->db, &MYSQL_FUNC_NAME, 0, TL_READ); if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { @@ -189,7 +188,7 @@ void udf_init() } table->use_all_columns(); - while (!(error= read_record_info.read_record(&read_record_info))) + while (!(error= read_record_info.read_record())) { DBUG_PRINT("info",("init udf record")); LEX_CSTRING name; @@ -313,7 +312,7 @@ static void del_udf(udf_func *udf) doesn't use it anymore */ const char *name= udf->name.str; - uint name_length=udf->name.length; + size_t name_length=udf->name.length; udf->name.str= "*"; udf->name.length=1; my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); @@ -348,7 +347,7 @@ void free_udf(udf_func *udf) /* This is only called if using_udf_functions != 0 */ -udf_func *find_udf(const char *name,uint length,bool mark_used) +udf_func *find_udf(const char *name,size_t length,bool mark_used) { udf_func *udf=0; DBUG_ENTER("find_udf"); @@ -433,7 +432,7 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) DBUG_ENTER("mysql_drop_function_internal"); const char *exact_name_str= udf->name.str; - uint exact_name_len= udf->name.length; + size_t exact_name_len= udf->name.length; del_udf(udf); /* @@ -504,8 +503,7 @@ int mysql_create_function(THD *thd,udf_func *udf) if (check_ident_length(&udf->name)) DBUG_RETURN(1); - tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), - "func", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE); table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); mysql_rwlock_wrlock(&THR_LOCK_udf); @@ -623,8 +621,7 @@ int mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name) DBUG_RETURN(1); } - tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), - "func", TL_WRITE); + tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_FUNC_NAME, 0, TL_WRITE); table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); mysql_rwlock_wrlock(&THR_LOCK_udf); diff --git a/sql/sql_udf.h b/sql/sql_udf.h index 1c805227f97..6e6fed2a81a 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -137,7 +137,7 @@ class udf_handler :public Sql_alloc #ifdef HAVE_DLOPEN void udf_init(void),udf_free(void); -udf_func *find_udf(const char *name, uint len, bool mark_used=0); +udf_func *find_udf(const char *name, size_t size, bool mark_used=0); void free_udf(udf_func *udf); int mysql_create_function(THD *thd,udf_func *udf); int mysql_drop_function(THD *thd, const LEX_CSTRING *name); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 909ef887928..a14c5dd4bd5 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -20,7 +20,7 @@ UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_union.h" @@ -363,7 +363,7 @@ bool select_unit::flush() bool select_unit::create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, - const char *alias, + const LEX_CSTRING *alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, uint hidden) @@ -397,7 +397,7 @@ select_union_recursive::create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, - const char *alias, + const LEX_CSTRING *alias, bool bit_fields_as_long, bool create_table, bool keep_row_order, @@ -405,14 +405,14 @@ select_union_recursive::create_result_table(THD *thd_arg, { if (select_unit::create_result_table(thd_arg, column_types, is_union_distinct, options, - "", bit_fields_as_long, + &empty_clex_str, bit_fields_as_long, create_table, keep_row_order, hidden)) return true; if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, false, 1, - options, HA_POS_ERROR, "", + options, HA_POS_ERROR, &empty_clex_str, !create_table, keep_row_order))) return true; @@ -645,6 +645,7 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl, bool is_union_select) { DBUG_ENTER("st_select_lex_unit::prepare_join"); + TABLE_LIST *derived= sl->master_unit()->derived; bool can_skip_order_by; sl->options|= SELECT_NO_UNLOCK; JOIN *join= new JOIN(thd_arg, sl->item_list, @@ -660,7 +661,7 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl, saved_error= join->prepare(sl->table_list.first, sl->with_wild, - sl->where, + (derived && derived->merged ? NULL : sl->where), (can_skip_order_by ? 0 : sl->order_list.elements) + sl->group_list.elements, @@ -694,64 +695,6 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl, } -class Type_holder: public Sql_alloc, - public Item_args, - public Type_handler_hybrid_field_type, - public Type_all_attributes, - public Type_geometry_attributes -{ - TYPELIB *m_typelib; - bool m_maybe_null; -public: - Type_holder() - :m_typelib(NULL), - m_maybe_null(false) - { } - - void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; } - bool get_maybe_null() const { return m_maybe_null; } - - uint decimal_precision() const - { - /* - Type_holder is not used directly to create fields, so - its virtual decimal_precision() is never called. - We should eventually extend create_result_table() to accept - an array of Type_holders directly, without having to allocate - Item_type_holder's and put them into List<Item>. - */ - DBUG_ASSERT(0); - return 0; - } - void set_geometry_type(uint type) - { - Type_geometry_attributes::set_geometry_type(type); - } - uint uint_geometry_type() const - { - return Type_geometry_attributes::get_geometry_type(); - } - void set_typelib(TYPELIB *typelib) - { - m_typelib= typelib; - } - TYPELIB *get_typelib() const - { - return m_typelib; - } - - bool aggregate_attributes(THD *thd) - { - for (uint i= 0; i < arg_count; i++) - m_maybe_null|= args[i]->maybe_null; - return - type_handler()->Item_hybrid_func_fix_attributes(thd, - "UNION", this, this, - args, arg_count); - } -}; - - /** Aggregate data type handlers for the "count" leftmost UNION parts. */ @@ -861,7 +804,7 @@ bool st_select_lex_unit::join_union_item_types(THD *thd_arg, /* Error's in 'new' will be detected after loop */ types.push_back(new (thd_arg->mem_root) Item_type_holder(thd_arg, - &item_tmp->name, + item_tmp, holders[pos].type_handler(), &holders[pos]/*Type_all_attributes*/, holders[pos].get_maybe_null())); @@ -884,6 +827,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, bool is_union_select; bool have_except= FALSE, have_intersect= FALSE; bool instantiate_tmp_table= false; + bool single_tvc= !first_sl->next_select() && first_sl->tvc; DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ASSERT(thd == thd_arg); DBUG_ASSERT(thd == current_thd); @@ -910,16 +854,26 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, /* fast reinit for EXPLAIN */ for (sl= first_sl; sl; sl= sl->next_select()) { - sl->join->result= result; - select_limit_cnt= HA_POS_ERROR; - offset_limit_cnt= 0; - if (!sl->join->procedure && - result->prepare(sl->join->fields_list, this)) + if (sl->tvc) { - DBUG_RETURN(TRUE); + sl->tvc->result= result; + if (result->prepare(sl->item_list, this)) + DBUG_RETURN(TRUE); + sl->tvc->select_options|= SELECT_DESCRIBE; + } + else + { + sl->join->result= result; + select_limit_cnt= HA_POS_ERROR; + offset_limit_cnt= 0; + if (!sl->join->procedure && + result->prepare(sl->join->fields_list, this)) + { + DBUG_RETURN(TRUE); + } + sl->join->select_options|= SELECT_DESCRIBE; + sl->join->reinit(); } - sl->join->select_options|= SELECT_DESCRIBE; - sl->join->reinit(); } } DBUG_RETURN(FALSE); @@ -929,7 +883,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; - is_union_select= is_unit_op() || fake_select_lex; + is_union_select= is_unit_op() || fake_select_lex || single_tvc; for (SELECT_LEX *s= first_sl; s; s= s->next_select()) { @@ -949,8 +903,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (is_union_select || is_recursive) { - if (is_unit_op() && !union_needs_tmp_table() && - !have_except && !have_intersect) + if ((is_unit_op() && !union_needs_tmp_table() && + !have_except && !have_intersect) || single_tvc) { SELECT_LEX *last= first_select(); while (last->next_select()) @@ -985,7 +939,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (!is_union_select && !is_recursive) { - if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, + if (sl->tvc) + { + if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) + goto err; + } + else if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, is_union_select)) goto err; types= first_sl->item_list; @@ -994,8 +953,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, for (;sl; sl= sl->next_select(), union_part_count++) { - if (prepare_join(thd_arg, sl, tmp_result, additional_options, - is_union_select)) + if (sl->tvc) + { + if (sl->tvc->prepare(thd_arg, sl, tmp_result, this)) + goto err; + } + else if (prepare_join(thd_arg, sl, tmp_result, additional_options, + is_union_select)) goto err; /* @@ -1046,7 +1010,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, goto err; if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct), - create_options, derived->alias, + create_options, &derived->alias, false, instantiate_tmp_table, false, 0)) @@ -1168,7 +1132,7 @@ cont: bool error= union_result->create_result_table(thd, &types, MY_TEST(union_distinct), - create_options, "", false, + create_options, &empty_clex_str, false, instantiate_tmp_table, false, hidden); if (intersect_mark) @@ -1183,8 +1147,10 @@ cont: save_maybe_null= result_table_list.maybe_null_exec; } bzero((char*) &result_table_list, sizeof(result_table_list)); - result_table_list.db= (char*) ""; - result_table_list.table_name= result_table_list.alias= (char*) "union"; + result_table_list.db.str= (char*) ""; + result_table_list.db.length= 0; + result_table_list.table_name.str= result_table_list.alias.str= "union"; + result_table_list.table_name.length= result_table_list.alias.length= sizeof("union")-1; result_table_list.table= table= union_result->table; if (fake_select_lex && !fake_select_lex->first_cond_optimization) { @@ -1315,6 +1281,18 @@ bool st_select_lex_unit::optimize() } for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { + if (sl->tvc) + { + sl->tvc->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + if (sl->tvc->optimize(thd)) + { + thd->lex->current_select= lex_select_save; + DBUG_RETURN(TRUE); + } + continue; + } thd->lex->current_select= sl; if (optimized) @@ -1338,7 +1316,7 @@ bool st_select_lex_unit::optimize() we don't calculate found_rows() per union part. Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts. */ - sl->join->select_options= + sl->join->select_options= (select_limit_cnt == HA_POS_ERROR || sl->braces) ? sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; @@ -1425,15 +1403,28 @@ bool st_select_lex_unit::exec() we don't calculate found_rows() per union part. Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts. */ - sl->join->select_options= - (select_limit_cnt == HA_POS_ERROR || sl->braces) ? - sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; - saved_error= sl->join->optimize(); + if (sl->tvc) + { + sl->tvc->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + saved_error= sl->tvc->optimize(thd); + } + else + { + sl->join->select_options= + (select_limit_cnt == HA_POS_ERROR || sl->braces) ? + sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; + saved_error= sl->join->optimize(); + } } if (!saved_error) { records_at_start= table->file->stats.records; - sl->join->exec(); + if (sl->tvc) + sl->tvc->exec(sl); + else + sl->join->exec(); if (sl == union_distinct && !(with_element && with_element->is_recursive)) { // This is UNION DISTINCT, so there should be a fake_select_lex @@ -1442,7 +1433,8 @@ bool st_select_lex_unit::exec() DBUG_RETURN(TRUE); table->no_keyread=1; } - saved_error= sl->join->error; + if (!sl->tvc) + saved_error= sl->join->error; offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : 0); @@ -1672,8 +1664,13 @@ bool st_select_lex_unit::exec_recursive() for (st_select_lex *sl= start ; sl != end; sl= sl->next_select()) { thd->lex->current_select= sl; - sl->join->exec(); - saved_error= sl->join->error; + if (sl->tvc) + sl->tvc->exec(sl); + else + { + sl->join->exec(); + saved_error= sl->join->error; + } if (!saved_error) { examined_rows+= thd->get_examined_row_count(); @@ -1728,7 +1725,7 @@ err: bool st_select_lex_unit::cleanup() { - int error= 0; + bool error= 0; DBUG_ENTER("st_select_lex_unit::cleanup"); if (cleaned) diff --git a/sql/sql_union.h b/sql/sql_union.h index 171f607fba7..d5659e5d947 100644 --- a/sql/sql_union.h +++ b/sql/sql_union.h @@ -16,8 +16,6 @@ #ifndef SQL_UNION_INCLUDED #define SQL_UNION_INCLUDED -#include "my_global.h" /* ulong */ - class THD; class select_result; struct LEX; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 55be8723ab3..b5bae06e1fb 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -20,7 +20,7 @@ Multi-table updates were introduced by Sinisa & Monty */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_update.h" #include "sql_cache.h" // query_cache_* @@ -44,6 +44,9 @@ // mysql_derived_filling +#include "sql_insert.h" // For vers_insert_history_row() that may be + // needed for System Versioning. + /** True if the table's input and output record buffers are comparable using compare_record(TABLE*). @@ -129,29 +132,71 @@ bool compare_record(const TABLE *table) FALSE Items are OK */ -static bool check_fields(THD *thd, List<Item> &items) +static bool check_fields(THD *thd, List<Item> &items, bool update_view) { - List_iterator<Item> it(items); Item *item; - Item_field *field; + if (update_view) + { + List_iterator<Item> it(items); + Item_field *field; + while ((item= it++)) + { + if (!(field= item->field_for_view_update())) + { + /* item has name, because it comes from VIEW SELECT list */ + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); + return TRUE; + } + /* + we make temporary copy of Item_field, to avoid influence of changing + result_field on Item_ref which refer on this field + */ + thd->change_item_tree(it.ref(), + new (thd->mem_root) Item_field(thd, field)); + } + } - while ((item= it++)) + if (thd->variables.sql_mode & MODE_SIMULTANEOUS_ASSIGNMENT) { - if (!(field= item->field_for_view_update())) + // Make sure that a column is updated only once + List_iterator_fast<Item> it(items); + while ((item= it++)) { - /* item has name, because it comes from VIEW SELECT list */ - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); - return TRUE; + item->field_for_view_update()->field->clear_has_explicit_value(); + } + it.rewind(); + while ((item= it++)) + { + Field *f= item->field_for_view_update()->field; + if (f->has_explicit_value()) + { + my_error(ER_UPDATED_COLUMN_ONLY_ONCE, MYF(0), + *(f->table_name), f->field_name.str); + return TRUE; + } + f->set_has_explicit_value(); } - /* - we make temporary copy of Item_field, to avoid influence of changing - result_field on Item_ref which refer on this field - */ - thd->change_item_tree(it.ref(), new (thd->mem_root) Item_field(thd, field)); } return FALSE; } +static bool check_has_vers_fields(TABLE *table, List<Item> &items) +{ + List_iterator<Item> it(items); + if (!table->versioned()) + return false; + + while (Item *item= it++) + { + if (Item_field *item_field= item->field_for_view_update()) + { + Field *field= item_field->field; + if (field->table == table && !field->vers_update_unversioned()) + return true; + } + } + return false; +} /** Re-read record if more columns are needed for error message. @@ -255,11 +300,12 @@ int mysql_update(THD *thd, { bool using_limit= limit != HA_POS_ERROR; bool safe_update= thd->variables.option_bits & OPTION_SAFE_UPDATES; - bool used_key_is_modified= FALSE, transactional_table, will_batch; + bool used_key_is_modified= FALSE, transactional_table; + bool will_batch= FALSE; bool can_compare_record; int res; int error, loc_error; - uint dup_key_found; + ha_rows dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -276,18 +322,23 @@ int mysql_update(THD *thd, ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; + bool has_triggers, binlog_is_row, do_direct_update= FALSE; Update_plan query_plan(thd->mem_root); Explain_update *explain; TABLE_LIST *update_source_table; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; + + // For System Versioning (may need to insert new fields to a table). + ha_rows updated_sys_ver= 0; + DBUG_ENTER("mysql_update"); create_explain_query(thd->lex, thd->mem_root); if (open_tables(thd, &table_list, &table_count, 0)) DBUG_RETURN(1); - //Prepare views so they are handled correctly. + /* Prepare views so they are handled correctly */ if (mysql_handle_derived(thd->lex, DT_INIT)) DBUG_RETURN(1); @@ -315,7 +366,7 @@ int mysql_update(THD *thd, if (!table_list->single_table_updatable()) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); DBUG_RETURN(1); } query_plan.updating_a_view= MY_TEST(table_list->view); @@ -347,16 +398,20 @@ int mysql_update(THD *thd, if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), fields, MARK_COLUMNS_WRITE, 0, 0)) DBUG_RETURN(1); /* purecov: inspected */ - if (table_list->view && check_fields(thd, fields)) + if (check_fields(thd, fields, table_list->view)) { DBUG_RETURN(1); } + bool has_vers_fields= check_has_vers_fields(table, fields); if (check_key_in_view(thd, table_list)) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); DBUG_RETURN(1); } + if (table->default_field) + table->mark_default_fields_for_write(false); + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ table_list->grant.want_privilege= table->grant.want_privilege= @@ -451,7 +506,8 @@ int mysql_update(THD *thd, goto err; } } - init_ftfuncs(thd, select_lex, 1); + if (init_ftfuncs(thd, select_lex, 1)) + goto err; table->mark_columns_needed_for_update(); @@ -514,7 +570,6 @@ int mysql_update(THD *thd, query_plan.using_io_buffer= true; } - /* Ok, we have generated a query plan for the UPDATE. - if we're running EXPLAIN UPDATE, goto produce explain output @@ -522,16 +577,75 @@ int mysql_update(THD *thd, */ if (thd->lex->describe) goto produce_explain_and_leave; - explain= query_plan.save_explain_update_data(query_plan.mem_root, thd); + if (!(explain= query_plan.save_explain_update_data(query_plan.mem_root, thd))) + goto err; ANALYZE_START_TRACKING(&explain->command_tracker); DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", dbug_serve_apcs(thd, 1);); - + + has_triggers= (table->triggers && + (table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_BEFORE) || + table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER))); + DBUG_PRINT("info", ("has_triggers: %s", has_triggers ? "TRUE" : "FALSE")); + binlog_is_row= thd->is_current_stmt_binlog_format_row(); + DBUG_PRINT("info", ("binlog_is_row: %s", binlog_is_row ? "TRUE" : "FALSE")); + if (!(select && select->quick)) status_var_increment(thd->status_var.update_scan_count); + /* + We can use direct update (update that is done silently in the handler) + if none of the following conditions are true: + - There are triggers + - There is binary logging + - using_io_buffer + - This means that the partition changed or the key we want + to use for scanning the table is changed + - ignore is set + - Direct updates don't return the number of ignored rows + - There is a virtual not stored column in the WHERE clause + - Changing a field used by a stored virtual column, which + would require the column to be recalculated. + - ORDER BY or LIMIT + - As this requires the rows to be updated in a specific order + - Note that Spider can handle ORDER BY and LIMIT in a cluster with + one data node. These conditions are therefore checked in + direct_update_rows_init(). + + Direct update does not require a WHERE clause + + Later we also ensure that we are only using one table (no sub queries) + */ + if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && + !has_triggers && !binlog_is_row && + !query_plan.using_io_buffer && !ignore && + !table->check_virtual_columns_marked_for_read() && + !table->check_virtual_columns_marked_for_write()) + { + DBUG_PRINT("info", ("Trying direct update")); + if (select && select->cond && + (select->cond->used_tables() == table->map)) + { + DBUG_ASSERT(!table->file->pushed_cond); + if (!table->file->cond_push(select->cond)) + table->file->pushed_cond= select->cond; + } + + if (!table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && + !table->file->direct_update_rows_init()) + { + do_direct_update= TRUE; + + /* Direct update is not using_filesort and is not using_io_buffer */ + goto update_begin; + } + } + if (query_plan.using_filesort || query_plan.using_io_buffer) { /* @@ -619,7 +733,7 @@ int mysql_update(THD *thd, THD_STAGE_INFO(thd, stage_searching_rows_for_update); ha_rows tmp_limit= limit; - while (!(error=info.read_record(&info)) && !thd->killed) + while (!(error=info.read_record()) && !thd->killed) { explain->buf_tracker.on_record_read(); thd->inc_examined_row_count(1); @@ -691,6 +805,7 @@ int mysql_update(THD *thd, } } +update_begin: if (ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -710,11 +825,20 @@ int mysql_update(THD *thd, transactional_table= table->file->has_transactions(); thd->abort_on_warning= !ignore && thd->is_strict_mode(); - if (table->prepare_triggers_for_update_stmt_or_event()) + + if (do_direct_update) { - will_batch= FALSE; + /* Direct updating is supported */ + DBUG_PRINT("info", ("Using direct update")); + table->reset_default_fields(); + if (!(error= table->file->ha_direct_update_rows(&updated))) + error= -1; + found= updated; + goto update_end; } - else + + if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_UPDATE) && + !table->prepare_triggers_for_update_stmt_or_event()) will_batch= !table->file->start_bulk_update(); /* @@ -735,8 +859,13 @@ int mysql_update(THD *thd, explain->tracker.on_scan_init(); THD_STAGE_INFO(thd, stage_updating); - while (!(error=info.read_record(&info)) && !thd->killed) + while (!(error=info.read_record()) && !thd->killed) { + if (table->versioned() && !table->vers_end_field()->is_max()) + { + continue; + } + explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) @@ -746,6 +875,7 @@ int mysql_update(THD *thd, explain->tracker.on_record_after_where(); store_record(table,record[1]); + if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -799,6 +929,7 @@ int mysql_update(THD *thd, call then it should be included in the count of dup_key_found and error should be set to 0 (only if these errors are ignored). */ + DBUG_PRINT("info", ("Batched update")); error= table->file->ha_bulk_update_row(table->record[1], table->record[0], &dup_key_found); @@ -808,19 +939,35 @@ int mysql_update(THD *thd, else { /* Non-batched update */ - error= table->file->ha_update_row(table->record[1], + error= table->file->ha_update_row(table->record[1], table->record[0]); } - if (!error || error == HA_ERR_RECORD_IS_THE_SAME) - { - if (error != HA_ERR_RECORD_IS_THE_SAME) - updated++; - else - error= 0; - } - else if (!ignore || + if (error == HA_ERR_RECORD_IS_THE_SAME) + { + error= 0; + } + else if (!error) + { + updated++; + + if (has_vers_fields && table->versioned()) + { + if (table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + if ((error = vers_insert_history_row(table))) + { + restore_record(table, record[2]); + break; + } + restore_record(table, record[2]); + } + updated_sys_ver++; + } + } + else if (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)) - { + { /* If (ignore && error is ignorable) we don't have to do anything; otherwise... @@ -947,6 +1094,8 @@ int mysql_update(THD *thd, updated-= dup_key_found; if (will_batch) table->file->end_bulk_update(); + +update_end: table->file->try_semi_consistent_read(0); if (!transactional_table && updated > 0) @@ -991,6 +1140,9 @@ int mysql_update(THD *thd, else errcode= query_error_code(thd, killed_status == NOT_KILLED); + ScopedStatementReplication scoped_stmt_rpl( + table->versioned(VERS_TRX_ID) ? thd : NULL); + if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_table, FALSE, FALSE, errcode)) @@ -1002,6 +1154,11 @@ int mysql_update(THD *thd, DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table); free_underlaid_joins(thd, select_lex); delete file_sort; + if (table->file->pushed_cond) + { + table->file->pushed_cond= 0; + table->file->cond_pop(); + } /* If LAST_INSERT_ID(X) was used, report X */ id= thd->arg_of_last_insert_id_function ? @@ -1010,9 +1167,15 @@ int mysql_update(THD *thd, if (error < 0 && !thd->lex->analyze_stmt) { char buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found, - (ulong) updated, - (ulong) thd->get_stmt_da()->current_statement_warn_count()); + if (!table->versioned(VERS_TIMESTAMP)) + my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found, + (ulong) updated, + (ulong) thd->get_stmt_da()->current_statement_warn_count()); + else + my_snprintf(buff, sizeof(buff), + ER_THD(thd, ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING), + (ulong) found, (ulong) updated, (ulong) updated_sys_ver, + (ulong) thd->get_stmt_da()->current_statement_warn_count()); my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, id, buff); DBUG_PRINT("info",("%ld records updated", (long) updated)); @@ -1027,7 +1190,6 @@ int mysql_update(THD *thd, *found_return= found; *updated_return= updated; - if (thd->lex->analyze_stmt) goto emit_explain_and_leave; @@ -1038,6 +1200,8 @@ err: delete file_sort; free_underlaid_joins(thd, select_lex); table->file->ha_end_keyread(); + if (table->file->pushed_cond) + table->file->cond_pop(); thd->abort_on_warning= 0; DBUG_RETURN(1); @@ -1046,7 +1210,8 @@ produce_explain_and_leave: We come here for various "degenerate" query plans: impossible WHERE, no-partitions-used, impossible-range, etc. */ - query_plan.save_explain_update_data(query_plan.mem_root, thd); + if (!query_plan.save_explain_update_data(query_plan.mem_root, thd)) + goto err; emit_explain_and_leave: int err2= thd->lex->explain->send_explain(thd); @@ -1225,8 +1390,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) { // Partitioned key is updated my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), - tl->top_table()->alias, - tl2->top_table()->alias); + tl->top_table()->alias.str, + tl2->top_table()->alias.str); return true; } @@ -1244,8 +1409,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) { // Clustered primary key is updated my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), - tl->top_table()->alias, - tl2->top_table()->alias); + tl->top_table()->alias.str, + tl2->top_table()->alias.str); return true; } } @@ -1394,6 +1559,7 @@ int mysql_multi_update_prepare(THD *thd) //We need to merge for insert prior to prepare. if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); + if (mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(TRUE); @@ -1420,7 +1586,7 @@ int mysql_multi_update_prepare(THD *thd) } } - if (update_view && check_fields(thd, *fields)) + if (check_fields(thd, *fields, update_view)) { DBUG_RETURN(TRUE); } @@ -1447,12 +1613,12 @@ int mysql_multi_update_prepare(THD *thd) if (!tl->single_table_updatable() || check_key_in_view(thd, tl)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - tl->top_table()->alias, "UPDATE"); + tl->top_table()->alias.str, "UPDATE"); DBUG_RETURN(TRUE); } DBUG_PRINT("info",("setting table `%s` for update", - tl->top_table()->alias)); + tl->top_table()->alias.str)); /* If table will be updated we should not downgrade lock for it and leave it as is. @@ -1460,7 +1626,7 @@ int mysql_multi_update_prepare(THD *thd) } else { - DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias)); + DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias.str)); /* If we are using the binary log, we need TL_READ_NO_INSERT to get correct order of statements. Otherwise, we use a TL_READ lock to @@ -1541,7 +1707,7 @@ int mysql_multi_update_prepare(THD *thd) (SELECT_ACL & ~tlist->grant.privilege); table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); } - DBUG_PRINT("info", ("table: %s want_privilege: %u", tl->alias, + DBUG_PRINT("info", ("table: %s want_privilege: %u", tl->alias.str, (uint) table->grant.want_privilege)); } /* @@ -1619,8 +1785,10 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, tmp_tables(0), updated(0), found(0), fields(field_list), values(value_list), table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), - transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0) -{} + transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0), + updated_sys_ver(0) +{ +} /* @@ -1776,7 +1944,7 @@ int multi_update::prepare(List<Item> ¬_used_values, switch_to_nullable_trigger_fields(*values_for_table[i], table); } } - copy_field= new Copy_field[max_fields]; + copy_field= new (thd->mem_root) Copy_field[max_fields]; DBUG_RETURN(thd->is_fatal_error != 0); } @@ -1871,7 +2039,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, return !is_key_used(table, table->s->primary_key, table->write_set); return TRUE; default: - break; // Avoid compler warning + break; // Avoid compiler warning } return FALSE; @@ -1924,6 +2092,7 @@ multi_update::initialize_tables(JOIN *join) if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables)) { table_to_update= table; // Update table on the fly + has_vers_fields= check_has_vers_fields(table, *fields); continue; } } @@ -2033,7 +2202,7 @@ loop_end: thd->variables.big_tables= FALSE; tmp_tables[cnt]=create_tmp_table(thd, tmp_param, temp_fields, (ORDER*) &group, 0, 0, - TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, ""); + TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str); thd->variables.big_tables= save_big_tables; if (!tmp_tables[cnt]) DBUG_RETURN(1); @@ -2096,6 +2265,11 @@ int multi_update::send_data(List<Item> ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; + if (table->versioned() && !table->vers_end_field()->is_max()) + { + continue; + } + if (table == table_to_update) { /* @@ -2108,6 +2282,7 @@ int multi_update::send_data(List<Item> ¬_used_values) table->status|= STATUS_UPDATED; store_record(table,record[1]); + if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], *values_for_table[offset], 0, @@ -2173,6 +2348,21 @@ int multi_update::send_data(List<Item> ¬_used_values) error= 0; updated--; } + else if (has_vers_fields && table->versioned()) + { + if (table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + if (vers_insert_history_row(table)) + { + restore_record(table, record[2]); + error= 1; + break; + } + restore_record(table, record[2]); + } + updated_sys_ver++; + } /* non-transactional or transactional table got modified */ /* either multi_update class' flag is raised in its branch */ if (table->file->has_transactions()) @@ -2199,6 +2389,7 @@ int multi_update::send_data(List<Item> ¬_used_values) */ uint field_num= 0; List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables); + /* Set first tbl = table and then tbl to tables from tbl_it */ TABLE *tbl= table; do { @@ -2261,10 +2452,6 @@ void multi_update::abort_result_set() if (do_update && table_count > 1) { /* Add warning here */ - /* - todo/fixme: do_update() is never called with the arg 1. - should it change the signature to become argless? - */ (void) do_updates(); } } @@ -2356,6 +2543,8 @@ int multi_update::do_updates() if (table->vfield) empty_record(table); + has_vers_fields= check_has_vers_fields(table, *fields); + check_opt_it.rewind(); while(TABLE *tbl= check_opt_it++) { @@ -2466,19 +2655,40 @@ int multi_update::do_updates() goto err2; } } - if ((local_error=table->file->ha_update_row(table->record[1], - table->record[0])) && + if (has_vers_fields && table->versioned()) + table->vers_update_fields(); + + if ((local_error=table->file->ha_update_row(table->record[1], + table->record[0])) && local_error != HA_ERR_RECORD_IS_THE_SAME) { if (!ignore || table->file->is_fatal_error(local_error, HA_CHECK_ALL)) { err_table= table; - goto err; + goto err; } - } + } if (local_error != HA_ERR_RECORD_IS_THE_SAME) + { updated++; + + if (has_vers_fields && table->versioned()) + { + if (table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + if ((local_error= vers_insert_history_row(table))) + { + restore_record(table, record[2]); + err_table = table; + goto err; + } + restore_record(table, record[2]); + } + updated_sys_ver++; + } + } else local_error= 0; } @@ -2594,9 +2804,21 @@ bool multi_update::send_eof() thd->clear_error(); else errcode= query_error_code(thd, killed_status == NOT_KILLED); - if (thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), - transactional_tables, FALSE, FALSE, errcode)) + + bool force_stmt= false; + for (TABLE *table= all_tables->table; table; table= table->next) + { + if (table->versioned(VERS_TRX_ID)) + { + force_stmt= true; + break; + } + } + ScopedStatementReplication scoped_stmt_rpl(force_stmt ? thd : NULL); + + if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), + thd->query_length(), transactional_tables, FALSE, + FALSE, errcode)) { local_error= 1; // Rollback update } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0a13a630487..b84dc46cae6 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -16,7 +16,7 @@ */ #define MYSQL_LEX 1 -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_view.h" @@ -140,7 +140,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view itc.rewind(); while ((check= itc++) && check != item) { - if (my_strcasecmp(system_charset_info, item->name.str, check->name.str) == 0) + if (lex_string_cmp(system_charset_info, &item->name, &check->name) == 0) { if (!gen_unique_view_name) goto err; @@ -171,7 +171,7 @@ err: void make_valid_column_names(THD *thd, List<Item> &item_list) { Item *item; - uint name_len; + size_t name_len; List_iterator_fast<Item> it(item_list); char buff[NAME_LEN]; DBUG_ENTER("make_valid_column_names"); @@ -273,13 +273,13 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, checked that we have not more privileges on correspondent column of view table (i.e. user will not get some privileges by view creation) */ - if ((check_access(thd, CREATE_VIEW_ACL, view->db, + if ((check_access(thd, CREATE_VIEW_ACL, view->db.str, &view->grant.privilege, &view->grant.m_internal, 0, 0) || check_grant(thd, CREATE_VIEW_ACL, view, FALSE, 1, FALSE)) || (mode != VIEW_CREATE_NEW && - (check_access(thd, DROP_ACL, view->db, + (check_access(thd, DROP_ACL, view->db.str, &view->grant.privilege, &view->grant.m_internal, 0, 0) || @@ -298,7 +298,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, { my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY", thd->security_ctx->priv_user, - thd->security_ctx->priv_host, tbl->table_name); + thd->security_ctx->priv_host, tbl->table_name.str); goto err; } /* @@ -319,8 +319,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, tbl->table_name will be correct name of table because VIEWs are not opened yet. */ - fill_effective_table_privileges(thd, &tbl->grant, tbl->db, - tbl->table_name); + fill_effective_table_privileges(thd, &tbl->grant, tbl->db.str, tbl->table_name.str); } } @@ -331,7 +330,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, { if (!tbl->table_in_first_from_clause) { - if (check_access(thd, SELECT_ACL, tbl->db, + if (check_access(thd, SELECT_ACL, tbl->db.str, &tbl->grant.privilege, &tbl->grant.m_internal, 0, 0) || @@ -457,9 +456,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, view= lex->unlink_first_table(&link_to_local); - if (check_db_dir_existence(view->db)) + if (check_db_dir_existence(view->db.str)) { - my_error(ER_BAD_DB_ERROR, MYF(0), view->db); + my_error(ER_BAD_DB_ERROR, MYF(0), view->db.str); res= TRUE; goto err; } @@ -495,8 +494,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { /* is this table view and the same view which we creates now? */ if (tbl->view && - strcmp(tbl->view_db.str, view->db) == 0 && - strcmp(tbl->view_name.str, view->table_name) == 0) + cmp(&tbl->view_db, &view->db) == 0 && + cmp(&tbl->view_name, &view->table_name) == 0) { my_error(ER_NO_SUCH_TABLE, MYF(0), tbl->view_db.str, tbl->view_name.str); res= TRUE; @@ -516,7 +515,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view && !tbl->schema_table) { - my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias); + my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias.str); res= TRUE; goto err; } @@ -545,9 +544,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, if (lex->view_list.elements) { List_iterator_fast<Item> it(select_lex->item_list); - List_iterator_fast<LEX_STRING> nm(lex->view_list); + List_iterator_fast<LEX_CSTRING> nm(lex->view_list); Item *item; - LEX_STRING *name; + LEX_CSTRING *name; if (lex->view_list.elements != select_lex->item_list.elements) { @@ -577,8 +576,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, Compare/check grants on view with grants of underlying tables */ - fill_effective_table_privileges(thd, &view->grant, view->db, - view->table_name); + fill_effective_table_privileges(thd, &view->grant, view->db.str, + view->table_name.str); /* Make sure that the current user does not have more column-level privileges @@ -597,24 +596,32 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, for (sl= select_lex; sl; sl= sl->next_select()) { - DBUG_ASSERT(view->db); /* Must be set in the parser */ + DBUG_ASSERT(view->db.str); /* Must be set in the parser */ List_iterator_fast<Item> it(sl->item_list); Item *item; while ((item= it++)) { Item_field *fld= item->field_for_view_update(); - uint priv= (get_column_grant(thd, &view->grant, view->db, - view->table_name, item->name.str) & + uint priv= (get_column_grant(thd, &view->grant, view->db.str, + view->table_name.str, item->name.str) & VIEW_ANY_ACL); - if (fld && !fld->field->table->s->tmp_table) + if (!fld) + continue; + TABLE_SHARE *s= fld->field->table->s; + const LString_i field_name= fld->field->field_name; + if (s->tmp_table || + (s->versioned && + (field_name == s->vers_start_field()->field_name || + field_name == s->vers_end_field()->field_name))) { + continue; + } - final_priv&= fld->have_privileges; + final_priv&= fld->have_privileges; - if (~fld->have_privileges & priv) - report_item= item; - } + if (~fld->have_privileges & priv) + report_item= item; } } @@ -623,7 +630,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "create view", thd->security_ctx->priv_user, thd->security_ctx->priv_host, report_item->name.str, - view->table_name); + view->table_name.str); res= TRUE; goto err; } @@ -639,19 +646,18 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, */ if (!res) - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str, view->table_name.str, false); if (!res && mysql_bin_log.is_open()) { StringBuffer<128> buff(thd->variables.character_set_client); DBUG_ASSERT(buff.charset()->mbminlen == 1); - const LEX_STRING command[3]= - {{ C_STRING_WITH_LEN("CREATE ") }, - { C_STRING_WITH_LEN("ALTER ") }, - { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; + const LEX_CSTRING command[3]= + {{ STRING_WITH_LEN("CREATE ") }, + { STRING_WITH_LEN("ALTER ") }, + { STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view->mode].str, - command[thd->lex->create_view->mode].length); + buff.append(&command[thd->lex->create_view->mode]); view_store_options(thd, views, &buff); buff.append(STRING_WITH_LEN("VIEW ")); @@ -660,29 +666,28 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, buff.append(STRING_WITH_LEN("IF NOT EXISTS ")); /* Test if user supplied a db (ie: we did not use thd->db) */ - if (views->db && views->db[0] && - (thd->db == NULL || strcmp(views->db, thd->db))) + if (views->db.str && views->db.str[0] && + (thd->db.str == NULL || cmp(&views->db, &thd->db))) { - append_identifier(thd, &buff, views->db, - views->db_length); + append_identifier(thd, &buff, &views->db); buff.append('.'); } - append_identifier(thd, &buff, views->table_name, - views->table_name_length); + append_identifier(thd, &buff, &views->table_name); if (lex->view_list.elements) { - List_iterator_fast<LEX_STRING> names(lex->view_list); - LEX_STRING *name; + List_iterator_fast<LEX_CSTRING> names(lex->view_list); + LEX_CSTRING *name; int i; for (i= 0; (name= names++); i++) { buff.append(i ? ", " : "("); - append_identifier(thd, &buff, name->str, name->length); + append_identifier(thd, &buff, name); } buff.append(')'); } buff.append(STRING_WITH_LEN(" AS ")); + /* views->source doesn't end with \0 */ buff.append(views->source.str, views->source.length); int errcode= query_error_code(thd, TRUE); @@ -726,11 +731,11 @@ static void make_view_filename(LEX_CSTRING *dir, char *dir_buff, { /* print file name */ dir->length= build_table_filename(dir_buff, dir_buff_len - 1, - view->db, "", "", 0); + view->db.str, "", "", 0); dir->str= dir_buff; path->length= build_table_filename(path_buff, path_buff_len - 1, - view->db, view->table_name, reg_ext, 0); + view->db.str, view->table_name.str, reg_ext, 0); path->str= path_buff; file->str= path->str + dir->length; @@ -843,11 +848,12 @@ int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum, (uchar*)view, view_parameters)) { sql_print_error("View '%-.192s'.'%-.192s': algorithm swap error.", - view->db, view->table_name); + view->db.str, view->table_name.str); DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR); } sql_print_information("View %`s.%`s: the version is set to %llu%s%s", - view->db, view->table_name, view->mariadb_version, + view->db.str, view->table_name.str, + view->mariadb_version, (wrong_checksum ? ", checksum corrected" : ""), (swap_alg ? ((view->algorithm == VIEW_ALGORITHM_MERGE) ? @@ -1027,19 +1033,19 @@ loop_out: fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); - if (ha_table_exists(thd, view->db, view->table_name)) + if (ha_table_exists(thd, &view->db, &view->table_name)) { if (lex->create_info.if_not_exists()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER_THD(thd, ER_TABLE_EXISTS_ERROR), - view->table_name); + view->table_name.str); DBUG_RETURN(0); } else if (mode == VIEW_CREATE_NEW) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias.str); error= -1; goto err; } @@ -1052,7 +1058,8 @@ loop_out: if (!parser->ok() || !is_equal(&view_type, parser->type())) { - my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); + my_error(ER_WRONG_OBJECT, MYF(0), view->db.str, view->table_name.str, + "VIEW"); error= -1; goto err; } @@ -1066,7 +1073,7 @@ loop_out: { if (mode == VIEW_ALTER) { - my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias); + my_error(ER_NO_SUCH_TABLE, MYF(0), view->db.str, view->alias.str); error= -1; goto err; } @@ -1110,8 +1117,8 @@ loop_out: !lex->select_lex.master_unit()->is_unit_op() && !(lex->select_lex.table_list.first)->next_local && find_table_in_global_list(lex->query_tables->next_global, - lex->query_tables->db, - lex->query_tables->table_name)) + &lex->query_tables->db, + &lex->query_tables->table_name)) { view->updatable_view= 0; } @@ -1119,7 +1126,7 @@ loop_out: if (view->with_check != VIEW_CHECK_NONE && !view->updatable_view) { - my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name); + my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db.str, view->table_name.str); error= -1; goto err; } @@ -1163,7 +1170,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, bool result, view_is_mergeable; TABLE_LIST *UNINIT_VAR(view_main_select_tables); DBUG_ENTER("mysql_make_view"); - DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name)); + DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name.str)); if (table->required_type == TABLE_TYPE_NORMAL) { @@ -1207,7 +1214,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, if (table->index_hints && table->index_hints->elements) { my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), - table->index_hints->head()->key_name.str, table->table_name); + table->index_hints->head()->key_name.str, table->table_name.str); DBUG_RETURN(TRUE); } @@ -1216,12 +1223,12 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, precedent; precedent= precedent->referencing_view) { - if (precedent->view_name.length == table->table_name_length && - precedent->view_db.length == table->db_length && + if (precedent->view_name.length == table->table_name.length && + precedent->view_db.length == table->db.length && my_strcasecmp(system_charset_info, - precedent->view_name.str, table->table_name) == 0 && + precedent->view_name.str, table->table_name.str) == 0 && my_strcasecmp(system_charset_info, - precedent->view_db.str, table->db) == 0) + precedent->view_db.str, table->db.str) == 0) { my_error(ER_VIEW_RECURSIVE, MYF(0), top_view->view_db.str, top_view->view_name.str); @@ -1265,7 +1272,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, !table->definer.host.length); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER_THD(thd, ER_VIEW_FRM_NO_USER), - table->db, table->table_name); + table->db.str, table->table_name.str); get_default_definer(thd, &table->definer, false); } @@ -1295,10 +1302,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, Save VIEW parameters, which will be wiped out by derived table processing */ - table->view_db.str= table->db; - table->view_db.length= table->db_length; - table->view_name.str= table->table_name; - table->view_name.length= table->table_name_length; + table->view_db= table->db; + table->view_name= table->table_name; /* We don't invalidate a prepared statement when a view changes, or when someone creates a temporary table. @@ -1341,7 +1346,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, bool dbchanged; Parser_state parser_state; if (parser_state.init(thd, table->select_stmt.str, - table->select_stmt.length)) + (uint)table->select_stmt.length)) goto err; /* @@ -1696,7 +1701,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_ORDERBY_IGNORED, ER_THD(thd, ER_VIEW_ORDERBY_IGNORED), - table->db, table->table_name); + table->db.str, table->table_name.str); } } /* @@ -1799,12 +1804,13 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) { bool not_exist; build_table_filename(path, sizeof(path) - 1, - view->db, view->table_name, reg_ext, 0); + view->db.str, view->table_name.str, reg_ext, 0); if ((not_exist= my_access(path, F_OK)) || !dd_frm_is_view(thd, path)) { char name[FN_REFLEN]; - my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name); + my_snprintf(name, sizeof(name), "%s.%s", view->db.str, + view->table_name.str); if (thd->lex->if_exists()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, @@ -1823,8 +1829,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) { if (!wrong_object_name) { - wrong_object_db= view->db; - wrong_object_name= view->table_name; + wrong_object_db= view->db.str; + wrong_object_name= view->table_name.str; } } continue; @@ -1838,7 +1844,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) For a view, there is a TABLE_SHARE object. Remove it from the table definition cache, in case the view was cached. */ - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str, view->table_name.str, FALSE); query_cache_invalidate3(thd, view, 0); sp_cache_invalidate(); @@ -1923,19 +1929,19 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) this operation should not have influence on Field::query_id, to avoid marking as used fields which are not used */ - enum_mark_columns save_mark_used_columns= thd->mark_used_columns; - thd->mark_used_columns= MARK_COLUMNS_NONE; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + enum_column_usage saved_column_usage= thd->column_usage; + thd->column_usage= COLUMNS_WRITE; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); for (Field_translator *fld= trans; fld < end_of_trans; fld++) { if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item)) { - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; DBUG_RETURN(TRUE); } } - thd->mark_used_columns= save_mark_used_columns; - DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); + thd->column_usage= saved_column_usage; + DBUG_PRINT("info", ("thd->column_usage: %d", thd->column_usage)); } /* Loop over all keys to see if a unique-not-null key is used */ for (;key_info != key_info_end ; key_info++) @@ -2013,7 +2019,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) RETURN FALSE OK - TRUE error (is not sent to cliet) + TRUE error (is not sent to client) */ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view) @@ -2030,10 +2036,18 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view) { Item_field *fld; if ((fld= entry->item->field_for_view_update())) + { + TABLE_SHARE *s= fld->context->table_list->table->s; + LString_i field_name= fld->field_name; + if (s->versioned && + (field_name == s->vers_start_field()->field_name || + field_name == s->vers_end_field()->field_name)) + continue; list->push_back(fld, thd->mem_root); + } else { - my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias.str, "INSERT"); DBUG_RETURN(TRUE); } } @@ -2041,7 +2055,7 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view) } /* - checking view md5 check suum + checking view md5 check sum SINOPSYS view_checksum() @@ -2135,8 +2149,8 @@ int view_repair(THD *thd, TABLE_LIST *view, HA_CHECK_OPT *check_opt) */ bool mysql_rename_view(THD *thd, - const char *new_db, - const char *new_name, + const LEX_CSTRING *new_db, + const LEX_CSTRING *new_name, TABLE_LIST *view) { LEX_CSTRING pathstr; @@ -2147,7 +2161,7 @@ mysql_rename_view(THD *thd, pathstr.str= (char *) path_buff; pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1, - view->db, view->table_name, + view->db.str, view->table_name.str, reg_ext, 0); if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) && @@ -2174,16 +2188,17 @@ mysql_rename_view(THD *thd, goto err; /* rename view and it's backups */ - if (rename_in_schema_file(thd, view->db, view->table_name, new_db, new_name)) + if (rename_in_schema_file(thd, view->db.str, view->table_name.str, + new_db->str, new_name->str)) goto err; dir.str= dir_buff; dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1, - new_db, "", "", 0); + new_db->str, "", "", 0); pathstr.str= path_buff; pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1, - new_db, new_name, reg_ext, 0); + new_db->str, new_name->str, reg_ext, 0); file.str= pathstr.str + dir.length; file.length= pathstr.length - dir.length; @@ -2192,7 +2207,8 @@ mysql_rename_view(THD *thd, (uchar*)&view_def, view_parameters)) { /* restore renamed view in case of error */ - rename_in_schema_file(thd, new_db, new_name, view->db, view->table_name); + rename_in_schema_file(thd, new_db->str, new_name->str, view->db.str, + view->table_name.str); goto err; } } else diff --git a/sql/sql_view.h b/sql/sql_view.h index ff94d2de935..08d79b8e4a5 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -53,7 +53,7 @@ extern TYPELIB updatable_views_with_limit_typelib; bool check_duplicate_names(THD *thd, List<Item>& item_list, bool gen_unique_view_names); -bool mysql_rename_view(THD *thd, const char *new_db, const char *new_name, +bool mysql_rename_view(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name, TABLE_LIST *view); void make_valid_column_names(THD *thd, List<Item> &item_list); diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 3e48fe6d267..db34b77ddcb 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1,3 +1,20 @@ +/* + Copyright (c) 2016, 2017 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 */ + +#include "mariadb.h" #include "sql_parse.h" #include "sql_select.h" #include "sql_list.h" @@ -5,7 +22,6 @@ #include "filesort.h" #include "sql_base.h" #include "sql_window.h" -#include "my_dbug.h" bool @@ -298,6 +314,12 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, } } + List_iterator_fast<Item_window_func> li(win_funcs); + while (Item_window_func * win_func_item= li++) + { + if (win_func_item->check_result_type_of_order_item()) + DBUG_RETURN(1); + } DBUG_RETURN(0); } @@ -716,7 +738,7 @@ public: void init(READ_RECORD *info) { ref_length= info->ref_length; - if (info->read_record == rr_from_pointers) + if (info->read_record_func == rr_from_pointers) { io_cache= NULL; cache_start= info->cache_pos; @@ -945,6 +967,8 @@ private: bool end_of_partition; }; + + ///////////////////////////////////////////////////////////////////////////// /* @@ -1712,7 +1736,17 @@ public: /* Walk to the end of the partition, find how many rows there are. */ while (!cursor.next()) num_rows_in_partition++; + set_win_funcs_row_count(num_rows_in_partition); + } + + ha_rows get_curr_rownum() const + { + return cursor.get_rownum(); + } +protected: + void set_win_funcs_row_count(ha_rows num_rows_in_partition) + { List_iterator_fast<Item_sum> it(sum_functions); Item_sum* item; while ((item= it++)) @@ -1722,11 +1756,43 @@ public: item_with_row_count->set_row_count(num_rows_in_partition); } } +}; + +class Frame_unbounded_following_set_count_no_nulls: + public Frame_unbounded_following_set_count +{ + +public: + Frame_unbounded_following_set_count_no_nulls(THD *thd, + SQL_I_List<ORDER> *partition_list, + SQL_I_List<ORDER> *order_list) : + Frame_unbounded_following_set_count(thd,partition_list, order_list) + { + order_item= order_list->first->item[0]; + } + void next_partition(ha_rows rownum) + { + ha_rows num_rows_in_partition= 0; + if (cursor.fetch()) + return; + + /* Walk to the end of the partition, find how many rows there are. */ + do + { + if (!order_item->is_null()) + num_rows_in_partition++; + } while (!cursor.next()); + + set_win_funcs_row_count(num_rows_in_partition); + } ha_rows get_curr_rownum() const { return cursor.get_rownum(); } + +private: + Item* order_item; }; ///////////////////////////////////////////////////////////////////////////// @@ -2467,6 +2533,21 @@ void add_special_frame_cursors(THD *thd, Cursor_manager *cursor_manager, cursor_manager->add_cursor(fc); break; } + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: + { + fc= new Frame_unbounded_preceding(thd, + spec->partition_list, + spec->order_list); + fc->add_sum_func(item_sum); + cursor_manager->add_cursor(fc); + fc= new Frame_unbounded_following(thd, + spec->partition_list, + spec->order_list); + fc->add_sum_func(item_sum); + cursor_manager->add_cursor(fc); + break; + } default: fc= new Frame_unbounded_preceding( thd, spec->partition_list, spec->order_list); @@ -2491,6 +2572,8 @@ static bool is_computed_with_remove(Item_sum::Sumfunctype sum_func) case Item_sum::NTILE_FUNC: case Item_sum::FIRST_VALUE_FUNC: case Item_sum::LAST_VALUE_FUNC: + case Item_sum::PERCENTILE_CONT_FUNC: + case Item_sum::PERCENTILE_DISC_FUNC: return false; default: return true; @@ -2521,9 +2604,18 @@ void get_window_functions_required_cursors( */ if (item_win_func->requires_partition_size()) { - fc= new Frame_unbounded_following_set_count(thd, + if (item_win_func->only_single_element_order_list()) + { + fc= new Frame_unbounded_following_set_count_no_nulls(thd, + item_win_func->window_spec->partition_list, + item_win_func->window_spec->order_list); + } + else + { + fc= new Frame_unbounded_following_set_count(thd, item_win_func->window_spec->partition_list, item_win_func->window_spec->order_list); + } fc->add_sum_func(sum_func); cursor_manager->add_cursor(fc); } @@ -2676,7 +2768,7 @@ bool compute_window_func(THD *thd, while (true) { - if ((err= info.read_record(&info))) + if ((err= info.read_record())) break; // End of file. /* Remember current row so that we can restore it before computing @@ -2704,6 +2796,13 @@ bool compute_window_func(THD *thd, { cursor_manager->notify_cursors_next_row(); } + + /* Check if we found any error in the window function while adding values + through cursors. */ + if (thd->is_error() || thd->is_killed()) + break; + + /* Return to current row after notifying cursors for each window function. */ tbl->file->ha_rnd_pos(tbl->record[0], rowid_buf); @@ -2987,10 +3086,14 @@ Window_funcs_computation::save_explain_plan(MEM_ROOT *mem_root, Explain_aggr_window_funcs *xpl= new Explain_aggr_window_funcs; List_iterator<Window_funcs_sort> it(win_func_sorts); Window_funcs_sort *srt; + if (!xpl) + return 0; while ((srt = it++)) { Explain_aggr_filesort *eaf= new Explain_aggr_filesort(mem_root, is_analyze, srt->filesort); + if (!eaf) + return 0; xpl->sorts.push_back(eaf, mem_root); } return xpl; @@ -3120,5 +3223,3 @@ Window_funcs_computation::save_explain_plan(MEM_ROOT *mem_root, } else #endif - - diff --git a/sql/sql_window.h b/sql/sql_window.h index ed1d9e36492..392f89e8f03 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -1,9 +1,22 @@ +/* + Copyright (c) 2016, 2017 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_WINDOW_INCLUDED #define SQL_WINDOW_INCLUDED -#include "my_global.h" -#include "item.h" #include "filesort.h" #include "records.h" diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 90db0cb7ec2..f7c69af45a2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -33,7 +33,7 @@ #define Lex (thd->lex) #define Select Lex->current_select -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" /* comp_*_creator */ #include "sql_table.h" /* primary_key_name */ @@ -67,6 +67,8 @@ #include "lex_token.h" #include "sql_lex.h" #include "sql_sequence.h" +#include "sql_tvc.h" +#include "vers_utils.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -78,7 +80,7 @@ int yylex(void *yylval, void *yythd); #define yyoverflow(A,B,C,D,E,F) \ { \ - ulong val= *(F); \ + size_t val= *(F); \ if (my_yyoverflow((B), (D), &val)) \ { \ yyerror(thd, (char*) (A)); \ @@ -217,7 +219,7 @@ int LEX::case_stmt_action_when(Item *when, bool simple) var= new (thd->mem_root) Item_case_expr(thd, spcont->get_current_case_expr_id()); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (var) { var->m_sp= sphead; @@ -399,7 +401,7 @@ LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar, Item_splocal(thd, name, spvar->offset, spvar->type_handler(), pos_in_q, len_in_q); -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS if (item) item->m_sp= sphead; #endif @@ -697,6 +699,18 @@ void LEX::add_key_to_list(LEX_CSTRING *field_name, alter_info.key_list.push_back(key, mem_root); } +bool LEX::add_alter_list(const char *name, Virtual_column_info *expr, + bool exists) +{ + MEM_ROOT *mem_root= thd->mem_root; + Alter_column *ac= new (mem_root) Alter_column(name, expr, exists); + if (ac == NULL) + return true; + alter_info.alter_list.push_back(ac, mem_root); + alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; + return false; +} + void LEX::init_last_field(Column_definition *field, const LEX_CSTRING *field_name, const CHARSET_INFO *cs) @@ -737,6 +751,28 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) MYSQL_YYABORT; \ } while(0) + +void vers_select_conds_t::init(vers_system_time_t t, vers_sys_type_t u_start, + Item *s, vers_sys_type_t u_end, Item *e) +{ + type= t; + unit_start= u_start; + unit_end= u_end; + start= fix_dec(s); + end= fix_dec(e); + used= from_query= false; +} + +Item *vers_select_conds_t::fix_dec(Item *item) +{ + if (item && item->decimals == 0 && item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype() == Item_func::NOW_FUNC) + item->decimals= 6; + + return item; +} + + Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) { Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); @@ -856,25 +892,28 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) enum Window_frame::Frame_exclusion frame_exclusion; enum trigger_order_type trigger_action_order_type; DDL_options_st object_ddl_options; + enum vers_sys_type_t vers_range_unit; + enum Column_definition::enum_column_versioning vers_column_versioning; } %{ -bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); +bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %} %pure-parser /* We have threads */ %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 104 shift/reduce conflicts. + Currently there are 123 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 104 +%expect 123 /* Comments for TOKENS. For each token, please include in the same line a comment that contains the following tags: + SQL-2011-R : Reserved keyword as per SQL-2011 SQL-2011-N : Non Reserved keyword as per SQL-2011 SQL-2003-R : Reserved keyword as per SQL-2003 SQL-2003-N : Non Reserved keyword as per SQL-2003 @@ -1099,6 +1138,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FORCE_SYM %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ +%token FOR_SYSTEM_TIME_SYM /* INTERNAL */ %token FORMAT_SYM %token FOUND_SYM /* SQL-2003-R */ %token FROM @@ -1128,6 +1168,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token HEX_NUM %token HEX_STRING %token HIGH_PRIORITY +%token HISTORY_SYM /* MYSQL */ %token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM @@ -1168,6 +1209,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ISOPEN_SYM /* Oracle-N */ %token ISSUER_SYM %token ITERATE_SYM +%token INVISIBLE_SYM %token JOIN_SYM /* SQL-2003-R */ %token JSON_SYM %token KEYS @@ -1237,6 +1279,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM %token MAXVALUE_SYM /* SQL-2003-N */ +%token MEDIAN_SYM %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -1286,6 +1329,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOMAXVALUE_SYM %token NOMINVALUE_SYM %token NO_WAIT_SYM +%token NOWAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM %token NULL_SYM /* SQL-2003-R */ @@ -1328,6 +1372,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERCENTILE_CONT_SYM +%token PERCENTILE_DISC_SYM +%token PERIOD_SYM /* SQL-2011-R */ %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -1494,6 +1541,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SWAPS_SYM %token SWITCHES_SYM %token SYSDATE +%token SYSTEM /* SQL-2011-R */ +%token SYSTEM_TIME_SYM /* SQL-2011-R */ %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY @@ -1554,6 +1603,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UTC_TIMESTAMP_SYM %token UTC_TIME_SYM %token VALUES /* SQL-2003-R */ +%token VALUES_IN_SYM +%token VALUES_LESS_SYM %token VALUE_SYM /* SQL-2003-R */ %token VARBINARY %token VARCHAR /* SQL-2003-R */ @@ -1562,6 +1613,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM +%token VERSIONING_SYM /* SQL-2011-R */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -1574,8 +1626,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHIN +%token WITHOUT /* SQL-2003-R */ %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ +%token WITH_SYSTEM_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ %token WRAPPER_SYM %token WRITE_SYM /* SQL-2003-N */ @@ -1647,6 +1702,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <const_simple_string> field_length opt_field_length opt_field_length_default_1 + opt_compression_method %type <string> text_string hex_or_bin_String opt_gconcat_separator @@ -1670,7 +1726,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -1680,7 +1736,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); optional_flush_tables_arguments opt_time_precision kill_type kill_option int_num opt_default_time_precision - case_stmt_body opt_bin_mod + case_stmt_body opt_bin_mod opt_for_system_time_clause opt_if_exists_table_element opt_if_not_exists_table_element opt_recursive opt_format_xid @@ -1736,6 +1792,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); window_func_expr window_func simple_window_func + inverse_distribution_function + percentile_function + inverse_distribution_function_def function_call_keyword function_call_nonkeyword function_call_generic @@ -1787,7 +1846,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_primary_ident table_primary_derived select_derived derived_table_list select_derived_union + derived_simple_table derived_query_specification + derived_table_value_constructor %type <date_time_type> date_time_type; %type <interval> interval @@ -1828,11 +1889,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <select_lex> subselect get_select_lex get_select_lex_derived + simple_table query_specification query_term_union_not_ready query_term_union_ready query_expression_body select_paren_derived + table_value_constructor %type <boolfunc2creator> comp_op @@ -1895,7 +1958,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_field_or_var_spec fields_or_vars opt_load_data_set_spec view_list_opt view_list view_select trigger_tail sp_tail sf_tail event_tail - udf_tail create_function_tail + udf_tail create_function_tail create_aggregate_function_tail install uninstall partition_entry binlog_base64_event normal_key_options normal_key_opts all_key_opt spatial_key_options fulltext_key_options normal_key_opt @@ -1903,13 +1966,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); keep_gcc_happy key_using_alg part_column_list + period_for_system_time server_def server_options_list server_option definer_opt no_definer definer get_diagnostics parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute explainable_command + opt_lock_wait_timeout opt_delete_gtid_domain + asrow_attribute END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1930,6 +1996,7 @@ END_OF_INPUT %type <num> sp_decl_idents sp_decl_idents_init_vars %type <num> sp_handler_type sp_hcond_list +%type <num> start_or_end %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value %type <spblock> sp_decls sp_decl sp_decl_body sp_decl_variable_list %type <spname> sp_name @@ -1959,7 +2026,6 @@ END_OF_INPUT %type <spvar_definition> row_field_name row_field_definition %type <spvar_definition_list> row_field_definition_list row_type_body - %type <NONE> opt_window_clause window_def_list window_def window_spec %type <lex_str_ptr> window_name %type <NONE> opt_window_ref opt_window_frame_clause @@ -1968,7 +2034,6 @@ END_OF_INPUT %type <frame_exclusion> opt_window_frame_exclusion; %type <window_frame_bound> window_frame_start window_frame_bound; - %type <NONE> '-' '+' '*' '/' '%' '(' ')' ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM @@ -1981,6 +2046,8 @@ END_OF_INPUT %type <lex_str_list> opt_with_column_list +%type <vers_range_unit> opt_trans_or_timestamp +%type <vers_column_versioning> with_or_without_system %% @@ -2523,8 +2590,8 @@ create: if (lex->create_info.seq_create_info->check_and_adjust(1)) { my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), - lex->select_lex.table_list.first->db, - lex->select_lex.table_list.first->table_name); + lex->select_lex.table_list.first->db.str, + lex->select_lex.table_list.first->table_name.str); MYSQL_YYABORT; } @@ -2558,7 +2625,7 @@ create: if (Lex->add_create_index($2, &$5, $6, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options + '(' key_list ')' opt_lock_wait_timeout normal_key_options opt_index_lock_algorithm { } | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2568,7 +2635,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' fulltext_key_options + '(' key_list ')' opt_lock_wait_timeout fulltext_key_options opt_index_lock_algorithm { } | create_or_replace spatial INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2578,7 +2645,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' spatial_key_options + '(' key_list ')' opt_lock_wait_timeout spatial_key_options opt_index_lock_algorithm { } | create_or_replace DATABASE opt_if_not_exists ident { @@ -2622,8 +2689,16 @@ create: event_tail { } | create_or_replace definer FUNCTION_SYM - { Lex->create_info.set($1); } - sf_tail + { + Lex->create_info.set($1); + } + sf_tail_not_aggregate + { } + | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM + { + Lex->create_info.set($1); + } + sf_tail_aggregate { } | create_or_replace no_definer FUNCTION_SYM { Lex->create_info.set($1); } @@ -2632,9 +2707,8 @@ create: | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM { Lex->create_info.set($1); - Lex->udf.type= UDFTYPE_AGGREGATE; } - udf_tail + create_aggregate_function_tail { } | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list opt_require_clause opt_resource_options @@ -2661,11 +2735,36 @@ create: { } ; +sf_tail_not_aggregate: + sf_tail + { + if (Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR) + { + my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0))); + } + Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE); + } + +sf_tail_aggregate: + sf_tail + { + if (!(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)) + { + my_yyabort_error((ER_INVALID_AGGREGATE_FUNCTION, MYF(0))); + } + Lex->sphead->set_chistics_agg_type(GROUP_AGGREGATE); + } + create_function_tail: - sf_tail { } + sf_tail_not_aggregate { } | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } ; +create_aggregate_function_tail: + sf_tail_aggregate + { } + | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; } + ; opt_sequence: /* empty */ { } | sequence_defs @@ -2982,7 +3081,7 @@ clear_privileges: lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; lex->all_privileges= 0; - lex->select_lex.db= 0; + lex->select_lex.db= null_clex_str; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; bzero((char *)&(lex->mqh),sizeof(lex->mqh)); @@ -3995,7 +4094,19 @@ sp_proc_stmt_fetch_head: ; sp_proc_stmt_fetch: - sp_proc_stmt_fetch_head sp_fetch_list { } + sp_proc_stmt_fetch_head sp_fetch_list { } + | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + lex->sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR; + sp_instr_agg_cfetch *i= + new (thd->mem_root) sp_instr_agg_cfetch(sp->instructions(), + lex->spcont); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } ; sp_proc_stmt_close: @@ -4870,7 +4981,7 @@ size_number: uint text_shift_number= 0; longlong prefix_number; const char *start_ptr= $1.str; - uint str_len= $1.length; + size_t str_len= $1.length; const char *end_ptr= start_ptr + str_len; int error; prefix_number= my_strtoll10(start_ptr, (char**) &end_ptr, &error); @@ -4936,7 +5047,7 @@ create_like: opt_create_select: /* empty */ {} - | opt_duplicate opt_as create_select_query_expression + | opt_duplicate opt_as create_select_query_expression opt_versioning_option ; create_select_query_expression: @@ -5072,10 +5183,21 @@ part_type_def: { Lex->part_info->part_type= RANGE_PARTITION; } | RANGE_SYM part_column_list { Lex->part_info->part_type= RANGE_PARTITION; } - | LIST_SYM part_func - { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM + { + Select->parsing_place= IN_PART_FUNC; + } + part_func + { + Lex->part_info->part_type= LIST_PARTITION; + Select->parsing_place= NO_MATTER; + } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } + | SYSTEM_TIME_SYM + { if (Lex->part_info->vers_init_info(thd)) MYSQL_YYABORT; } + opt_versioning_interval + opt_versioning_limit ; opt_linear: @@ -5283,6 +5405,7 @@ part_definition: MYSQL_YYABORT; } p_elem->part_state= PART_NORMAL; + p_elem->id= part_info->partitions.elements - 1; part_info->curr_part_elem= p_elem; part_info->current_partition= p_elem; part_info->use_default_partitions= FALSE; @@ -5315,11 +5438,15 @@ opt_part_values: { if (part_info->error_if_requires_values()) MYSQL_YYABORT; + if (part_info->part_type == VERSIONING_PARTITION) + my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), + lex->create_last_non_select_table-> + table_name.str)); } else part_info->part_type= HASH_PARTITION; } - | VALUES LESS_SYM THAN_SYM + | VALUES_LESS_SYM THAN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -5333,7 +5460,7 @@ opt_part_values: part_info->part_type= RANGE_PARTITION; } part_func_max {} - | VALUES IN_SYM + | VALUES_IN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -5347,6 +5474,63 @@ opt_part_values: part_info->part_type= LIST_PARTITION; } part_values_in {} + | CURRENT_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *elem= part_info->curr_part_elem; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME")); + } + else + { + DBUG_ASSERT(Lex->create_last_non_select_table); + DBUG_ASSERT(Lex->create_last_non_select_table->table_name.str); + // FIXME: other ALTER commands? + my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), + Lex->create_last_non_select_table-> + table_name.str)); + } + elem->type(partition_element::CURRENT); + DBUG_ASSERT(part_info->vers_info); + part_info->vers_info->now_part= elem; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + } + | HISTORY_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *elem= part_info->curr_part_elem; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME")); + } + else + { + part_info->vers_init_info(thd); + elem->id= UINT_MAX32; + } + DBUG_ASSERT(part_info->vers_info); + if (part_info->vers_info->now_part) + { + DBUG_ASSERT(Lex->create_last_non_select_table); + DBUG_ASSERT(Lex->create_last_non_select_table->table_name.str); + my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), + Lex->create_last_non_select_table-> + table_name.str)); + } + elem->type(partition_element::HISTORY); + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + } | DEFAULT { LEX *lex= Lex; @@ -5592,6 +5776,7 @@ sub_part_definition: mem_alloc_error(sizeof(partition_element)); MYSQL_YYABORT; } + sub_p_elem->id= curr_part->subpartitions.elements - 1; part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; part_info->use_default_num_subpartitions= FALSE; @@ -5648,6 +5833,40 @@ opt_part_option: { Lex->part_info->curr_part_elem->part_comment= $3.str; } ; +opt_versioning_interval: + /* empty */ {} + | INTERVAL_SYM expr interval + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + INTERVAL interval; + if (get_interval_value($2, $3, &interval) || + part_info->vers_set_interval(interval)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + Lex->create_last_non_select_table->table_name.str, + "INTERVAL"); + MYSQL_YYABORT; + } + } + ; + +opt_versioning_limit: + /* empty */ {} + | LIMIT ulonglong_num + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + if (part_info->vers_set_limit($2)) + { + my_error(ER_PART_WRONG_VALUE, MYF(0), + Lex->create_last_non_select_table->table_name.str, + "LIMIT"); + MYSQL_YYABORT; + } + } + ; + /* End of partition parser part */ @@ -6014,6 +6233,31 @@ create_table_option: { Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= ($3 == HA_CHOICE_YES); + } + | versioning_option + ; + +opt_versioning_option: + /* empty */ + | versioning_option + ; + +versioning_option: + WITH_SYSTEM_SYM VERSIONING_SYM + { + if (Lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + { + if (DBUG_EVALUATE_IF("sysvers_force", 0, 1)) + { + my_error(ER_VERS_TEMPORARY, MYF(0)); + MYSQL_YYABORT; + } + } + else + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_SYSTEM_VERSIONING; + Lex->create_info.options|= HA_VERSIONED_TABLE; + } } ; @@ -6114,6 +6358,7 @@ field_list_item: column_def { } | key_def | constraint_def + | period_for_system_time ; column_def: @@ -6213,6 +6458,15 @@ constraint_def: } ; +period_for_system_time: + // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM . + PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')' + { + Vers_parse_info &info= Lex->vers_get_info(); + info.set_system_time($4, $6); + } + ; + opt_check_constraint: /* empty */ { $$= (Virtual_column_info*) 0; } | check_constraint { $$= $1;} @@ -6298,6 +6552,15 @@ opt_serial_attribute_list: | serial_attribute ; +opt_asrow_attribute: + /* empty */ {} + | opt_asrow_attribute_list {} + ; + +opt_asrow_attribute_list: + opt_asrow_attribute_list asrow_attribute {} + | asrow_attribute + ; field_def: opt_attribute @@ -6307,6 +6570,46 @@ field_def: Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps } vcol_opt_specifier vcol_opt_attribute + | opt_generated_always AS ROW_SYM start_or_end opt_asrow_attribute + { + LEX *lex= Lex; + Vers_parse_info &info= lex->vers_get_info(); + const LEX_CSTRING &field_name= lex->last_field->field_name; + + LString_i *p; + switch ($4) + { + case 1: + p= &info.as_row.start; + if (*p) + { + my_yyabort_error((ER_VERS_DUPLICATE_ROW_START_END, MYF(0), + "START", field_name.str)); + } + lex->last_field->flags|= VERS_SYS_START_FLAG | NOT_NULL_FLAG; + break; + case 0: + p= &info.as_row.end; + if (*p) + { + my_yyabort_error((ER_VERS_DUPLICATE_ROW_START_END, MYF(0), + "END", field_name.str)); + } + lex->last_field->flags|= VERS_SYS_END_FLAG | NOT_NULL_FLAG; + break; + default: + /* Not Reachable */ + MYSQL_YYABORT; + break; + } + DBUG_ASSERT(p); + *p= field_name; + } + ; + +start_or_end: + START_SYM { $$ = 1; } + | END { $$ = 0; } ; opt_generated_always: @@ -6357,6 +6660,10 @@ vcol_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + | INVISIBLE_SYM + { + Lex->last_field->invisible= INVISIBLE_USER; + } ; parse_vcol_expr: @@ -6741,7 +7048,7 @@ attribute: } | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } | SERIAL_SYM DEFAULT VALUE_SYM - { + { LEX *lex=Lex; lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_KEY_FLAG; lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; @@ -6753,11 +7060,24 @@ attribute: $2->name,Lex->charset->csname)); Lex->last_field->charset= $2; } + | COMPRESSED_SYM opt_compression_method + { + if (Lex->last_field->set_compressed($2)) + MYSQL_YYABORT; + } | serial_attribute ; -serial_attribute: - not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } +opt_compression_method: + /* empty */ { $$= NULL; } + | equal ident { $$= $2.str; } + ; + +asrow_attribute: + not NULL_SYM + { + Lex->last_field->flags|= NOT_NULL_FLAG; + } | opt_primary KEY_SYM { LEX *lex=Lex; @@ -6765,6 +7085,10 @@ serial_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | vcol_attribute + ; + +serial_attribute: + asrow_attribute | IDENT_sys equal TEXT_STRING_sys { if ($3.length > ENGINE_OPTION_MAX_LENGTH) @@ -6792,6 +7116,26 @@ serial_attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } + | with_or_without_system VERSIONING_SYM + { + Lex->last_field->versioning= $1; + Lex->create_info.options|= HA_VERSIONED_TABLE; + } + ; + +with_or_without_system: + WITH_SYSTEM_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_COLUMN_UNVERSIONED; + Lex->create_info.vers_info.versioned_fields= true; + $$= Column_definition::WITH_VERSIONING; + } + | WITHOUT SYSTEM + { + Lex->alter_info.flags|= Alter_info::ALTER_COLUMN_UNVERSIONED; + Lex->create_info.vers_info.unversioned_fields= true; + $$= Column_definition::WITHOUT_VERSIONING; + } ; @@ -7270,7 +7614,7 @@ alter: Lex->create_info.storage_media= HA_SM_DEFAULT; DBUG_ASSERT(!Lex->m_sql_cmd); } - alter_options TABLE_SYM table_ident + alter_options TABLE_SYM table_ident opt_lock_wait_timeout { if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, @@ -7300,8 +7644,7 @@ alter: LEX *lex=Lex; lex->sql_command=SQLCOM_ALTER_DB; lex->name= $3; - if (lex->name.str == NULL && - lex->copy_db_to(&lex->name.str, &lex->name.length)) + if (lex->name.str == NULL && lex->copy_db_to(&lex->name)) MYSQL_YYABORT; } | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM @@ -7604,10 +7947,9 @@ alter_commands: WITH TABLE_SYM table_ident have_partitioning { LEX *lex= thd->lex; - size_t dummy; - lex->select_lex.db=$6->db.str; - if (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, &dummy)) + lex->select_lex.db= $6->db; + if (lex->select_lex.db.str == NULL && + lex->copy_db_to(&lex->select_lex.db)) { MYSQL_YYABORT; } @@ -7750,6 +8092,10 @@ alter_list_item: Lex->create_last_non_select_table= Lex->last_table(); Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } + | ADD period_for_system_time + { + Lex->vers_get_info().add_period= true; + } | add_column '(' create_field_list ')' { Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | @@ -7845,32 +8191,22 @@ alter_list_item: lex->alter_info.keys_onoff= Alter_info::ENABLE; lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; } - | ALTER opt_column field_ident SET DEFAULT column_default_expr + | ALTER opt_column opt_if_exists_table_element field_ident SET DEFAULT column_default_expr { - LEX *lex=Lex; - Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6); - if (ac == NULL) + if (Lex->add_alter_list($4.str, $7, $3)) MYSQL_YYABORT; - lex->alter_info.alter_list.push_back(ac, thd->mem_root); - lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } - | ALTER opt_column field_ident DROP DEFAULT + | ALTER opt_column opt_if_exists_table_element field_ident DROP DEFAULT { - LEX *lex=Lex; - Alter_column *ac= (new (thd->mem_root) - Alter_column($3.str, (Virtual_column_info*) 0)); - if (ac == NULL) + if (Lex->add_alter_list($4.str, (Virtual_column_info*) 0, $3)) MYSQL_YYABORT; - lex->alter_info.alter_list.push_back(ac, thd->mem_root); - lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } | RENAME opt_to table_ident { LEX *lex=Lex; - size_t dummy; - lex->select_lex.db=$3->db.str; - if (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, &dummy)) + lex->select_lex.db= $3->db; + if (lex->select_lex.db.str == NULL && + lex->copy_db_to(&lex->select_lex.db)) { MYSQL_YYABORT; } @@ -7915,6 +8251,15 @@ alter_list_item: } | alter_algorithm_option | alter_lock_option + | ADD SYSTEM VERSIONING_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_SYSTEM_VERSIONING; + Lex->create_info.options|= HA_VERSIONED_TABLE; + } + | DROP SYSTEM VERSIONING_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_DROP_SYSTEM_VERSIONING; + } ; opt_index_lock_algorithm: @@ -8386,7 +8731,7 @@ optimize: /* Will be overridden during execution. */ YYPS->m_lock_type= TL_UNLOCK; } - table_list + table_list opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -8436,13 +8781,13 @@ table_to_table_list: ; table_to_table: - table_ident TO_SYM table_ident + table_ident opt_lock_wait_timeout TO_SYM table_ident { LEX *lex=Lex; SELECT_LEX *sl= lex->current_select; if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE) || - !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + !sl->add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } @@ -8581,6 +8926,9 @@ select: select_init: SELECT_SYM select_options_and_item_list select_init3 + | table_value_constructor + | table_value_constructor union_list + | table_value_constructor union_order_or_limit | '(' select_paren ')' | '(' select_paren ')' union_list | '(' select_paren ')' union_order_or_limit @@ -8588,6 +8936,9 @@ select_init: union_list_part2: SELECT_SYM select_options_and_item_list select_init3_union_query_term + | table_value_constructor + | table_value_constructor union_list + | table_value_constructor union_order_or_limit | '(' select_paren_union_query_term ')' | '(' select_paren_union_query_term ')' union_list | '(' select_paren_union_query_term ')' union_order_or_limit @@ -8595,6 +8946,14 @@ union_list_part2: select_paren: { + Lex->current_select->set_braces(true); + } + table_value_constructor + { + DBUG_ASSERT(Lex->current_select->braces); + } + | + { /* In order to correctly parse UNION's global ORDER BY we need to set braces before parsing the clause. @@ -8646,6 +9005,15 @@ select_paren_derived: { Lex->current_select->set_braces(true); } + table_value_constructor + { + DBUG_ASSERT(Lex->current_select->braces); + $$= Lex->current_select->master_unit()->first_select(); + } + | + { + Lex->current_select->set_braces(true); + } SELECT_SYM select_part2_derived opt_table_expression opt_order_clause @@ -8804,6 +9172,53 @@ select_options: } ; +opt_trans_or_timestamp: + /* empty */ + { + $$ = VERS_UNDEFINED; + } + | TRANSACTION_SYM + { + $$ = VERS_TRX_ID; + } + | TIMESTAMP + { + $$ = VERS_TIMESTAMP; + } + ; + +opt_for_system_time_clause: + /* empty */ + { + $$= false; + } + | FOR_SYSTEM_TIME_SYM system_time_expr + { + $$= true; + } + ; + +system_time_expr: + AS OF_SYM opt_trans_or_timestamp simple_expr + { + Lex->vers_conditions.init(SYSTEM_TIME_AS_OF, $3, $4); + } + | ALL + { + Lex->vers_conditions.init(SYSTEM_TIME_ALL); + } + | FROM opt_trans_or_timestamp simple_expr + TO_SYM opt_trans_or_timestamp simple_expr + { + Lex->vers_conditions.init(SYSTEM_TIME_FROM_TO, $2, $3, $5, $6); + } + | BETWEEN_SYM opt_trans_or_timestamp simple_expr + AND_SYM opt_trans_or_timestamp simple_expr + { + Lex->vers_conditions.init(SYSTEM_TIME_BETWEEN, $2, $3, $5, $6); + } + ; + select_option_list: select_option_list select_option | select_option @@ -8849,14 +9264,14 @@ select_option: opt_select_lock_type: /* empty */ - | FOR_SYM UPDATE_SYM + | FOR_SYM UPDATE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_WRITE; lex->current_select->set_lock_for_tables(TL_WRITE); lex->safe_to_cache_query=0; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; @@ -9478,6 +9893,7 @@ column_default_non_parenthesized_expr: | variable | sum_expr | window_func_expr + | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3, thd->mem_root); @@ -9561,8 +9977,9 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } - | VALUES '(' simple_ident_nospvar ')' + | VALUE_SYM '(' simple_ident_nospvar ')' { $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), $3); @@ -10336,6 +10753,11 @@ geometry_function: Geometry::wkb_polygon, Geometry::wkb_linestring)); } + | WITHIN '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, Item_func_spatial_precise_rel(thd, $3, $5, + Item_func::SP_WITHIN_FUNC)); + } ; /* @@ -10645,16 +11067,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -10797,6 +11225,71 @@ simple_window_func: } ; + + +inverse_distribution_function: + percentile_function OVER_SYM + '(' opt_window_partition_clause ')' + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +percentile_function: + inverse_distribution_function_def WITHIN GROUP_SYM '(' + { Select->prepare_add_window_spec(thd); } + order_by_single_element_list ')' + { + $$= $1; + } + | MEDIAN_SYM '(' expr ')' + { + Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT; + + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, args); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +inverse_distribution_function_def: + PERCENTILE_CONT_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PERCENTILE_DISC_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_disc(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +order_by_single_element_list: + ORDER_SYM BY order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + ; + + window_name: ident { @@ -10845,7 +11338,7 @@ variable_aux: thd->parse_error(); MYSQL_YYABORT; } - if (!($$= get_system_var(thd, $2, $3, $4))) + if (!($$= get_system_var(thd, $2, &$3, &$4))) MYSQL_YYABORT; if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); @@ -10879,6 +11372,48 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + + + in_sum_expr: opt_all { @@ -11204,12 +11739,13 @@ table_factor: table_primary_ident: { + DBUG_ASSERT(Select); SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_use_partition opt_table_alias opt_key_definition + table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition { - if (!($$= Select->add_table_to_list(thd, $2, $4, + if (!($$= Select->add_table_to_list(thd, $2, $5, Select->get_table_join_options(), YYPS->m_lock_type, YYPS->m_mdl_type, @@ -11217,6 +11753,8 @@ table_primary_ident: $3))) MYSQL_YYABORT; Select->add_joined_table($$); + if ($4) + $$->vers_conditions= Lex->vers_conditions; } ; @@ -11239,11 +11777,11 @@ table_primary_ident: */ table_primary_derived: - '(' get_select_lex select_derived_union ')' opt_table_alias + '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias { /* Use $2 instead of Lex->current_select as derived table will alter value of Lex->current_select. */ - if (!($3 || $5) && $2->embedding && + if (!($3 || $6) && $2->embedding && !$2->embedding->nested_join->join_list.elements) { /* we have a derived table ($3 == NULL) but no alias, @@ -11266,7 +11804,7 @@ table_primary_derived: if (ti == NULL) MYSQL_YYABORT; if (!($$= sel->add_table_to_list(thd, - ti, $5, 0, + ti, $6, 0, TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; @@ -11274,7 +11812,7 @@ table_primary_derived: lex->pop_context(); lex->nest_level--; } - else if ($5 != NULL) + else if ($6 != NULL) { /* Tables with or without joins within parentheses cannot @@ -11298,11 +11836,16 @@ table_primary_derived: if ($$ && $$->derived && !$$->derived->first_select()->next_select()) $$->select_lex->add_where_field($$->derived->first_select()); + if ($5) + { + MYSQL_YYABORT_UNLESS(!$3); + $$->vers_conditions= Lex->vers_conditions; + } } /* Represents derived table with WITH clause */ | '(' get_select_lex subselect_start with_clause query_expression_body - subselect_end ')' opt_table_alias + subselect_end ')' opt_for_system_time_clause opt_table_alias { LEX *lex=Lex; SELECT_LEX *sel= $2; @@ -11313,10 +11856,12 @@ table_primary_derived: $5->set_with_clause($4); lex->current_select= sel; if (!($$= sel->add_table_to_list(lex->thd, - ti, $8, 0, + ti, $9, 0, TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; sel->add_joined_table($$); + if ($8) + $$->vers_conditions= Lex->vers_conditions; } ; @@ -11358,9 +11903,9 @@ select_derived_union: } } union_list_derived_part2 - | derived_query_specification opt_select_lock_type - | derived_query_specification order_or_limit opt_select_lock_type - | derived_query_specification opt_select_lock_type union_list_derived + | derived_simple_table opt_select_lock_type + | derived_simple_table order_or_limit opt_select_lock_type + | derived_simple_table opt_select_lock_type union_list_derived ; union_list_derived_part2: @@ -11415,6 +11960,10 @@ select_derived: } ; +derived_simple_table: + derived_query_specification { $$= $1; } + | derived_table_value_constructor { $$= $1; } + ; /* Similar to query_specification, but for derived tables. Example: the inner parenthesized SELECT in this query: @@ -11429,6 +11978,41 @@ derived_query_specification: } ; +derived_table_value_constructor: + VALUES + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + } + values_list + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1, NULL)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + + if (!(lex->current_select->tvc= + new (lex->thd->mem_root) table_value_constr(lex->many_values, + lex->current_select, + lex->current_select->options))) + MYSQL_YYABORT; + lex->many_values.empty(); + $$= NULL; + } + ; + + select_derived2: { LEX *lex= Lex; @@ -12339,9 +12923,9 @@ drop: YYPS->m_lock_type= TL_UNLOCK; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_list opt_restrict + table_list opt_lock_wait_timeout opt_restrict {} - | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout { LEX *lex=Lex; Alter_drop *ad= (new (thd->mem_root) @@ -12385,7 +12969,7 @@ drop: sp_name *spname; if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); - if (thd->db && lex->copy_db_to(&db.str, &db.length)) + if (thd->db.str && lex->copy_db_to(&db)) MYSQL_YYABORT; lex->set_command(SQLCOM_DROP_FUNCTION, $3); spname= new (thd->mem_root) sp_name(&db, &$4, false); @@ -12734,7 +13318,14 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } - KEY_SYM UPDATE_SYM insert_update_list + KEY_SYM UPDATE_SYM + { + Select->parsing_place= IN_UPDATE_ON_DUP_KEY; + } + insert_update_list + { + Select->parsing_place= NO_MATTER; + } ; /* Update rows in a table */ @@ -12757,7 +13348,7 @@ update: { /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - lex->select_lex.get_table_list()->alias, "UPDATE"); + lex->select_lex.get_table_list()->alias.str, "UPDATE"); MYSQL_YYABORT; } /* @@ -12817,10 +13408,29 @@ delete: lex->ignore= 0; lex->select_lex.init_order(); } - opt_delete_options single_multi + delete_part2 + ; + +opt_delete_system_time: + /* empty */ + { + Lex->vers_conditions.init(SYSTEM_TIME_ALL); + } + | BEFORE_SYM SYSTEM_TIME_SYM opt_trans_or_timestamp simple_expr + { + Lex->vers_conditions.init(SYSTEM_TIME_BEFORE, $3, $4); + } + ; + +delete_part2: + opt_delete_options single_multi {} + | HISTORY_SYM delete_single_table opt_delete_system_time + { + Lex->last_table()->vers_conditions= Lex->vers_conditions; + } ; -single_multi: +delete_single_table: FROM table_ident opt_use_partition { if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING, @@ -12832,8 +13442,13 @@ single_multi: YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; } - opt_where_clause opt_order_clause - delete_limit_clause {} + ; + +single_multi: + delete_single_table + opt_where_clause + opt_order_clause + delete_limit_clause opt_select_expressions {} | table_wild_list { @@ -12915,7 +13530,7 @@ opt_delete_option: ; truncate: - TRUNCATE_SYM opt_table_sym + TRUNCATE_SYM { LEX* lex= Lex; lex->sql_command= SQLCOM_TRUNCATE; @@ -12926,7 +13541,7 @@ truncate: YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_name + opt_table_sym table_name opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -13029,7 +13644,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) MYSQL_YYABORT; } @@ -13037,7 +13652,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TRIGGERS; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) MYSQL_YYABORT; } @@ -13045,7 +13660,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $2.str; + lex->select_lex.db= $2; if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) MYSQL_YYABORT; } @@ -13053,7 +13668,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) MYSQL_YYABORT; } @@ -13061,7 +13676,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; } @@ -13149,9 +13764,15 @@ show_param: lex->sql_command= SQLCOM_SHOW_PRIVILEGES; } | COUNT_SYM '(' '*' ')' WARNINGS - { (void) create_select_for_variable("warning_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("warning_count")}; + (void) create_select_for_variable(thd, &var); + } | COUNT_SYM '(' '*' ')' ERRORS - { (void) create_select_for_variable("error_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; + (void) create_select_for_variable(thd, &var); + } | WARNINGS opt_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause @@ -13215,13 +13836,21 @@ show_param: Lex->set_command(SQLCOM_SHOW_CREATE_DB, $3); Lex->name= $4; } - | CREATE TABLE_SYM table_ident + | CREATE TABLE_SYM table_ident opt_for_system_time_clause { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0)) MYSQL_YYABORT; lex->create_info.storage_media= HA_SM_DEFAULT; + + if (lex->vers_conditions.type != SYSTEM_TIME_UNSPECIFIED && + lex->vers_conditions.type != SYSTEM_TIME_AS_OF) + { + my_yyabort_error((ER_VERS_RANGE_PROHIBITED, MYF(0))); + } + if ($4) + Lex->last_table()->vers_conditions= Lex->vers_conditions; } | CREATE VIEW_SYM table_ident { @@ -13333,7 +13962,7 @@ show_param: LEX *lex= Lex; bool in_plugin; lex->sql_command= SQLCOM_SHOW_GENERIC; - ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin); + ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1, &in_plugin); if (!table || !table->old_format || !in_plugin) { thd->parse_error(ER_SYNTAX_ERROR, $2); @@ -13420,7 +14049,7 @@ describe: mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->select_lex.db= 0; + lex->select_lex.db= null_clex_str; lex->verbose= 0; if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS)) MYSQL_YYABORT; @@ -13473,7 +14102,8 @@ opt_format_json: else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) DBUG_ASSERT(Lex->explain_json==false); else - my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), $3.str)); + my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), "EXPLAIN", + $3.str)); } ; @@ -13606,14 +14236,14 @@ flush_option: | IDENT_sys remember_tok_start { Lex->type|= REFRESH_GENERIC; - ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str); + ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1); if (!table || !table->reset_table) { thd->parse_error(ER_SYNTAX_ERROR, $2); MYSQL_YYABORT; } - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$1, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING)), thd->mem_root); } ; @@ -13772,7 +14402,7 @@ use: { LEX *lex=Lex; lex->sql_command=SQLCOM_CHANGE_DB; - lex->select_lex.db= $2.str; + lex->select_lex.db= $2; } ; @@ -14382,17 +15012,17 @@ field_ident: | ident '.' ident '.' ident { TABLE_LIST *table= Select->table_list.first; - if (my_strcasecmp(table_alias_charset, $1.str, table->db)) + if (my_strcasecmp(table_alias_charset, $1.str, table->db.str)) my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); if (my_strcasecmp(table_alias_charset, $3.str, - table->table_name)) + table->table_name.str)) my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3.str)); $$=$5; } | ident '.' ident { TABLE_LIST *table= Select->table_list.first; - if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) + if (my_strcasecmp(table_alias_charset, $1.str, table->alias.str)) my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str)); $$=$3; } @@ -14453,7 +15083,7 @@ IDENT_sys: if (thd->charset_is_system_charset) { CHARSET_INFO *cs= system_charset_info; - uint wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); + size_t wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); if (wlen < $1.length) { ErrConvString err($1.str, $1.length, &my_charset_bin); @@ -14687,6 +15317,7 @@ keyword_alias: | OTHERS_SYM {} | OWNER_SYM {} | PARSER_SYM {} + | PERIOD_SYM {} | PORT_SYM {} | PRECEDES_SYM {} | PRECEDING_SYM {} @@ -14708,6 +15339,7 @@ keyword_alias: | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} + | WITHIN {} | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} @@ -14891,6 +15523,7 @@ keyword_sp_not_data_type: | GOTO_SYM {} | HASH_SYM {} | HARD_SYM {} + | HISTORY_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | ID_SYM {} @@ -14908,6 +15541,7 @@ keyword_sp_not_data_type: | ISOPEN_SYM {} | ISSUER_SYM {} | INSERT_METHOD {} + | INVISIBLE_SYM {} | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} @@ -14972,6 +15606,7 @@ keyword_sp_not_data_type: | NOMINVALUE_SYM {} | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} + | NOWAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} | NOTFOUND_SYM {} @@ -15067,6 +15702,8 @@ keyword_sp_not_data_type: | SUSPEND_SYM {} | SWAPS_SYM {} | SWITCHES_SYM {} + | SYSTEM {} + | SYSTEM_TIME_SYM {} | TABLE_NAME_SYM {} | TABLES {} | TABLE_CHECKSUM_SYM {} @@ -15092,6 +15729,7 @@ keyword_sp_not_data_type: | USER_SYM {} | USE_FRM {} | VARIABLES {} + | VERSIONING_SYM {} | VIEW_SYM {} | VIRTUAL_SYM {} | VALUE_SYM {} @@ -15099,6 +15737,7 @@ keyword_sp_not_data_type: | WAIT_SYM {} | WEEK_SYM {} | WEIGHT_STRING_SYM {} + | WITHOUT {} | WORK_SYM {} | X509_SYM {} | XML_SYM {} @@ -15541,10 +16180,27 @@ lock: my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); lex->sql_command= SQLCOM_LOCK_TABLES; } - table_lock_list + table_lock_list opt_lock_wait_timeout {} ; +opt_lock_wait_timeout: + /* empty */ + {} + | WAIT_SYM ulong_num + { + if (set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), $2) || + set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), $2)) + MYSQL_YYABORT; + } + | NOWAIT_SYM + { + if (set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), 0) || + set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), 0)) + MYSQL_YYABORT; + } + ; + table_or_tables: TABLE_SYM { } | TABLES { } @@ -15893,6 +16549,7 @@ object_privilege: | EVENT_SYM { Lex->grant |= EVENT_ACL;} | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } + | DELETE_SYM HISTORY_SYM { Lex->grant |= DELETE_HISTORY_ACL; } ; opt_and: @@ -15933,8 +16590,7 @@ grant_ident: '*' { LEX *lex= Lex; - size_t dummy; - if (lex->copy_db_to(&lex->current_select->db, &dummy)) + if (lex->copy_db_to(&lex->current_select->db)) MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; @@ -15944,7 +16600,7 @@ grant_ident: | ident '.' '*' { LEX *lex= Lex; - lex->current_select->db = $1.str; + lex->current_select->db= $1; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) @@ -15953,7 +16609,7 @@ grant_ident: | '*' '.' '*' { LEX *lex= Lex; - lex->current_select->db = NULL; + lex->current_select->db= null_clex_str; if (lex->grant == GLOBAL_ACLS) lex->grant= GLOBAL_ACLS & ~GRANT_ACL; else if (lex->columns.elements) @@ -16354,6 +17010,31 @@ union_option: | ALL { $$=0; } ; +simple_table: + query_specification { $$= $1; } + | table_value_constructor { $$= $1; } + ; + +table_value_constructor: + VALUES + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + } + values_list + { + LEX *lex=Lex; + $$= lex->current_select; + mysql_init_select(Lex); + if (!($$->tvc= + new (lex->thd->mem_root) table_value_constr(lex->many_values, $$, $$->options))) + MYSQL_YYABORT; + lex->many_values.empty(); + } + ; + /* Corresponds to the SQL Standard <query specification> ::= @@ -16371,12 +17052,12 @@ query_specification: ; query_term_union_not_ready: - query_specification order_or_limit opt_select_lock_type { $$= $1; } + simple_table order_or_limit opt_select_lock_type { $$= $1; } | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } ; query_term_union_ready: - query_specification opt_select_lock_type { $$= $1; } + simple_table opt_select_lock_type { $$= $1; } | '(' select_paren_derived ')' { $$= $2; } ; @@ -16546,14 +17227,14 @@ view_list_opt: view_list: ident { - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$1, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING)), thd->mem_root); } | view_list ',' ident { - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$3, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$3, sizeof(LEX_CSTRING)), thd->mem_root); } ; @@ -16568,13 +17249,11 @@ view_select: { LEX *lex= Lex; size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; - uint not_used; void *create_view_select= thd->memdup(lex->create_view->select.str, len); lex->create_view->select.length= len; lex->create_view->select.str= (char *) create_view_select; trim_whitespace(thd->charset(), - &lex->create_view->select, - ¬_used); + &lex->create_view->select); lex->create_view->check= $4; lex->parsing_options.allows_variable= TRUE; lex->current_select->set_with_clause($2); @@ -16587,6 +17266,9 @@ view_select: */ query_expression_body_view: SELECT_SYM select_options_and_item_list select_init3_view + | table_value_constructor + | table_value_constructor union_order_or_limit + | table_value_constructor union_list_view | '(' select_paren_view ')' | '(' select_paren_view ')' union_order_or_limit | '(' select_paren_view ')' union_list_view @@ -16644,9 +17326,14 @@ trigger_tail: } table_ident /* $10 */ FOR_SYM - remember_name /* $12 */ - { /* $13 */ - Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start(); + remember_name /* $13 */ + { /* $14 */ + /* + FOR token is already passed through (see 'case FOR_SYM' in sql_lex.cc), + so we use _prev() to get it back. + */ + DBUG_ASSERT(YYLIP->lookahead_token >= 0); + Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start_prev(); } EACH_SYM ROW_SYM @@ -16824,7 +17511,8 @@ opt_format_xid: $$= false; else { - my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), "XA RECOVER", $3.str)); + my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), + "XA RECOVER", $3.str)); $$= false; } } diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index cd1885e6b80..dd84847387a 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -33,7 +33,7 @@ #define Lex (thd->lex) #define Select Lex->current_select -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "sql_parse.h" /* comp_*_creator */ #include "sql_table.h" /* primary_key_name */ @@ -78,7 +78,7 @@ int yylex(void *yylval, void *yythd); #define yyoverflow(A,B,C,D,E,F) \ { \ - ulong val= *(F); \ + size_t val= *(F); \ if (my_yyoverflow((B), (D), &val)) \ { \ yyerror(thd, (char*) (A)); \ @@ -268,7 +268,7 @@ void ORAerror(THD *thd, const char *s) } %{ -bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); +bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %} %pure-parser /* We have threads */ @@ -284,6 +284,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); Comments for TOKENS. For each token, please include in the same line a comment that contains the following tags: + SQL-2011-R : Reserved keyword as per SQL-2011 SQL-2011-N : Non Reserved keyword as per SQL-2011 SQL-2003-R : Reserved keyword as per SQL-2003 SQL-2003-N : Non Reserved keyword as per SQL-2003 @@ -508,6 +509,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FORCE_SYM %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ +%token FOR_SYSTEM_TIME_SYM /* INTERNAL */ %token FORMAT_SYM %token FOUND_SYM /* SQL-2003-R */ %token FROM @@ -537,6 +539,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token HEX_NUM %token HEX_STRING %token HIGH_PRIORITY +%token HISTORY_SYM /* MYSQL */ %token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM @@ -577,6 +580,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ISOPEN_SYM /* Oracle-N */ %token ISSUER_SYM %token ITERATE_SYM +%token INVISIBLE_SYM %token JOIN_SYM /* SQL-2003-R */ %token JSON_SYM %token KEYS @@ -646,6 +650,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM %token MAXVALUE_SYM /* SQL-2003-N */ +%token MEDIAN_SYM %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -695,6 +700,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOMAXVALUE_SYM %token NOMINVALUE_SYM %token NO_WAIT_SYM +%token NOWAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM %token NULL_SYM /* SQL-2003-R */ @@ -737,6 +743,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERCENTILE_CONT_SYM +%token PERCENTILE_DISC_SYM +%token PERIOD_SYM /* SQL-2011-R */ %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -903,6 +912,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SWAPS_SYM %token SWITCHES_SYM %token SYSDATE +%token SYSTEM /* SQL-2011-R */ +%token SYSTEM_TIME_SYM /* SQL-2011-R */ %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY @@ -963,6 +974,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UTC_TIMESTAMP_SYM %token UTC_TIME_SYM %token VALUES /* SQL-2003-R */ +%token VALUES_IN_SYM +%token VALUES_LESS_SYM %token VALUE_SYM /* SQL-2003-R */ %token VARBINARY %token VARCHAR /* SQL-2003-R */ @@ -971,6 +984,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM +%token VERSIONING_SYM /* SQL-2011-R */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -983,8 +997,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHIN +%token WITHOUT /* SQL-2003-R */ %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ +%token WITH_SYSTEM_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ %token WRAPPER_SYM %token WRITE_SYM /* SQL-2003-N */ @@ -1085,7 +1102,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -1151,6 +1168,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); window_func_expr window_func simple_window_func + inverse_distribution_function + percentile_function + inverse_distribution_function_def explicit_cursor_attr function_call_keyword function_call_nonkeyword @@ -1332,7 +1352,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute - explainable_command + explainable_command opt_lock_wait_timeout set_assign sf_tail_standalone sp_tail_standalone @@ -1959,8 +1979,8 @@ create: if (lex->create_info.seq_create_info->check_and_adjust(1)) { my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), - lex->select_lex.table_list.first->db, - lex->select_lex.table_list.first->table_name); + lex->select_lex.table_list.first->db.str, + lex->select_lex.table_list.first->table_name.str); MYSQL_YYABORT; } @@ -1994,7 +2014,7 @@ create: if (Lex->add_create_index($2, &$5, $6, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' normal_key_options + '(' key_list ')' opt_lock_wait_timeout normal_key_options opt_index_lock_algorithm { } | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2004,7 +2024,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' fulltext_key_options + '(' key_list ')' opt_lock_wait_timeout fulltext_key_options opt_index_lock_algorithm { } | create_or_replace spatial INDEX_SYM opt_if_not_exists ident ON table_ident @@ -2014,7 +2034,7 @@ create: if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } - '(' key_list ')' spatial_key_options + '(' key_list ')' opt_lock_wait_timeout spatial_key_options opt_index_lock_algorithm { } | create_or_replace DATABASE opt_if_not_exists ident { @@ -2418,7 +2438,7 @@ clear_privileges: lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; lex->all_privileges= 0; - lex->select_lex.db= 0; + lex->select_lex.db= null_clex_str; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; bzero((char *)&(lex->mqh),sizeof(lex->mqh)); @@ -3621,7 +3641,7 @@ opt_parenthesized_cursor_actual_parameters: sp_proc_stmt_open: OPEN_SYM ident opt_parenthesized_cursor_actual_parameters { - if (Lex->sp_open_cursor(thd, &$2, $3)) + if (Lex->sp_open_cursor(thd, &$2, $3)) MYSQL_YYABORT; } ; @@ -4611,7 +4631,7 @@ size_number: uint text_shift_number= 0; longlong prefix_number; const char *start_ptr= $1.str; - uint str_len= $1.length; + size_t str_len= $1.length; const char *end_ptr= start_ptr + str_len; int error; prefix_number= my_strtoll10(start_ptr, (char**) &end_ptr, &error); @@ -4819,8 +4839,15 @@ part_type_def: { Lex->part_info->part_type= RANGE_PARTITION; } | RANGE_SYM part_column_list { Lex->part_info->part_type= RANGE_PARTITION; } - | LIST_SYM part_func - { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM + { + Select->parsing_place= IN_PART_FUNC; + } + part_func + { + Lex->part_info->part_type= LIST_PARTITION; + Select->parsing_place= NO_MATTER; + } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } ; @@ -5070,7 +5097,7 @@ opt_part_values: else part_info->part_type= HASH_PARTITION; } - | VALUES LESS_SYM THAN_SYM + | VALUES_LESS_SYM THAN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -5084,7 +5111,7 @@ opt_part_values: part_info->part_type= RANGE_PARTITION; } part_func_max {} - | VALUES IN_SYM + | VALUES_IN_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; @@ -6108,6 +6135,10 @@ vcol_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + | INVISIBLE_SYM + { + Lex->last_field->invisible= INVISIBLE_USER; + } ; parse_vcol_expr: @@ -7130,7 +7161,7 @@ alter: Lex->create_info.storage_media= HA_SM_DEFAULT; DBUG_ASSERT(!Lex->m_sql_cmd); } - alter_options TABLE_SYM table_ident + alter_options TABLE_SYM table_ident opt_lock_wait_timeout { if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, @@ -7160,8 +7191,7 @@ alter: LEX *lex=Lex; lex->sql_command=SQLCOM_ALTER_DB; lex->name= $3; - if (lex->name.str == NULL && - lex->copy_db_to(&lex->name.str, &lex->name.length)) + if (lex->name.str == NULL && lex->copy_db_to(&lex->name)) MYSQL_YYABORT; } | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM @@ -7464,10 +7494,9 @@ alter_commands: WITH TABLE_SYM table_ident have_partitioning { LEX *lex= thd->lex; - size_t dummy; - lex->select_lex.db=$6->db.str; - if (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, &dummy)) + lex->select_lex.db= $6->db; + if (lex->select_lex.db.str == NULL && + lex->copy_db_to(&lex->select_lex.db)) { MYSQL_YYABORT; } @@ -7705,32 +7734,22 @@ alter_list_item: lex->alter_info.keys_onoff= Alter_info::ENABLE; lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; } - | ALTER opt_column field_ident SET DEFAULT column_default_expr + | ALTER opt_column opt_if_exists_table_element field_ident SET DEFAULT column_default_expr { - LEX *lex=Lex; - Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6); - if (ac == NULL) + if (Lex->add_alter_list($4.str, $7, $3)) MYSQL_YYABORT; - lex->alter_info.alter_list.push_back(ac, thd->mem_root); - lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } - | ALTER opt_column field_ident DROP DEFAULT + | ALTER opt_column opt_if_exists_table_element field_ident DROP DEFAULT { - LEX *lex=Lex; - Alter_column *ac= (new (thd->mem_root) - Alter_column($3.str, (Virtual_column_info*) 0)); - if (ac == NULL) + if (Lex->add_alter_list($4.str, (Virtual_column_info*) 0, $3)) MYSQL_YYABORT; - lex->alter_info.alter_list.push_back(ac, thd->mem_root); - lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } | RENAME opt_to table_ident { LEX *lex=Lex; - size_t dummy; - lex->select_lex.db=$3->db.str; - if (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, &dummy)) + lex->select_lex.db= $3->db; + if (lex->select_lex.db.str == NULL && + lex->copy_db_to(&lex->select_lex.db)) { MYSQL_YYABORT; } @@ -8246,7 +8265,7 @@ optimize: /* Will be overridden during execution. */ YYPS->m_lock_type= TL_UNLOCK; } - table_list + table_list opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -8296,13 +8315,13 @@ table_to_table_list: ; table_to_table: - table_ident TO_SYM table_ident + table_ident opt_lock_wait_timeout TO_SYM table_ident { LEX *lex=Lex; SELECT_LEX *sl= lex->current_select; if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE) || - !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + !sl->add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, TL_IGNORE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } @@ -8709,14 +8728,14 @@ select_option: opt_select_lock_type: /* empty */ - | FOR_SYM UPDATE_SYM + | FOR_SYM UPDATE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_WRITE; lex->current_select->set_lock_for_tables(TL_WRITE); lex->safe_to_cache_query=0; } - | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout { LEX *lex=Lex; lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; @@ -9377,6 +9396,7 @@ column_default_non_parenthesized_expr: | variable | sum_expr | window_func_expr + | inverse_distribution_function | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3, thd->mem_root); @@ -9478,8 +9498,9 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } - | VALUES '(' simple_ident_nospvar ')' + | VALUE_SYM '(' simple_ident_nospvar ')' { $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), $3); @@ -10554,16 +10575,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -10705,6 +10732,67 @@ simple_window_func: MYSQL_YYABORT; } ; +inverse_distribution_function: + percentile_function OVER_SYM + '(' opt_window_partition_clause ')' + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +percentile_function: + inverse_distribution_function_def WITHIN GROUP_SYM '(' + { Select->prepare_add_window_spec(thd); } + order_by_single_element_list ')' + { + $$= $1; + } + | MEDIAN_SYM '(' expr ')' + { + Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + if (add_order_to_list(thd, $3,FALSE)) MYSQL_YYABORT; + + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, args); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +inverse_distribution_function_def: + PERCENTILE_CONT_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_cont(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PERCENTILE_DISC_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_percentile_disc(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +order_by_single_element_list: + ORDER_SYM BY order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + ; window_name: ident @@ -10754,7 +10842,7 @@ variable_aux: thd->parse_error(); MYSQL_YYABORT; } - if (!($$= get_system_var(thd, $2, $3, $4))) + if (!($$= get_system_var(thd, $2, &$3, &$4))) MYSQL_YYABORT; if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); @@ -10788,6 +10876,46 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + in_sum_expr: opt_all { @@ -12274,9 +12402,9 @@ drop: YYPS->m_lock_type= TL_UNLOCK; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_list opt_restrict + table_list opt_lock_wait_timeout opt_restrict {} - | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout { LEX *lex=Lex; Alter_drop *ad= (new (thd->mem_root) @@ -12320,7 +12448,7 @@ drop: sp_name *spname; if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); - if (thd->db && lex->copy_db_to(&db.str, &db.length)) + if (thd->db.str && lex->copy_db_to(&db)) MYSQL_YYABORT; lex->set_command(SQLCOM_DROP_FUNCTION, $3); spname= new (thd->mem_root) sp_name(&db, &$4, false); @@ -12669,7 +12797,14 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } - KEY_SYM UPDATE_SYM insert_update_list + KEY_SYM UPDATE_SYM + { + Select->parsing_place= IN_UPDATE_ON_DUP_KEY; + } + insert_update_list + { + Select->parsing_place= NO_MATTER; + } ; /* Update rows in a table */ @@ -12692,7 +12827,7 @@ update: { /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), - lex->select_lex.get_table_list()->alias, "UPDATE"); + lex->select_lex.get_table_list()->alias.str, "UPDATE"); MYSQL_YYABORT; } /* @@ -12861,7 +12996,7 @@ truncate: YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_name + table_name opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); @@ -12971,7 +13106,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) MYSQL_YYABORT; } @@ -12979,7 +13114,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TRIGGERS; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) MYSQL_YYABORT; } @@ -12987,7 +13122,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $2.str; + lex->select_lex.db= $2; if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) MYSQL_YYABORT; } @@ -12995,7 +13130,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) MYSQL_YYABORT; } @@ -13003,7 +13138,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3.str; + lex->select_lex.db= $3; if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; } @@ -13091,9 +13226,15 @@ show_param: lex->sql_command= SQLCOM_SHOW_PRIVILEGES; } | COUNT_SYM '(' '*' ')' WARNINGS - { (void) create_select_for_variable("warning_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("warning_count")}; + (void) create_select_for_variable(thd, &var); + } | COUNT_SYM '(' '*' ')' ERRORS - { (void) create_select_for_variable("error_count"); } + { + LEX_CSTRING var= {STRING_WITH_LEN("error_count")}; + (void) create_select_for_variable(thd, &var); + } | WARNINGS opt_limit_clause { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause @@ -13275,7 +13416,7 @@ show_param: LEX *lex= Lex; bool in_plugin; lex->sql_command= SQLCOM_SHOW_GENERIC; - ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin); + ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1, &in_plugin); if (!table || !table->old_format || !in_plugin) { thd->parse_error(ER_SYNTAX_ERROR, $2); @@ -13362,7 +13503,7 @@ describe: mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->select_lex.db= 0; + lex->select_lex.db= null_clex_str; lex->verbose= 0; if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS)) MYSQL_YYABORT; @@ -13415,7 +13556,8 @@ opt_format_json: else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) DBUG_ASSERT(Lex->explain_json==false); else - my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), $3.str)); + my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), "EXPLAIN", + $3.str)); } ; @@ -13548,14 +13690,14 @@ flush_option: | IDENT_sys remember_tok_start { Lex->type|= REFRESH_GENERIC; - ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str); + ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1); if (!table || !table->reset_table) { thd->parse_error(ER_SYNTAX_ERROR, $2); MYSQL_YYABORT; } - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$1, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING)), thd->mem_root); } ; @@ -13696,7 +13838,7 @@ use: { LEX *lex=Lex; lex->sql_command=SQLCOM_CHANGE_DB; - lex->select_lex.db= $2.str; + lex->select_lex.db= $2; } ; @@ -14335,17 +14477,17 @@ field_ident: | ident '.' ident '.' ident { TABLE_LIST *table= Select->table_list.first; - if (my_strcasecmp(table_alias_charset, $1.str, table->db)) + if (my_strcasecmp(table_alias_charset, $1.str, table->db.str)) my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); if (my_strcasecmp(table_alias_charset, $3.str, - table->table_name)) + table->table_name.str)) my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3.str)); $$=$5; } | ident '.' ident { TABLE_LIST *table= Select->table_list.first; - if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) + if (my_strcasecmp(table_alias_charset, $1.str, table->alias.str)) my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str)); $$=$3; } @@ -14406,7 +14548,7 @@ IDENT_sys: if (thd->charset_is_system_charset) { CHARSET_INFO *cs= system_charset_info; - uint wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); + size_t wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); if (wlen < $1.length) { ErrConvString err($1.str, $1.length, &my_charset_bin); @@ -14685,6 +14827,7 @@ keyword_directly_assignable: | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} + | WITHIN {} | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} @@ -14908,6 +15051,7 @@ keyword_sp_not_data_type: | GLOBAL_SYM {} | HASH_SYM {} | HARD_SYM {} + | INVISIBLE_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | ID_SYM {} @@ -14989,6 +15133,7 @@ keyword_sp_not_data_type: | NOMINVALUE_SYM {} | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} + | NOWAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} | NOTFOUND_SYM {} @@ -15040,7 +15185,7 @@ keyword_sp_not_data_type: | RESUME_SYM {} | RETURNED_SQLSTATE_SYM {} | RETURNS_SYM {} - | REUSE_SYM {} /* Oracle-R */ + | REUSE_SYM {} | REVERSE_SYM {} | ROLE_SYM {} | ROLLUP_SYM {} @@ -15611,10 +15756,27 @@ lock: my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); lex->sql_command= SQLCOM_LOCK_TABLES; } - table_lock_list + table_lock_list opt_lock_wait_timeout {} ; +opt_lock_wait_timeout: + /* empty */ + {} + | WAIT_SYM ulong_num + { + if (set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), $2) || + set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), $2)) + MYSQL_YYABORT; + } + | NOWAIT_SYM + { + if (set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), 0) || + set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), 0)) + MYSQL_YYABORT; + } + ; + table_or_tables: TABLE_SYM { } | TABLES { } @@ -16003,8 +16165,7 @@ grant_ident: '*' { LEX *lex= Lex; - size_t dummy; - if (lex->copy_db_to(&lex->current_select->db, &dummy)) + if (lex->copy_db_to(&lex->current_select->db)) MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; @@ -16014,7 +16175,7 @@ grant_ident: | ident '.' '*' { LEX *lex= Lex; - lex->current_select->db = $1.str; + lex->current_select->db= $1; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) @@ -16023,7 +16184,7 @@ grant_ident: | '*' '.' '*' { LEX *lex= Lex; - lex->current_select->db = NULL; + lex->current_select->db= null_clex_str; if (lex->grant == GLOBAL_ACLS) lex->grant= GLOBAL_ACLS & ~GRANT_ACL; else if (lex->columns.elements) @@ -16607,14 +16768,14 @@ view_list_opt: view_list: ident { - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$1, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING)), thd->mem_root); } | view_list ',' ident { - Lex->view_list.push_back((LEX_STRING*) - thd->memdup(&$3, sizeof(LEX_STRING)), + Lex->view_list.push_back((LEX_CSTRING*) + thd->memdup(&$3, sizeof(LEX_CSTRING)), thd->mem_root); } ; @@ -16629,13 +16790,11 @@ view_select: { LEX *lex= Lex; size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; - uint not_used; void *create_view_select= thd->memdup(lex->create_view->select.str, len); lex->create_view->select.length= len; lex->create_view->select.str= (char *) create_view_select; trim_whitespace(thd->charset(), - &lex->create_view->select, - ¬_used); + &lex->create_view->select); lex->create_view->check= $4; lex->parsing_options.allows_variable= TRUE; lex->current_select->set_with_clause($2); diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 1c0b672fbcc..f701c4a09ed 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -15,7 +15,7 @@ /* Some useful string utility functions used by the MySQL server */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "strfunc.h" @@ -45,7 +45,7 @@ static const char field_separator=','; -ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs, +ulonglong find_set(TYPELIB *lib, const char *str, size_t length, CHARSET_INFO *cs, char **err_pos, uint *err_len, bool *set_warning) { CHARSET_INFO *strip= cs ? cs : &my_charset_latin1; @@ -111,7 +111,7 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs, > 0 position in TYPELIB->type_names +1 */ -uint find_type(const TYPELIB *lib, const char *find, uint length, +uint find_type(const TYPELIB *lib, const char *find, size_t length, bool part_match) { uint found_count=0, found_pos=0; @@ -152,13 +152,13 @@ uint find_type(const TYPELIB *lib, const char *find, uint length, >0 Offset+1 in typelib for matched string */ -uint find_type2(const TYPELIB *typelib, const char *x, uint length, +uint find_type2(const TYPELIB *typelib, const char *x, size_t length, CHARSET_INFO *cs) { int pos; const char *j; DBUG_ENTER("find_type2"); - DBUG_PRINT("enter",("x: '%.*s' lib: %p", length, x, typelib)); + DBUG_PRINT("enter",("x: '%.*s' lib: %p", (int)length, x, typelib)); if (!typelib->count) { @@ -265,8 +265,8 @@ uint check_word(TYPELIB *lib, const char *val, const char *end, */ -uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length, - CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors) +uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length, + CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors) { int cnvres; my_wc_t wc; diff --git a/sql/strfunc.h b/sql/strfunc.h index 7f3021e1a6d..1bf3cbf47b3 100644 --- a/sql/strfunc.h +++ b/sql/strfunc.h @@ -16,19 +16,17 @@ #ifndef STRFUNC_INCLUDED #define STRFUNC_INCLUDED -#include "my_global.h" /* ulonglong, uint */ - typedef struct st_typelib TYPELIB; -ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs, +ulonglong find_set(TYPELIB *lib, const char *x, size_t length, CHARSET_INFO *cs, char **err_pos, uint *err_len, bool *set_warning); ulonglong find_set_from_flags(TYPELIB *lib, uint default_name, ulonglong cur_set, ulonglong default_set, const char *str, uint length, CHARSET_INFO *cs, char **err_pos, uint *err_len, bool *set_warning); -uint find_type(const TYPELIB *lib, const char *find, uint length, +uint find_type(const TYPELIB *lib, const char *find, size_t length, bool part_match); -uint find_type2(const TYPELIB *lib, const char *find, uint length, +uint find_type2(const TYPELIB *lib, const char *find, size_t length, CHARSET_INFO *cs); void unhex_type2(TYPELIB *lib); uint check_word(TYPELIB *lib, const char *val, const char *end, @@ -44,7 +42,7 @@ const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, /* These functions were protected by INNODB_COMPATIBILITY_HOOKS */ -uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length, - CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors); +uint strconvert(CHARSET_INFO *from_cs, const char *from, size_t from_length, + CHARSET_INFO *to_cs, char *to, size_t to_length, uint *errors); #endif /* STRFUNC_INCLUDED */ diff --git a/sql/structs.h b/sql/structs.h index 97702e5727b..4d05a2433cc 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -39,7 +39,7 @@ typedef struct st_date_time_format { uchar positions[8]; char time_separator; /* Separator between hour and minute */ uint flag; /* For future */ - LEX_STRING format; + LEX_CSTRING format; } DATE_TIME_FORMAT; @@ -109,9 +109,9 @@ typedef struct st_key { pk2 is explicitly present in idx1, it is not in the extension, so ext_key_part_map.is_set(1) == false */ + LEX_CSTRING name; key_part_map ext_key_part_map; uint block_size; - uint name_length; enum ha_key_alg algorithm; /* The flag is on if statistical data for the index prefixes @@ -128,7 +128,6 @@ typedef struct st_key { LEX_CSTRING *parser_name; /* Fulltext [pre]parser name */ }; KEY_PART_INFO *key_part; - const char *name; /* Name of key */ /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */ uchar *cache_name; /* @@ -331,7 +330,7 @@ typedef struct st_user_stats typedef struct st_table_stats { char table[NAME_LEN * 2 + 2]; // [db] + '\0' + [table] + '\0' - uint table_name_length; + size_t table_name_length; ulonglong rows_read, rows_changed; ulonglong rows_changed_x_indexes; /* Stores enum db_type, but forward declarations cannot be done */ @@ -342,7 +341,7 @@ typedef struct st_index_stats { // [db] + '\0' + [table] + '\0' + [index] + '\0' char index[NAME_LEN * 3 + 3]; - uint index_name_length; /* Length of 'index' */ + size_t index_name_length; /* Length of 'index' */ ulonglong rows_read; } INDEX_STATS; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index a517ab0965e..291f13d27f8 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -31,7 +31,7 @@ (for example in storage/myisam/ha_myisam.cc) ! */ -#include "sql_plugin.h" // Includes my_global.h +#include "sql_plugin.h" #include "sql_priv.h" #include "sql_class.h" // set_var.h: THD #include "sys_vars.ic" @@ -51,7 +51,6 @@ #include "sql_base.h" // close_cached_tables #include "hostname.h" // host_cache_size #include <myisam.h> -#include "log_slow.h" #include "debug_sync.h" // DEBUG_SYNC #include "sql_show.h" @@ -63,6 +62,8 @@ #include "sql_repl.h" #include "opt_range.h" #include "rpl_parallel.h" +#include "semisync_master.h" +#include "semisync_slave.h" #include <ssl_compat.h> /* @@ -384,6 +385,33 @@ static Sys_var_charptr Sys_basedir( READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT(0)); +static Sys_var_charptr Sys_my_bind_addr( + "bind_address", "IP address to bind to.", + READ_ONLY GLOBAL_VAR(my_bind_addr_str), CMD_LINE(REQUIRED_ARG), + IN_FS_CHARSET, DEFAULT(0)); + +const char *Sys_var_vers_asof::asof_keywords[]= {"DEFAULT", NULL}; +static Sys_var_vers_asof Sys_vers_asof_timestamp( + "system_versioning_asof", "Default value for the FOR SYSTEM_TIME AS OF clause", + SESSION_VAR(vers_asof_timestamp.type), NO_CMD_LINE, + Sys_var_vers_asof::asof_keywords, DEFAULT(SYSTEM_TIME_UNSPECIFIED)); + +static Sys_var_mybool Sys_vers_innodb_algorithm_simple( + "system_versioning_innodb_algorithm_simple", + "Use simple algorithm of timestamp handling in InnoDB instead of TRX_SEES", + SESSION_VAR(vers_innodb_algorithm_simple), CMD_LINE(OPT_ARG), + DEFAULT(TRUE)); + +static const char *vers_alter_history_keywords[]= {"ERROR", "KEEP",/* "SURVIVE", "DROP",*/ NULL}; +static Sys_var_enum Sys_vers_alter_history( + "system_versioning_alter_history", "Versioning ALTER TABLE mode. " + "ERROR: Fail ALTER with error; " /* TODO: fail only when history non-empty */ + "KEEP: Keep historical system rows and subject them to ALTER; " + /*"SURVIVE: Keep historical system rows intact; " + "DROP: Drop historical system rows while processing ALTER"*/, + SESSION_VAR(vers_alter_history), CMD_LINE(REQUIRED_ARG), + vers_alter_history_keywords, DEFAULT(VERS_ALTER_HISTORY_ERROR)); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " @@ -393,6 +421,13 @@ static Sys_var_ulonglong Sys_binlog_cache_size( CMD_LINE(REQUIRED_ARG), VALID_RANGE(IO_SIZE, SIZE_T_MAX), DEFAULT(32768), BLOCK_SIZE(IO_SIZE)); +static Sys_var_ulonglong Sys_binlog_file_cache_size( + "binlog_file_cache_size", + "The size of file cache for the binary log", + GLOBAL_VAR(binlog_file_cache_size), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(IO_SIZE*2, SIZE_T_MAX), DEFAULT(IO_SIZE*4), BLOCK_SIZE(IO_SIZE)); + static Sys_var_ulonglong Sys_binlog_stmt_cache_size( "binlog_stmt_cache_size", "The size of the statement cache for " "updates to non-transactional engines for the binary log. " @@ -563,8 +598,7 @@ static Sys_var_mybool Sys_explicit_defaults_for_timestamp( "explicit_defaults_for_timestamp", "This option causes CREATE TABLE to create all TIMESTAMP columns " "as NULL with DEFAULT NULL attribute, Without this option, " - "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses. " - "The old behavior is deprecated.", + "TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses.", READ_ONLY GLOBAL_VAR(opt_explicit_defaults_for_timestamp), CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG); @@ -765,6 +799,53 @@ static Sys_var_struct Sys_collation_server( offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info), NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_not_null)); +static Sys_var_uint Sys_column_compression_threshold( + "column_compression_threshold", + "Minimum column data length eligible for compression", + SESSION_VAR(column_compression_threshold), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(100), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_column_compression_zlib_level( + "column_compression_zlib_level", + "zlib compression level (1 gives best speed, 9 gives best compression)", + SESSION_VAR(column_compression_zlib_level), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 9), DEFAULT(6), BLOCK_SIZE(1)); + +/* + Note that names must correspond to zlib strategy definition. So that we can + pass column_compression_zlib_strategy directly to deflateInit2(). +*/ +static const char *column_compression_zlib_strategy_names[]= +{ "DEFAULT_STRATEGY", "FILTERED", "HUFFMAN_ONLY", "RLE", "FIXED", 0 }; + +static Sys_var_enum Sys_column_compression_zlib_strategy( + "column_compression_zlib_strategy", + "The strategy parameter is used to tune the compression algorithm. Use " + "the value DEFAULT_STRATEGY for normal data, FILTERED for data produced " + "by a filter (or predictor), HUFFMAN_ONLY to force Huffman encoding " + "only (no string match), or RLE to limit match distances to one " + "(run-length encoding). Filtered data consists mostly of small values " + "with a somewhat random distribution. In this case, the compression " + "algorithm is tuned to compress them better. The effect of FILTERED is " + "to force more Huffman coding and less string matching; it is somewhat " + "intermediate between DEFAULT_STRATEGY and HUFFMAN_ONLY. RLE is " + "designed to be almost as fast as HUFFMAN_ONLY, but give better " + "compression for PNG image data. The strategy parameter only affects " + "the compression ratio but not the correctness of the compressed output " + "even if it is not set appropriately. FIXED prevents the use of dynamic " + "Huffman codes, allowing for a simpler decoder for special " + "applications.", + SESSION_VAR(column_compression_zlib_strategy), CMD_LINE(REQUIRED_ARG), + column_compression_zlib_strategy_names, DEFAULT(0)); + +static Sys_var_mybool Sys_column_compression_zlib_wrap( + "column_compression_zlib_wrap", + "Generate zlib header and trailer and compute adler32 check value. " + "It can be used with storage engines that don't provide data integrity " + "verification to detect data corruption.", + SESSION_VAR(column_compression_zlib_wrap), CMD_LINE(OPT_ARG), + DEFAULT(FALSE)); + static const char *concurrent_insert_names[]= {"NEVER", "AUTO", "ALWAYS", 0}; static Sys_var_enum Sys_concurrent_insert( "concurrent_insert", "Use concurrent insert with MyISAM", @@ -1166,7 +1247,7 @@ static Sys_var_ulong Sys_lock_wait_timeout( "lock_wait_timeout", "Timeout in seconds to wait for a lock before returning an error.", SESSION_VAR(lock_wait_timeout), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1)); + VALID_RANGE(0, LONG_TIMEOUT), DEFAULT(24 * 60 * 60), BLOCK_SIZE(1)); #ifdef HAVE_MLOCKALL static Sys_var_mybool Sys_locked_in_memory( @@ -1213,25 +1294,28 @@ static Sys_var_charptr Sys_log_error( CMD_LINE(OPT_ARG, OPT_LOG_ERROR), IN_FS_CHARSET, DEFAULT(disabled_my_option)); -static Sys_var_mybool Sys_log_queries_not_using_indexes( +static Sys_var_bit Sys_log_queries_not_using_indexes( "log_queries_not_using_indexes", "Log queries that are executed without benefit of any index to the " - "slow log if it is open", - GLOBAL_VAR(opt_log_queries_not_using_indexes), - CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + "slow log if it is open. Same as log_slow_filter='not_using_index'", + SESSION_VAR(log_slow_filter), CMD_LINE(OPT_ARG), QPLAN_NOT_USING_INDEX, + DEFAULT(FALSE)); -static Sys_var_mybool Sys_log_slow_admin_statements( +static Sys_var_bit Sys_log_slow_admin_statements( "log_slow_admin_statements", - "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to " - "the slow log if it is open.", - GLOBAL_VAR(opt_log_slow_admin_statements), - CMD_LINE(OPT_ARG), DEFAULT(TRUE)); + "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements " + "to the slow log if it is open. Resets or sets the option 'admin' in " + "log_slow_disabled_statements", + SESSION_VAR(log_slow_disabled_statements), + CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_ADMIN), DEFAULT(TRUE)); -static Sys_var_mybool Sys_log_slow_slave_statements( +static Sys_var_bit Sys_log_slow_slave_statements( "log_slow_slave_statements", - "Log slow statements executed by slave thread to the slow log if it is open.", - GLOBAL_VAR(opt_log_slow_slave_statements), - CMD_LINE(OPT_ARG), DEFAULT(TRUE)); + "Log slow statements executed by slave thread to the slow log if it is " + "open. Resets or sets the option 'slave' in " + "log_slow_disabled_statements", + SESSION_VAR(log_slow_disabled_statements), + CMD_LINE(OPT_ARG), REVERSE(LOG_SLOW_DISABLE_SLAVE), DEFAULT(TRUE)); static Sys_var_ulong Sys_log_warnings( "log_warnings", @@ -2395,7 +2479,7 @@ export const char *optimizer_switch_names[]= "exists_to_in", "orderby_uses_equalities", "condition_pushdown_for_derived", - "split_grouping_derived", + "split_materialized", "default", NullS }; @@ -2735,7 +2819,7 @@ static Sys_var_enum Sys_thread_handling( #ifdef HAVE_QUERY_CACHE static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type) { - ulong new_cache_size= query_cache.resize((ulong)query_cache_size); + size_t new_cache_size= query_cache.resize((size_t)query_cache_size); /* Note: query_cache_size is a global variable reflecting the requested cache size. See also query_cache_size_arg @@ -2743,7 +2827,7 @@ static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type) if (query_cache_size != new_cache_size) push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_QC_RESIZE, ER_THD(thd, ER_WARN_QC_RESIZE), - query_cache_size, new_cache_size); + query_cache_size, (ulong)new_cache_size); query_cache_size= new_cache_size; @@ -2774,7 +2858,7 @@ static Sys_var_ulong Sys_query_cache_limit( static bool fix_qcache_min_res_unit(sys_var *self, THD *thd, enum_var_type type) { query_cache_min_res_unit= - query_cache.set_min_res_unit(query_cache_min_res_unit); + (ulong)query_cache.set_min_res_unit(query_cache_min_res_unit); return false; } static Sys_var_ulong Sys_query_cache_min_res_unit( @@ -2980,8 +3064,174 @@ static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip "the slave).", GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG), replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE)); -#endif +/* new options for semisync */ + +static bool fix_rpl_semi_sync_master_enabled(sys_var *self, THD *thd, + enum_var_type type) +{ + if (rpl_semi_sync_master_enabled) + { + if (repl_semisync_master.enable_master() != 0) + rpl_semi_sync_master_enabled= false; + else if (ack_receiver.start()) + { + repl_semisync_master.disable_master(); + rpl_semi_sync_master_enabled= false; + } + } + else + { + if (repl_semisync_master.disable_master() != 0) + rpl_semi_sync_master_enabled= true; + if (!rpl_semi_sync_master_enabled) + ack_receiver.stop(); + } + return false; +} + +static bool fix_rpl_semi_sync_master_timeout(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_wait_timeout(rpl_semi_sync_master_timeout); + return false; +} + +static bool fix_rpl_semi_sync_master_trace_level(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_trace_level(rpl_semi_sync_master_trace_level); + ack_receiver.set_trace_level(rpl_semi_sync_master_trace_level); + return false; +} + +static bool fix_rpl_semi_sync_master_wait_point(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.set_wait_point(rpl_semi_sync_master_wait_point); + return false; +} + +static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_master.check_and_switch(); + return false; +} + +static Sys_var_mybool Sys_semisync_master_enabled( + "rpl_semi_sync_master_enabled", + "Enable semi-synchronous replication master (disabled by default).", + GLOBAL_VAR(rpl_semi_sync_master_enabled), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_enabled)); + +static Sys_var_ulong Sys_semisync_master_timeout( + "rpl_semi_sync_master_timeout", + "The timeout value (in ms) for semi-synchronous replication in the " + "master", + GLOBAL_VAR(rpl_semi_sync_master_timeout), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(10000),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_timeout)); + +static Sys_var_mybool Sys_semisync_master_wait_no_slave( + "rpl_semi_sync_master_wait_no_slave", + "Wait until timeout when no semi-synchronous replication slave " + "available (enabled by default).", + GLOBAL_VAR(rpl_semi_sync_master_wait_no_slave), + CMD_LINE(OPT_ARG), DEFAULT(TRUE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_wait_no_slave)); + +static Sys_var_ulong Sys_semisync_master_trace_level( + "rpl_semi_sync_master_trace_level", + "The tracing level for semi-sync replication.", + GLOBAL_VAR(rpl_semi_sync_master_trace_level), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_trace_level)); + +static const char *repl_semisync_wait_point[]= +{"AFTER_SYNC", "AFTER_COMMIT", NullS}; + +static Sys_var_enum Sys_semisync_master_wait_point( + "rpl_semi_sync_master_wait_point", + "Should transaction wait for semi-sync ack after having synced binlog, " + "or after having committed in storage engine.", + GLOBAL_VAR(rpl_semi_sync_master_wait_point), CMD_LINE(REQUIRED_ARG), + repl_semisync_wait_point, DEFAULT(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG,ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_master_wait_point)); + +static bool fix_rpl_semi_sync_slave_enabled(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_slave_enabled(rpl_semi_sync_slave_enabled != 0); + return false; +} + +static bool fix_rpl_semi_sync_slave_trace_level(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_trace_level(rpl_semi_sync_slave_trace_level); + return false; +} + +static bool fix_rpl_semi_sync_slave_delay_master(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave.set_delay_master(rpl_semi_sync_slave_delay_master); + return false; +} + +static bool fix_rpl_semi_sync_slave_kill_conn_timeout(sys_var *self, THD *thd, + enum_var_type type) +{ + repl_semisync_slave. + set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout); + return false; +} + +static Sys_var_mybool Sys_semisync_slave_enabled( + "rpl_semi_sync_slave_enabled", + "Enable semi-synchronous replication slave (disabled by default).", + GLOBAL_VAR(rpl_semi_sync_slave_enabled), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_enabled)); + +static Sys_var_ulong Sys_semisync_slave_trace_level( + "rpl_semi_sync_slave_trace_level", + "The tracing level for semi-sync replication.", + GLOBAL_VAR(rpl_semi_sync_slave_trace_level), + CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0,~0L),DEFAULT(32),BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_trace_level)); + +static Sys_var_mybool Sys_semisync_slave_delay_master( + "rpl_semi_sync_slave_delay_master", + "Only write master info file when ack is needed.", + GLOBAL_VAR(rpl_semi_sync_slave_delay_master), + CMD_LINE(OPT_ARG), DEFAULT(FALSE), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_delay_master)); + +static Sys_var_uint Sys_semisync_slave_kill_conn_timeout( + "rpl_semi_sync_slave_kill_conn_timeout", + "Timeout for the mysql connection used to kill the slave io_thread's " + "connection on master. This timeout comes into play when stop slave " + "is executed.", + GLOBAL_VAR(rpl_semi_sync_slave_kill_conn_timeout), + CMD_LINE(OPT_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_rpl_semi_sync_slave_kill_conn_timeout)); +#endif /* HAVE_REPLICATION */ static Sys_var_ulong Sys_slow_launch_time( "slow_launch_time", @@ -3016,7 +3266,8 @@ export sql_mode_t expand_sql_mode(sql_mode_t sql_mode) sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | - MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER); + MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER | + MODE_SIMULTANEOUS_ASSIGNMENT); if (sql_mode & MODE_MSSQL) sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | @@ -3085,7 +3336,7 @@ static const char *sql_mode_names[]= "STRICT_ALL_TABLES", "NO_ZERO_IN_DATE", "NO_ZERO_DATE", "ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL", "NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION", - "PAD_CHAR_TO_FULL_LENGTH", "EMPTY_STRING_IS_NULL", + "PAD_CHAR_TO_FULL_LENGTH", "EMPTY_STRING_IS_NULL", "SIMULTANEOUS_ASSIGNMENT", 0 }; @@ -3532,6 +3783,27 @@ static Sys_var_ulong Sys_net_wait_timeout( VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1)); +static Sys_var_uint Sys_idle_transaction_timeout( + "idle_transaction_timeout", + "The number of seconds the server waits for idle transaction", + SESSION_VAR(idle_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_readonly_transaction_timeout( + "idle_readonly_transaction_timeout", + "The number of seconds the server waits for read-only idle transaction", + SESSION_VAR(idle_readonly_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_write_transaction_timeout( + "idle_write_transaction_timeout", + "The number of seconds the server waits for write idle transaction", + SESSION_VAR(idle_write_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + static Sys_var_plugin Sys_default_storage_engine( "default_storage_engine", "The default storage engine for new tables", SESSION_VAR(table_plugin), NO_CMD_LINE, @@ -3556,6 +3828,45 @@ static Sys_var_plugin Sys_enforce_storage_engine( NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super)); + +#ifdef HAVE_REPLICATION +/* + Check + 1. Value for gtid_pos_auto_engines is not NULL. + 2. No slave SQL thread is running. +*/ +static bool +check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var) +{ + bool running; + bool err= false; + + DBUG_ASSERT(var->type == OPT_GLOBAL); + if (var->value && var->value->is_null()) + err= true; + else + { + running= give_error_if_slave_running(false); + if (running) + err= true; + } + return err; +} + + +static Sys_var_pluginlist Sys_gtid_pos_auto_engines( + "gtid_pos_auto_engines", + "List of engines for which to automatically create a " + "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine " + "is replicated. This can be used to avoid introducing cross-engine " + "transactions, if engines are used different from that used by table " + "mysql.gtid_slave_pos", + GLOBAL_VAR(opt_gtid_pos_auto_plugins), NO_CMD_LINE, + DEFAULT(>id_pos_auto_engines), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_gtid_pos_auto_engines)); +#endif + + #if defined(ENABLED_DEBUG_SYNC) /* Variable can be set for the session only. @@ -4111,6 +4422,17 @@ static Sys_var_charptr Sys_license( READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET, DEFAULT(STRINGIFY_ARG(LICENSE))); +char *my_proxy_protocol_networks; +static Sys_var_charptr Sys_proxy_protocol_networks( + "proxy_protocol_networks", "Enable proxy protocol for these source " + "networks. The syntax is a comma separated list of IPv4 and IPv6 " + "networks. If the network doesn't contain mask, it is considered to be " + "a single host. \"*\" represents all networks and must the only " + "directive on the line. String \"localhost\" represents non-TCP " + "local connections (Unix domain socket, Windows named pipe or shared memory).", + READ_ONLY GLOBAL_VAR(my_proxy_protocol_networks), + CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT("")); + static bool check_log_path(sys_var *self, THD *thd, set_var *var) { if (!var->value) @@ -4347,6 +4669,7 @@ static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type) return res; } + static bool check_not_empty_set(sys_var *self, THD *thd, set_var *var) { return var->save_result.ulonglong_value == 0; @@ -4720,6 +5043,14 @@ static Sys_var_ulonglong Sys_read_binlog_speed_limit( GLOBAL_VAR(opt_read_binlog_speed_limit), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1)); +static Sys_var_charptr Sys_slave_transaction_retry_errors( + "slave_transaction_retry_errors", "Tells the slave thread to retry " + "transaction for replication when a query event returns an error from " + "the provided list. Deadlock and elapsed lock wait timeout errors are " + "automatically added to this list", + READ_ONLY GLOBAL_VAR(opt_slave_transaction_retry_errors), CMD_LINE(REQUIRED_ARG), + IN_SYSTEM_CHARSET, DEFAULT(0)); + static Sys_var_ulonglong Sys_relay_log_space_limit( "relay_log_space_limit", "Maximum space to use for all relay logs", READ_ONLY GLOBAL_VAR(relay_log_space_limit), CMD_LINE(REQUIRED_ARG), @@ -4754,10 +5085,19 @@ static Sys_var_uint Sys_sync_masterinfo_period( #ifdef HAVE_REPLICATION static Sys_var_ulong Sys_slave_trans_retries( "slave_transaction_retries", "Number of times the slave SQL " - "thread will retry a transaction in case it failed with a deadlock " - "or elapsed lock wait timeout, before giving up and stopping", + "thread will retry a transaction in case it failed with a deadlock, " + "elapsed lock wait timeout or listed in " + "slave_transaction_retry_errors, before giving up and stopping", GLOBAL_VAR(slave_trans_retries), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX), DEFAULT(10), BLOCK_SIZE(1)); + +static Sys_var_ulong Sys_slave_trans_retry_interval( + "slave_transaction_retry_interval", "Interval of the slave SQL " + "thread will retry a transaction in case it failed with a deadlock " + "or elapsed lock wait timeout or listed in " + "slave_transaction_retry_errors", + GLOBAL_VAR(slave_trans_retry_interval), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 3600), DEFAULT(0), BLOCK_SIZE(1)); #endif static bool check_locale(sys_var *self, THD *thd, set_var *var) @@ -5159,6 +5499,38 @@ static Sys_var_ulong Sys_host_cache_size( NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(fix_host_cache_size)); +vio_keepalive_opts opt_vio_keepalive; + +static Sys_var_int Sys_keepalive_time( + "tcp_keepalive_time", + "Timeout, in milliseconds, with no activity until the first TCP keep-alive packet is sent." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.idle), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + +static Sys_var_int Sys_keepalive_interval( + "tcp_keepalive_interval", + "The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.interval), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + +static Sys_var_int Sys_keepalive_probes( + "tcp_keepalive_probes", + "The number of unacknowledged probes to send before considering the connection dead and notifying the application layer." + "If set to 0, system dependent default is used.", + AUTO_SET GLOBAL_VAR(opt_vio_keepalive.probes), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, INT_MAX32/1000), + DEFAULT(0), + BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL)); + static Sys_var_charptr Sys_ignore_db_dirs( "ignore_db_dirs", "Specifies a directory to add to the ignore list when collecting " @@ -5182,7 +5554,9 @@ static Sys_var_enum Sys_plugin_maturity( "The lowest desirable plugin maturity. " "Plugins less mature than that will not be installed or loaded", READ_ONLY GLOBAL_VAR(plugin_maturity), CMD_LINE(REQUIRED_ARG), - plugin_maturity_names, DEFAULT(MariaDB_PLUGIN_MATURITY_UNKNOWN)); + plugin_maturity_names, + DEFAULT(SERVER_MATURITY_LEVEL > 0 ? + SERVER_MATURITY_LEVEL - 1 : SERVER_MATURITY_LEVEL)); static Sys_var_ulong Sys_deadlock_search_depth_short( "deadlock_search_depth_short", @@ -5238,15 +5612,42 @@ static Sys_var_keycache Sys_key_cache_segments( ON_UPDATE(repartition_keycache)); static const char *log_slow_filter_names[]= -{ "admin", "filesort", "filesort_on_disk", "full_join", "full_scan", - "query_cache", "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0 +{ + "admin", "filesort", "filesort_on_disk", "filesort_priority_queue", + "full_join", "full_scan", "not_using_index", "query_cache", + "query_cache_miss", "tmp_table", "tmp_table_on_disk", 0 }; + + static Sys_var_set Sys_log_slow_filter( "log_slow_filter", - "Log only certain types of queries", + "Log only certain types of queries to the slow log. If variable empty alll kind of queries are logged. All types are bound by slow_query_time, except 'not_using_index' which is always logged if enabled", SESSION_VAR(log_slow_filter), CMD_LINE(REQUIRED_ARG), log_slow_filter_names, - DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1))); + /* by default we log all queries except 'not_using_index' */ + DEFAULT(my_set_bits(array_elements(log_slow_filter_names)-1) & + ~QPLAN_NOT_USING_INDEX)); + +static const char *log_slow_disabled_statements_names[]= +{ "admin", "call", "slave", "sp", 0 }; + +static const char *log_disabled_statements_names[]= +{ "slave", "sp", 0 }; + +static Sys_var_set Sys_log_slow_disabled_statements( + "log_slow_disabled_statements", + "Don't log certain types of statements to slow log", + SESSION_VAR(log_slow_disabled_statements), CMD_LINE(REQUIRED_ARG), + log_slow_disabled_statements_names, + DEFAULT(LOG_SLOW_DISABLE_SP)); + +static Sys_var_set Sys_log_disabled_statements( + "log_disabled_statements", + "Don't log certain types of statements to general log", + SESSION_VAR(log_disabled_statements), CMD_LINE(REQUIRED_ARG), + log_disabled_statements_names, + DEFAULT(LOG_DISABLE_SP), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super)); static const char *default_regex_flags_names[]= { @@ -5551,12 +5952,10 @@ static Sys_var_ulonglong Sys_max_thread_mem( static Sys_var_sesvartrack Sys_track_session_sys_vars( "session_track_system_variables", - "Track changes in registered system variables. " - "For compatibility with MySQL defaults this variable should be set to " - "\"autocommit, character_set_client, character_set_connection, " - "character_set_results, time_zone\"", + "Track changes in registered system variables. ", CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, - DEFAULT(""), + DEFAULT("autocommit,character_set_client,character_set_connection," + "character_set_results,time_zone"), NO_MUTEX_GUARD); static bool update_session_track_schema(sys_var *self, THD *thd, @@ -5621,3 +6020,12 @@ static Sys_var_mybool Sys_session_track_state_change( ON_UPDATE(update_session_track_state_change)); #endif //EMBEDDED_LIBRARY + +#ifndef DBUG_OFF +static Sys_var_uint Sys_in_subquery_conversion_threshold( + "in_predicate_conversion_threshold", + "The minimum number of scalar elements in the value list of " + "IN predicate that triggers its conversion to IN subquery", + SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(OPT_ARG), + VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD), BLOCK_SIZE(1)); +#endif diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index ba58315b449..e012cd48ecb 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -77,6 +77,11 @@ #define GET_HA_ROWS GET_ULONG #endif +// Disable warning caused by SESSION_VAR() macro +#ifdef __clang__ +#pragma clang diagnostic ignored "-Winvalid-offsetof" +#endif + /* special assert for sysvars. Tells the name of the variable, and fails even in non-debug builds. @@ -88,7 +93,7 @@ while(!(X)) \ { \ fprintf(stderr, "Sysvar '%s' failed '%s'\n", name_arg, #X); \ - DBUG_ABORT(); \ + DBUG_ASSERT(0); \ exit(255); \ } @@ -612,7 +617,7 @@ public: /* parse and feel list with default values */ if (thd) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS bool res= #endif sysvartrack_validate_value(thd, @@ -1535,6 +1540,116 @@ public: { return valptr(thd, get_default(thd)); } }; +/** + Class for variables that containg a list of plugins. + Currently this is used only for @@gtid_pos_auto_create_engines + + Backing store: plugin_ref + + @note + Currently this is only used for storage engine type plugins, and thus only + storage engine type plugin is implemented. It could be extended to other + plugin types later if needed, similar to Sys_var_plugin. + + These variables don't support command-line equivalents, any such + command-line options should be added manually to my_long_options in mysqld.cc + + Note on lifetimes of resources allocated: We allocate a zero-terminated array + of plugin_ref*, and lock the contained plugins. The list in the global + variable must be freed (with free_engine_list()). However, the way Sys_var + works, there is no place to explicitly free other lists, like the one + returned from get_default(). + + Therefore, the code needs to work with temporary lists, which are + registered in the THD to be automatically freed (and plugins similarly + automatically unlocked). This is why do_check() allocates a temporary + list, from which do_update() then makes a permanent copy. +*/ +class Sys_var_pluginlist: public sys_var +{ + int plugin_type; +public: + Sys_var_pluginlist(const char *name_arg, + const char *comment, int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt, + char **def_val, PolyLock *lock=0, + enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG, + on_check_function on_check_func=0, + on_update_function on_update_func=0, + const char *substitute=0) + : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id, + getopt.arg_type, SHOW_CHAR, (intptr)def_val, + lock, binlog_status_arg, on_check_func, on_update_func, + substitute) + { + option.var_type|= GET_STR; + SYSVAR_ASSERT(size == sizeof(plugin_ref)); + SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE + } + bool do_check(THD *thd, set_var *var) + { + char buff[STRING_BUFFER_USUAL_SIZE]; + String str(buff,sizeof(buff), system_charset_info), *res; + plugin_ref *plugins; + + if (!(res=var->value->val_str(&str))) + plugins= resolve_engine_list(thd, "", 0, true, true); + else + plugins= resolve_engine_list(thd, res->ptr(), res->length(), true, true); + if (!plugins) + return true; + var->save_result.plugins= plugins; + return false; + } + void do_update(plugin_ref **valptr, plugin_ref* newval) + { + plugin_ref *oldval= *valptr; + *valptr= copy_engine_list(newval); + free_engine_list(oldval); + } + bool session_update(THD *thd, set_var *var) + { + do_update((plugin_ref**)session_var_ptr(thd), + var->save_result.plugins); + return false; + } + bool global_update(THD *thd, set_var *var) + { + do_update((plugin_ref**)global_var_ptr(), + var->save_result.plugins); + return false; + } + void session_save_default(THD *thd, set_var *var) + { + plugin_ref* plugins= global_var(plugin_ref *); + var->save_result.plugins= plugins ? temp_copy_engine_list(thd, plugins) : 0; + } + plugin_ref *get_default(THD *thd) + { + char *default_value= *reinterpret_cast<char**>(option.def_value); + if (!default_value) + return 0; + return resolve_engine_list(thd, default_value, strlen(default_value), + false, true); + } + + void global_save_default(THD *thd, set_var *var) + { + var->save_result.plugins= get_default(thd); + } + + uchar *valptr(THD *thd, plugin_ref *plugins) + { + return (uchar*)pretty_print_engine_list(thd, plugins); + } + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) + { return valptr(thd, session_var(thd, plugin_ref*)); } + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) + { return valptr(thd, global_var(plugin_ref*)); } + uchar *default_value_ptr(THD *thd) + { return valptr(thd, get_default(thd)); } +}; + #if defined(ENABLED_DEBUG_SYNC) #include "debug_sync.h" @@ -1654,12 +1769,12 @@ public: binlog_status_arg, on_check_func, on_update_func, substitute) { - option.var_type|= GET_BOOL; + option.var_type|= GET_BIT; reverse_semantics= my_count_bits(bitmask_arg) > 1; bitmask= reverse_semantics ? ~bitmask_arg : bitmask_arg; + option.block_size= reverse_semantics ? -(long) bitmask : (long)bitmask; set(global_var_ptr(), def_val); SYSVAR_ASSERT(def_val < 2); - SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE SYSVAR_ASSERT(size == sizeof(ulonglong)); } bool session_update(THD *thd, set_var *var) @@ -2487,3 +2602,96 @@ public: bool global_update(THD *thd, set_var *var); uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); }; + + +class Sys_var_vers_asof: public Sys_var_enum +{ +public: + static const char *asof_keywords[]; + +public: + Sys_var_vers_asof(const char *name_arg, + const char *comment, int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt, const char *values[], + uint def_val) + : Sys_var_enum(name_arg, comment, flag_args, off, size, + getopt, values, def_val) + { + // setval() accepts string rather enum + option.var_type= GET_STR; + } + virtual bool do_check(THD *thd, set_var *var) + { + if (!Sys_var_enum::do_check(thd, var)) + return false; + MYSQL_TIME ltime; + bool res= var->value->get_date(<ime, 0); + if (!res) + { + var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF; + } + return res; + } + +private: + bool update(set_var *var, vers_asof_timestamp_t &out) + { + bool res= false; + out.type= static_cast<enum_var_type>(var->save_result.ulonglong_value); + if (out.type == SYSTEM_TIME_AS_OF) + { + if (var->value) + { + res= var->value->get_date(&out.ltime, 0); + } + else // set DEFAULT from global var + { + out= global_var(vers_asof_timestamp_t); + res= false; + } + } + return res; + } + +public: + virtual bool global_update(THD *thd, set_var *var) + { + return update(var, global_var(vers_asof_timestamp_t)); + } + virtual bool session_update(THD *thd, set_var *var) + { + return update(var, session_var(thd, vers_asof_timestamp_t)); + } + +private: + uchar *value_ptr(THD *thd, vers_asof_timestamp_t &val) + { + switch (val.type) + { + case SYSTEM_TIME_UNSPECIFIED: + case SYSTEM_TIME_ALL: + return (uchar*) thd->strdup(asof_keywords[val.type]); + case SYSTEM_TIME_AS_OF: + { + uchar *buf= (uchar*) thd->alloc(MAX_DATE_STRING_REP_LENGTH); + if (buf &&!my_datetime_to_str(&val.ltime, (char*) buf, 6)) + { + // TODO: figure out variable name + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "system_versioning_asof_timestamp", "NULL (wrong datetime)"); + return (uchar*) thd->strdup("Error: wrong datetime"); + } + return buf; + } + default: + break; + } + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "system_versioning_asof_timestamp", "NULL (wrong range type)"); + return (uchar*) thd->strdup("Error: wrong range type"); + } + +public: + virtual uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) + { return value_ptr(thd, session_var(thd, vers_asof_timestamp_t)); } + virtual uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) + { return value_ptr(thd, global_var(vers_asof_timestamp_t)); } +}; diff --git a/sql/sys_vars_shared.h b/sql/sys_vars_shared.h index ff050f63064..48154c95a72 100644 --- a/sql/sys_vars_shared.h +++ b/sql/sys_vars_shared.h @@ -32,7 +32,7 @@ extern bool throw_bounds_warning(THD *thd, const char *name, bool fixed, bool is_unsigned, longlong v); extern bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v); -extern sys_var *intern_find_sys_var(const char *str, uint length); +extern sys_var *intern_find_sys_var(const char *str, size_t length); extern sys_var_chain all_sys_vars; diff --git a/sql/table.cc b/sql/table.cc index 7502a13d5ce..a1129d48b52 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -17,7 +17,7 @@ /* Some general useful functions */ -#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "table.h" #include "key.h" // find_ref_key @@ -43,6 +43,7 @@ #include "rpl_filter.h" #include "sql_cte.h" #include "ha_sequence.h" +#include "sql_show.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -67,6 +68,11 @@ LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")}; /* SLOW_LOG name */ LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")}; +LEX_CSTRING TRANSACTION_REG_NAME= {STRING_WITH_LEN("transaction_registry")}; +LEX_CSTRING MYSQL_USER_NAME= {STRING_WITH_LEN("user")}; +LEX_CSTRING MYSQL_DB_NAME= {STRING_WITH_LEN("db")}; +LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")}; + /* Keyword added as a prefix when parsing the defining expression for a virtual column read from the column definition saved in the frm file @@ -81,7 +87,7 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, char **names); static uint find_field(Field **fields, uchar *record, uint start, uint length); -inline bool is_system_table_name(const char *name, uint length); +inline bool is_system_table_name(const char *name, size_t length); /************************************************************************** Object_creation_ctx implementation. @@ -160,8 +166,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_NO_CREATION_CTX, ER_THD(thd, ER_VIEW_NO_CREATION_CTX), - (const char *) view->db, - (const char *) view->table_name); + view->db.str, + view->table_name.str); ctx->m_client_cs= system_charset_info; ctx->m_connection_cl= system_charset_info; @@ -186,16 +192,16 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, { sql_print_warning("View '%s'.'%s': there is unknown charset/collation " "names (client: '%s'; connection: '%s').", - (const char *) view->db, - (const char *) view->table_name, + view->db.str, + view->table_name.str, (const char *) view->view_client_cs_name.str, (const char *) view->view_connection_cl_name.str); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_INVALID_CREATION_CTX, ER_THD(thd, ER_VIEW_INVALID_CREATION_CTX), - (const char *) view->db, - (const char *) view->table_name); + view->db.str, + view->table_name.str); } return ctx; @@ -217,64 +223,50 @@ static uchar *get_field_name(Field **buff, size_t *length, Returns pointer to '.frm' extension of the file name. SYNOPSIS - fn_rext() + fn_frm_ext() name file name DESCRIPTION Checks file name part starting with the rightmost '.' character, and returns it if it is equal to '.frm'. - TODO - It is a good idea to get rid of this function modifying the code - to garantee that the functions presently calling fn_rext() always - get arguments in the same format: either with '.frm' or without '.frm'. - RETURN VALUES - Pointer to the '.frm' extension. If there is no extension, - or extension is not '.frm', pointer at the end of file name. + Pointer to the '.frm' extension or NULL if not a .frm file */ -char *fn_rext(char *name) +const char *fn_frm_ext(const char *name) { - char *res= strrchr(name, '.'); + const char *res= strrchr(name, '.'); if (res && !strcmp(res, reg_ext)) return res; - return name + strlen(name); + return 0; } + TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, const LEX_CSTRING *name) { DBUG_ASSERT(db != NULL); DBUG_ASSERT(name != NULL); - if (is_infoschema_db(db->str, db->length)) + if (is_infoschema_db(db)) return TABLE_CATEGORY_INFORMATION; - if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) && - (my_strcasecmp(system_charset_info, - PERFORMANCE_SCHEMA_DB_NAME.str, - db->str) == 0)) + if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db) == 0) return TABLE_CATEGORY_PERFORMANCE; - if ((db->length == MYSQL_SCHEMA_NAME.length) && - (my_strcasecmp(system_charset_info, - MYSQL_SCHEMA_NAME.str, - db->str) == 0)) + if (lex_string_eq(&MYSQL_SCHEMA_NAME, db) == 0) { if (is_system_table_name(name->str, name->length)) return TABLE_CATEGORY_SYSTEM; - if ((name->length == GENERAL_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - GENERAL_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&GENERAL_LOG_NAME, name) == 0) + return TABLE_CATEGORY_LOG; + + if (lex_string_eq(&SLOW_LOG_NAME, name) == 0) return TABLE_CATEGORY_LOG; - if ((name->length == SLOW_LOG_NAME.length) && - (my_strcasecmp(system_charset_info, - SLOW_LOG_NAME.str, - name->str) == 0)) + if (lex_string_eq(&TRANSACTION_REG_NAME, name) == 0) return TABLE_CATEGORY_LOG; } @@ -328,8 +320,13 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, share->normalized_path.length= path_length; share->table_category= get_table_category(& share->db, & share->table_name); share->open_errno= ENOENT; - /* The following will be fixed in open_table_from_share */ - share->cached_row_logging_check= 1; + /* The following will be updated in open_table_from_share */ + 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, + (const uchar*) "mysql", 6) == 0) + share->not_usable_by_query_cache= 1; init_sql_alloc(&share->stats_cb.mem_root, "share_stats", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); @@ -403,8 +400,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, share->normalized_path.str= (char*) path; share->path.length= share->normalized_path.length= strlen(path); share->frm_version= FRM_VER_CURRENT; - - share->cached_row_logging_check= 0; // No row logging + share->not_usable_by_query_cache= 1; + share->can_do_row_logging= 0; // No row logging /* table_map_id is also used for MERGE tables to suppress repeated @@ -428,6 +425,9 @@ void TABLE_SHARE::destroy() DBUG_ENTER("TABLE_SHARE::destroy"); DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str)); + if (versioned) + vers_destroy(); + if (ha_share) { delete ha_share; @@ -509,7 +509,7 @@ void free_table_share(TABLE_SHARE *share) and should not contain user tables. */ -inline bool is_system_table_name(const char *name, uint length) +inline bool is_system_table_name(const char *name, size_t length) { CHARSET_INFO *ci= system_charset_info; @@ -1180,7 +1180,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, const uchar *frm_image_end = frm_image + frm_length; uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos); const uchar *disk_buff, *strpos; - ulong pos, record_offset; + ulong pos, record_offset; ulong rec_buff_length; handler *handler_file= 0; KEY *keyinfo; @@ -1190,15 +1190,23 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum legacy_db_type legacy_db_type; my_bitmap_map *bitmaps; bool null_bits_are_used; - uint vcol_screen_length, UNINIT_VAR(options_len); + uint vcol_screen_length; + size_t UNINIT_VAR(options_len); uchar *vcol_screen_pos; const uchar *options= 0; - uint UNINIT_VAR(gis_options_len); + size_t UNINIT_VAR(gis_options_len); const uchar *gis_options= 0; KEY first_keyinfo; uint len; uint ext_key_parts= 0; plugin_ref se_plugin= 0; + const uchar *system_period= 0; + bool vtmd_used= false; + share->vtmd= false; + bool vers_can_native= false; + const uchar *extra2_field_flags= 0; + size_t extra2_field_flags_length= 0; + MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); @@ -1234,7 +1242,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (*extra2 != '/') // old frm had '/' there { const uchar *e2end= extra2 + len; - while (extra2 + 3 < e2end) + while (extra2 + 3 <= e2end) { uchar type= *extra2++; size_t length= *extra2++; @@ -1292,6 +1300,28 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif /*HAVE_SPATIAL*/ break; + case EXTRA2_PERIOD_FOR_SYSTEM_TIME: + if (system_period || length != 2 * sizeof(uint16)) + goto err; + system_period = extra2; + break; + case EXTRA2_VTMD: + if (vtmd_used) + goto err; + share->vtmd= *extra2; + if (share->vtmd) + { + share->table_category= TABLE_CATEGORY_LOG; + share->no_replicate= true; + } + vtmd_used= true; + break; + case EXTRA2_FIELD_FLAGS: + if (extra2_field_flags) + goto err; + extra2_field_flags= extra2; + extra2_field_flags_length= length; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1609,8 +1639,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, memcpy(record, frm_image + record_offset, share->reclength); disk_buff= frm_image + pos + FRM_FORMINFO_SIZE; - share->fields= uint2korr(forminfo+258); + if (extra2_field_flags && extra2_field_flags_length != share->fields) + goto err; pos= uint2korr(forminfo+260); /* Length of all screens */ n_length= uint2korr(forminfo+268); interval_count= uint2korr(forminfo+270); @@ -1621,6 +1652,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_screen_length= uint2korr(forminfo+286); share->virtual_fields= share->default_expressions= share->field_check_constraints= share->default_fields= 0; + share->visible_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1681,7 +1713,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, for (count= 0; count < interval->count; count++) { char *val= (char*) interval->type_names[count]; - interval->type_lengths[count]= strlen(val); + interval->type_lengths[count]= (uint)strlen(val); } interval->type_lengths[count]= 0; } @@ -1742,6 +1774,28 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, strpos, vcol_screen_pos); } + /* Set system versioning information. */ + if (system_period == NULL) + { + versioned= VERS_UNDEFINED; + row_start_field= 0; + row_end_field= 0; + } + else + { + DBUG_PRINT("info", ("Setting system versioning informations")); + uint16 row_start= uint2korr(system_period); + uint16 row_end= uint2korr(system_period + sizeof(uint16)); + if (row_start >= share->fields || row_end >= share->fields) + goto err; + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + versioned= VERS_TIMESTAMP; + vers_can_native= plugin_hton(se_plugin)->flags & HTON_NATIVE_SYS_VERSIONING; + vers_init(); + row_start_field= row_start; + row_end_field= row_end; + } // if (system_period == NULL) + for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; @@ -1756,6 +1810,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint gis_length, gis_decimals, srid= 0; Field::utype unireg_check; const Type_handler *handler; + uint32 flags= 0; if (new_frm_ver >= 3) { @@ -1798,7 +1853,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, char tmp[10]; if (!csname || csname[0] =='?') { - my_snprintf(tmp, sizeof(tmp), "#%d", cs_new); + my_snprintf(tmp, sizeof(tmp), "#%u", cs_new); csname= tmp; } my_printf_error(ER_UNKNOWN_COLLATION, @@ -1965,6 +2020,36 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos); } + if (versioned) + { + if (i == row_start_field) + flags|= VERS_SYS_START_FLAG; + else if (i == row_end_field) + flags|= VERS_SYS_END_FLAG; + + if (flags & VERS_SYSTEM_FIELD) + { + switch (field_type) + { + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_DATETIME2: + break; + case MYSQL_TYPE_LONGLONG: + if (vers_can_native) + { + versioned= VERS_TRX_ID; + break; + } + /* Fallthrough */ + default: + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), fieldnames.type_names[i], + versioned == VERS_TIMESTAMP ? "TIMESTAMP(6)" : "BIGINT(20) UNSIGNED", + table_name.str); + goto err; + } + } + } + /* Convert pre-10.2.2 timestamps to use Field::default_value */ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type); name.str= fieldnames.type_names[i]; @@ -1976,7 +2061,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, null_pos, null_bit_pos, pack_flag, handler, charset, geom_type, srid, unireg_check, (interval_nr ? share->intervals+interval_nr-1 : NULL), - &name); + &name, flags); if (!reg_field) // Not supported field type goto err; @@ -1992,6 +2077,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, reg_field->field_index= i; reg_field->comment=comment; reg_field->vcol_info= vcol_info; + reg_field->flags|= flags; + if (extra2_field_flags) + { + uchar flags= *extra2_field_flags++; + if (flags & VERS_OPTIMIZED_UPDATE) + reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG; + + reg_field->invisible= f_visibility(flags); + } + if (reg_field->invisible == INVISIBLE_USER) + status_var_increment(thd->status_var.feature_invisible_columns); + if (!reg_field->invisible) + share->visible_fields++; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { null_bits_are_used= 1; @@ -2127,18 +2225,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, for (uint key=0 ; key < keys ; key++,keyinfo++) { uint usable_parts= 0; - keyinfo->name=(char*) share->keynames.type_names[key]; - keyinfo->name_length= strlen(keyinfo->name); + keyinfo->name.str= share->keynames.type_names[key]; + keyinfo->name.length= strlen(keyinfo->name.str); keyinfo->cache_name= (uchar*) alloc_root(&share->mem_root, share->table_cache_key.length+ - keyinfo->name_length + 1); + keyinfo->name.length + 1); if (keyinfo->cache_name) // If not out of memory { uchar *pos= keyinfo->cache_name; memcpy(pos, share->table_cache_key.str, share->table_cache_key.length); - memcpy(pos + share->table_cache_key.length, keyinfo->name, - keyinfo->name_length+1); + memcpy(pos + share->table_cache_key.length, keyinfo->name.str, + keyinfo->name.length+1); } if (ext_key_parts > share->key_parts && key) @@ -2241,6 +2339,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); + if (field->invisible > INVISIBLE_USER && !field->vers_sys_field()) + keyinfo->flags |= HA_INVISIBLE_KEY; if (field->null_ptr) { key_part->null_offset=(uint) ((uchar*) field->null_ptr - @@ -2563,19 +2663,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, bitmap_clear_all(share->check_set); } - delete handler_file; #ifndef DBUG_OFF if (use_hash) (void) my_hash_check(&share->name_hash); #endif share->db_plugin= se_plugin; + delete handler_file; + share->error= OPEN_FRM_OK; thd->status_var.opened_shares++; thd->mem_root= old_root; DBUG_RETURN(0); - err: +err: + share->db_plugin= NULL; share->error= OPEN_FRM_CORRUPTED; share->open_errno= my_errno; delete handler_file; @@ -2646,7 +2748,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, uint unused2; handlerton *hton= plugin_hton(db_plugin); LEX_CUSTRING frm= {0,0}; - LEX_STRING db_backup= { thd->db, thd->db_length }; + LEX_CSTRING db_backup= thd->db; DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string"); /* @@ -2673,7 +2775,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, else thd->set_n_backup_active_arena(arena, &backup); - thd->reset_db((char*) db.str, db.length); + thd->reset_db(&db); lex_start(thd); if ((error= parse_sql(thd, & parser_state, NULL) || @@ -2686,7 +2788,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, thd->lex->create_info.tabledef_version= tabledef_version; promote_first_timestamp_column(&thd->lex->alter_info.create_list); - file= mysql_create_frm_image(thd, db.str, table_name.str, + file= mysql_create_frm_image(thd, &db, &table_name, &thd->lex->create_info, &thd->lex->alter_info, C_ORDINARY_CREATE, &unused1, &unused2, &frm); error|= file == 0; @@ -2702,7 +2804,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, ret: my_free(const_cast<uchar*>(frm.str)); lex_end(thd->lex); - thd->reset_db(db_backup.str, db_backup.length); + thd->reset_db(&db_backup); thd->lex= old_lex; if (arena) thd->restore_active_arena(arena, &backup); @@ -2757,12 +2859,12 @@ static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol) { DBUG_ENTER("fix_vcol_expr"); - const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns; - thd->mark_used_columns= MARK_COLUMNS_NONE; + const enum enum_column_usage saved_column_usage= thd->column_usage; + thd->column_usage= COLUMNS_WRITE; int error= vcol->expr->fix_fields(thd, &vcol->expr); - thd->mark_used_columns= save_mark_used_columns; + thd->column_usage= saved_column_usage; if (unlikely(error)) { @@ -2960,6 +3062,14 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (error) goto end; + if (lex.current_select->table_list.first[0].next_global) + { + /* We are using NEXT VALUE FOR sequence. Remember table name for open */ + TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global; + sequence->next_global= table->internal_tables; + table->internal_tables= sequence; + } + vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type()); vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; vcol_storage.vcol_info->name= vcol->name; @@ -3000,6 +3110,7 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) prgflag READ_ALL etc.. ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. outparam result table + partitions_to_open open only these partitions. RETURN VALUES 0 ok @@ -3012,9 +3123,9 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol) */ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, - const char *alias, uint db_stat, uint prgflag, + const LEX_CSTRING *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, - bool is_create_table) + bool is_create_table, List<String> *partitions_to_open) { enum open_frm_error error; uint records, i, bitmap_size, bitmap_count; @@ -3045,7 +3156,7 @@ 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, strlen(alias), table_alias_charset)) + if (outparam->alias.copy(alias->str, alias->length, table_alias_charset)) goto err; outparam->quick_keys.init(); outparam->covering_keys.init(); @@ -3084,26 +3195,31 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, records=0; if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN)) records=1; - if (prgflag & (READ_ALL+EXTRA_RECORD)) + if (prgflag & (READ_ALL + EXTRA_RECORD)) + { records++; - - if (!(record= (uchar*) alloc_root(&outparam->mem_root, - share->rec_buff_length * records))) - goto err; /* purecov: inspected */ - MEM_NOACCESS(record, share->rec_buff_length * records); + if (share->versioned) + records++; + } if (records == 0) { /* We are probably in hard repair, and the buffers should not be used */ - outparam->record[0]= outparam->record[1]= share->default_values; + record= share->default_values; } else { - outparam->record[0]= record; - if (records > 1) - outparam->record[1]= record+ share->rec_buff_length; - else - outparam->record[1]= outparam->record[0]; // Safety + if (!(record= (uchar*) alloc_root(&outparam->mem_root, + share->rec_buff_length * records))) + goto err; /* purecov: inspected */ + MEM_NOACCESS(record, share->rec_buff_length * records); + } + + for (i= 0; i < 3;) + { + outparam->record[i]= record; + if (++i < records) + record+= share->rec_buff_length; } MEM_UNDEFINED(outparam->record[0], share->reclength); MEM_UNDEFINED(outparam->record[1], share->reclength); @@ -3130,6 +3246,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } (*field_ptr)= 0; // End marker + outparam->vers_write= share->versioned; + if (share->found_next_number_field) outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; @@ -3221,6 +3339,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } #ifdef WITH_PARTITION_STORAGE_ENGINE + bool work_part_info_used; if (share->partition_info_str_len && outparam->file) { /* @@ -3241,7 +3360,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); thd->stmt_arena= &part_func_arena; bool tmp; - bool work_part_info_used; tmp= mysql_unpack_partition(thd, share->partition_info_str, share->partition_info_str_len, @@ -3355,7 +3473,7 @@ partititon_err: int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str, (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), - ha_open_flags); + ha_open_flags, 0, partitions_to_open); if (ha_err) { share->open_errno= ha_err; @@ -3386,24 +3504,52 @@ partititon_err: outparam->mark_columns_used_by_check_constraints(); - if (share->table_category == TABLE_CATEGORY_LOG) - { - outparam->no_replicate= TRUE; - } - else if (outparam->file) + if (db_stat) { + /* Set some flags in share on first open of the table */ handler::Table_flags flags= outparam->file->ha_table_flags(); - outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE - | HA_BINLOG_ROW_CAPABLE)) - || MY_TEST(flags & HA_HAS_OWN_BINLOGGING); + if (! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE | + HA_BINLOG_ROW_CAPABLE)) || + MY_TEST(flags & HA_HAS_OWN_BINLOGGING)) + share->no_replicate= TRUE; + if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE) + share->not_usable_by_query_cache= TRUE; } - else + + if (share->no_replicate || !binlog_filter->db_ok(share->db.str)) + share->can_do_row_logging= 0; // No row based replication + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (outparam->part_info && + outparam->part_info->part_type == VERSIONING_PARTITION) { - outparam->no_replicate= FALSE; - } + Query_arena *backup_stmt_arena_ptr= thd->stmt_arena; + Query_arena backup_arena; + Query_arena part_func_arena(&outparam->mem_root, + Query_arena::STMT_INITIALIZED); + if (!work_part_info_used) + { + thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); + thd->stmt_arena= &part_func_arena; + } + + bool err= outparam->part_info->vers_setup_stats(thd, is_create_table); - if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str)) - outparam->s->cached_row_logging_check= 0; // No row based replication + if (!work_part_info_used) + { + thd->stmt_arena= backup_stmt_arena_ptr; + thd->restore_active_arena(&part_func_arena, &backup_arena); + } + + if (err) + { + outparam->file->ha_close(); + error= OPEN_FRM_OPEN_ERROR; + error_reported= true; + goto err; + } + } +#endif /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) @@ -3663,7 +3809,7 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length) May fail with some multibyte charsets though. */ -void append_unescaped(String *res, const char *pos, uint length) +void append_unescaped(String *res, const char *pos, size_t length) { const char *end= pos+length; res->append('\''); @@ -3714,7 +3860,7 @@ void append_unescaped(String *res, const char *pos, uint length) void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys, KEY *key_info) { - ulong key_comment_total_bytes= 0; + size_t key_comment_total_bytes= 0; uint i; DBUG_ENTER("prepare_frm_header"); @@ -3724,7 +3870,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, if (create_info->min_rows > UINT_MAX32) create_info->min_rows= UINT_MAX32; - uint key_length, tmp_key_length, tmp, csid; + size_t key_length, tmp_key_length, tmp, csid; bzero((char*) fileinfo, FRM_HEADER_SIZE); /* header */ fileinfo[0]=(uchar) 254; @@ -3950,7 +4096,7 @@ bool ok_for_lower_case_names(const char *name) bool check_db_name(LEX_STRING *org_name) { char *name= org_name->str; - uint name_length= org_name->length; + size_t name_length= org_name->length; bool check_for_path_chars; if ((check_for_path_chars= check_mysql50_prefix(name))) @@ -4472,10 +4618,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) if (thd->lex->need_correct_ident()) alias_name_used= my_strcasecmp(table_alias_charset, s->table_name.str, - tl->alias); + tl->alias.str); /* Fix alias if table name changes. */ - if (strcmp(alias.c_ptr(), tl->alias)) - alias.copy(tl->alias, strlen(tl->alias), alias.charset()); + if (strcmp(alias.c_ptr(), tl->alias.str)) + alias.copy(tl->alias.str, tl->alias.length, alias.charset()); tablenr= thd->current_tablenr++; used_fields= 0; @@ -4627,7 +4773,7 @@ bool TABLE_LIST::create_field_translation(THD *thd) bool res= FALSE; DBUG_ENTER("TABLE_LIST::create_field_translation"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (alias ? alias : "<NULL>"), + (alias.str ? alias.str : "<NULL>"), get_unit())); if (thd->stmt_arena->is_conventional() || @@ -4655,16 +4801,20 @@ bool TABLE_LIST::create_field_translation(THD *thd) */ if (is_view() && get_unit()->prepared && !field_translation_updated) { + field_translation_updated= TRUE; + if (static_cast<uint>(field_translation_end - field_translation) < + select->item_list.elements) + goto allocate; while ((item= it++)) { field_translation[field_count++].item= item; } - field_translation_updated= TRUE; } DBUG_RETURN(FALSE); } +allocate: arena= thd->activate_stmt_arena_if_needed(&backup); /* Create view fields translation table */ @@ -4872,7 +5022,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) DBUG_ENTER("merge_on_conds"); Item *cond= NULL; - DBUG_PRINT("info", ("alias: %s", table->alias)); + DBUG_PRINT("info", ("alias: %s", table->alias.str)); if (table->on_expr) cond= table->on_expr->copy_andor_structure(thd); if (!table->view) @@ -5100,9 +5250,9 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) { TABLE_LIST *main_view= top_table(); const char *name_db= (main_view->view ? main_view->view_db.str : - main_view->db); + main_view->db.str); const char *name_table= (main_view->view ? main_view->view_name.str : - main_view->table_name); + main_view->table_name.str); my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0), name_db, name_table); return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR; @@ -5133,7 +5283,6 @@ int TABLE::verify_constraints(bool ignore_failure) return(VIEW_CHECK_OK); } - /* Find table in underlying tables by mask and check that only this table belong to given mask @@ -5403,7 +5552,7 @@ void TABLE_LIST::register_want_access(ulong want_access) bool TABLE_LIST::prepare_view_security_context(THD *thd) { DBUG_ENTER("TABLE_LIST::prepare_view_security_context"); - DBUG_PRINT("enter", ("table: %s", alias)); + DBUG_PRINT("enter", ("table: %s", alias.str)); DBUG_ASSERT(!prelocking_placeholder && view); if (view_suid) @@ -5411,7 +5560,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd) DBUG_PRINT("info", ("This table is suid view => load contest")); DBUG_ASSERT(view && view_sctx); if (acl_getroot(view_sctx, definer.user.str, definer.host.str, - definer.host.str, thd->db)) + definer.host.str, thd->db.str)) { if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) || (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) @@ -5476,7 +5625,7 @@ Security_context *TABLE_LIST::find_view_security_context(THD *thd) if (upper_view) { DBUG_PRINT("info", ("Securety context of view %s will be used", - upper_view->alias)); + upper_view->alias.str)); sctx= upper_view->view_sctx; DBUG_ASSERT(sctx); } @@ -5525,8 +5674,8 @@ bool TABLE_LIST::prepare_security(THD *thd) } else { - local_db= tbl->db; - local_table_name= tbl->table_name; + local_db= tbl->db.str; + local_table_name= tbl->table_name.str; } fill_effective_table_privileges(thd, &tbl->grant, local_db, local_table_name); @@ -5677,30 +5826,29 @@ Field *Natural_join_column::field() } -const char *Natural_join_column::table_name() +const char *Natural_join_column::safe_table_name() { DBUG_ASSERT(table_ref); - return table_ref->alias; + return table_ref->alias.str ? table_ref->alias.str : ""; } -const char *Natural_join_column::db_name() +const char *Natural_join_column::safe_db_name() { if (view_field) - return table_ref->view_db.str; + return table_ref->view_db.str ? table_ref->view_db.str : ""; /* Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ - DBUG_ASSERT(!strcmp(table_ref->db, - table_ref->table->s->db.str) || + DBUG_ASSERT(!cmp(&table_ref->db, + &table_ref->table->s->db) || (table_ref->schema_table && - is_infoschema_db(table_ref->table->s->db.str, - table_ref->table->s->db.length)) || - table_ref->is_materialized_derived()); - return table_ref->db; + is_infoschema_db(&table_ref->table->s->db)) || + table_ref->is_materialized_derived()); + return table_ref->db.str ? table_ref->db.str : ""; } @@ -5801,7 +5949,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, Name_resolution_context *context= view->view ? &view->view->select_lex.context : &thd->lex->select_lex.context; Item *item= (new (thd->mem_root) - Item_direct_view_ref(thd, context, field_ref, view->alias, + Item_direct_view_ref(thd, context, field_ref, view->alias.str, name, view)); /* Force creation of nullable item for the result tmp table for outer joined @@ -5865,7 +6013,7 @@ void Field_iterator_table_ref::set_field_iterator() table_ref->table->s->fields)))); field_it= &natural_join_it; DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join", - table_ref->alias)); + table_ref->alias.str)); } /* This is a merge view, so use field_translation. */ else if (table_ref->field_translation) @@ -5873,7 +6021,7 @@ void Field_iterator_table_ref::set_field_iterator() DBUG_ASSERT(table_ref->is_merged_derived()); field_it= &view_field_it; DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view", - table_ref->alias)); + table_ref->alias.str)); } /* This is a base table or stored view. */ else @@ -5881,7 +6029,7 @@ void Field_iterator_table_ref::set_field_iterator() DBUG_ASSERT(table_ref->table || table_ref->view); field_it= &table_field_it; DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table", - table_ref->alias)); + table_ref->alias.str)); } field_it->set(table_ref); DBUG_VOID_RETURN; @@ -5923,11 +6071,11 @@ const char *Field_iterator_table_ref::get_table_name() if (table_ref->is_derived()) return table_ref->table->s->table_name.str; else if (table_ref->is_natural_join) - return natural_join_it.column_ref()->table_name(); + return natural_join_it.column_ref()->safe_table_name(); - DBUG_ASSERT(!strcmp(table_ref->table_name, + DBUG_ASSERT(!strcmp(table_ref->table_name.str, table_ref->table->s->table_name.str)); - return table_ref->table_name; + return table_ref->table_name.str; } @@ -5936,19 +6084,18 @@ const char *Field_iterator_table_ref::get_db_name() if (table_ref->view) return table_ref->view_db.str; else if (table_ref->is_natural_join) - return natural_join_it.column_ref()->db_name(); + return natural_join_it.column_ref()->safe_db_name(); /* Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ - DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) || + DBUG_ASSERT(!cmp(&table_ref->db, &table_ref->table->s->db) || (table_ref->schema_table && - is_infoschema_db(table_ref->table->s->db.str, - table_ref->table->s->db.length))); + is_infoschema_db(&table_ref->table->s->db))); - return table_ref->db; + return table_ref->db.str; } @@ -6298,14 +6445,18 @@ void TABLE::mark_columns_needed_for_delete() need_signal= true; } } - if (check_constraints) - { - mark_check_constraint_columns_for_read(); - need_signal= true; - } if (need_signal) file->column_bitmaps_signal(); + + /* + For System Versioning we have to write and read Sys_end. + */ + if (s->versioned) + { + bitmap_set_bit(read_set, s->vers_end_field()->field_index); + bitmap_set_bit(write_set, s->vers_end_field()->field_index); + } } @@ -6346,7 +6497,7 @@ void TABLE::mark_columns_needed_for_update() for (KEY *k= key_info; k < end; k++) { KEY_PART_INFO *kpend= k->key_part + k->ext_key_parts; - bool any_written= false, all_read= true; + int any_written= 0, all_read= 1; for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++) { int idx= kp->fieldnr - 1; @@ -6382,6 +6533,15 @@ void TABLE::mark_columns_needed_for_update() need_signal= true; } } + /* + For System Versioning we have to read all columns since we will store + a copy of previous row with modified Sys_end column back to a table. + */ + if (s->versioned) + { + // We will copy old columns to a new row. + use_all_columns(); + } if (check_constraints) { mark_check_constraint_columns_for_read(); @@ -6662,6 +6822,58 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl) DBUG_RETURN(bitmap_updated); } + +/** + Check if a virtual not stored column field is in read set + + @retval FALSE No virtual not stored column is used + @retval TRUE At least one virtual not stored column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_read() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(read_set, tmp_vfield->field_index) && + !tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + +/** + Check if a stored virtual column field is marked for write + + This can be used to check if any column that is part of a virtual + stored column is changed + + @retval FALSE No stored virtual column is used + @retval TRUE At least one stored virtual column is used +*/ + +bool TABLE::check_virtual_columns_marked_for_write() +{ + if (vfield) + { + Field **vfield_ptr; + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + Field *tmp_vfield= *vfield_ptr; + if (bitmap_is_set(write_set, tmp_vfield->field_index) && + tmp_vfield->vcol_info->stored_in_db) + return TRUE; + } + } + return FALSE; +} + + /* Mark fields used by check constraints. This is done once for the TABLE_SHARE the first time the table is opened. @@ -6719,6 +6931,7 @@ void TABLE::mark_default_fields_for_write(bool is_insert) DBUG_VOID_RETURN; } + void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from) { my_ptrdiff_t diff= to - from; @@ -6925,7 +7138,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, if (unique) keyinfo->flags|= HA_NOSAME; sprintf(buf, "key%i", key); - if (!(keyinfo->name= strdup_root(&mem_root, buf))) + keyinfo->name.length= strlen(buf); + if (!(keyinfo->name.str= strmake_root(&mem_root, buf, keyinfo->name.length))) return TRUE; keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root, sizeof(ulong)*key_parts); @@ -7204,7 +7418,7 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl) (pos= find_type(&tbl->s->keynames, hint->key_name.str, hint->key_name.length, 1)) <= 0) { - my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias); + my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias.str); return 1; } @@ -7304,7 +7518,7 @@ void init_mdl_requests(TABLE_LIST *table_list) { for ( ; table_list ; table_list= table_list->next_global) table_list->mdl_request.init(MDL_key::TABLE, - table_list->db, table_list->table_name, + table_list->db.str, table_list->table_name.str, table_list->lock_type >= TL_WRITE_ALLOW_WRITE ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_TRANSACTION); @@ -7573,6 +7787,62 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) DBUG_RETURN(res); } + +void TABLE::vers_update_fields() +{ + bitmap_set_bit(write_set, vers_start_field()->field_index); + bitmap_set_bit(write_set, vers_end_field()->field_index); + + if (versioned(VERS_TIMESTAMP)) + { + if (!vers_write) + return; + vers_start_field()->set_notnull(); + if (vers_start_field()->store_timestamp(in_use->system_time, + in_use->system_time_sec_part)) + DBUG_ASSERT(0); + } + else + { + vers_start_field()->set_notnull(); + if (!vers_write) + return; + } + + vers_end_field()->set_max(); +} + + +void TABLE::vers_update_end() +{ + vers_end_field()->set_notnull(); + if (vers_end_field()->store_timestamp(in_use->system_time, + in_use->system_time_sec_part)) + DBUG_ASSERT(0); +} + + +bool TABLE_LIST::vers_vtmd_name(String& out) const +{ + static const char *vtmd_suffix= "_vtmd"; + static const size_t vtmd_suffix_len= strlen(vtmd_suffix); + if (table_name.length > NAME_CHAR_LEN - vtmd_suffix_len) + { + my_printf_error(ER_VERS_VTMD_ERROR, "Table name is longer than %d characters", MYF(0), + int(NAME_CHAR_LEN - vtmd_suffix_len)); + return true; + } + out.set(table_name.str, table_name.length, table_alias_charset); + if (out.append(vtmd_suffix, vtmd_suffix_len + 1)) + { + my_message(ER_VERS_VTMD_ERROR, "Failed allocate VTMD name", MYF(0)); + return true; + } + out.length(out.length() - 1); + return false; +} + + /** Reset markers that fields are being updated */ @@ -8269,8 +8539,8 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) if (!(item->used_tables() == tab_map)) continue; Item_func_eq *eq= 0; - Item *left_item_clone= left_item->build_clone(thd, thd->mem_root); - Item *right_item_clone= item->build_clone(thd, thd->mem_root); + Item *left_item_clone= left_item->build_clone(thd); + Item *right_item_clone= item->build_clone(thd); if (left_item_clone && right_item_clone) { left_item_clone->set_item_equal(NULL); @@ -8300,7 +8570,7 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond) return new_cond; } else if (cond->get_extraction_flag() != NO_EXTRACTION_FL) - return cond->build_clone(thd, thd->mem_root); + return cond->build_clone(thd); return 0; } @@ -8318,15 +8588,375 @@ LEX_CSTRING *fk_option_name(enum_fk_option opt) return names + opt; } +void TABLE_SHARE::vers_destroy() +{ + mysql_mutex_destroy(&LOCK_rotation); + mysql_cond_destroy(&COND_rotation); + mysql_rwlock_destroy(&LOCK_stat_serial); + if (stat_trx) + { + for (Vers_min_max_stats** p= stat_trx; *p; ++p) + { + delete *p; + } + } +} + +enum TR_table::enabled TR_table::use_transaction_registry= TR_table::MAYBE; + +TR_table::TR_table(THD* _thd, bool rw) : + thd(_thd), open_tables_backup(NULL) +{ + init_one_table(&MYSQL_SCHEMA_NAME, &TRANSACTION_REG_NAME, + NULL, rw ? TL_WRITE : TL_READ); +} + +bool TR_table::open() +{ + DBUG_ASSERT(!table); + open_tables_backup= new Open_tables_backup; + if (!open_tables_backup) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return true; + } + + All_tmp_tables_list *temporary_tables= thd->temporary_tables; + bool error= !open_log_table(thd, this, open_tables_backup); + thd->temporary_tables= temporary_tables; + + if (use_transaction_registry == MAYBE) + error= check(error); + + use_transaction_registry= error ? NO : YES; + + return error; +} + +TR_table::~TR_table() +{ + if (table) + { + thd->temporary_tables= NULL; + close_log_table(thd, open_tables_backup); + } + delete open_tables_backup; +} + +void TR_table::store(uint field_id, ulonglong val) +{ + table->field[field_id]->store(val, true); + table->field[field_id]->set_notnull(); +} + +void TR_table::store(uint field_id, timeval ts) +{ + table->field[field_id]->store_timestamp(ts.tv_sec, ts.tv_usec); + table->field[field_id]->set_notnull(); +} + +enum_tx_isolation TR_table::iso_level() const +{ + enum_tx_isolation res= (enum_tx_isolation) ((*this)[FLD_ISO_LEVEL]->val_int() - 1); + DBUG_ASSERT(res <= ISO_SERIALIZABLE); + return res; +} + +bool TR_table::update(ulonglong start_id, ulonglong end_id) +{ + if (!table && open()) + return true; + + timeval start_time= {thd->system_time, long(thd->system_time_sec_part)}; + thd->set_start_time(); + timeval end_time= {thd->system_time, long(thd->system_time_sec_part)}; + store(FLD_TRX_ID, start_id); + store(FLD_COMMIT_ID, end_id); + store(FLD_BEGIN_TS, start_time); + store(FLD_COMMIT_TS, end_time); + store_iso_level(thd->tx_isolation); + + int error= table->file->ha_write_row(table->record[0]); + if (error) + table->file->print_error(error, MYF(0)); + return error; +} + +#define newx new (thd->mem_root) +bool TR_table::query(ulonglong trx_id) +{ + if (!table && open()) + return false; + SQL_SELECT_auto select; + READ_RECORD info; + int error; + List<TABLE_LIST> dummy; + SELECT_LEX &slex= thd->lex->select_lex; + Name_resolution_context_backup backup(slex.context, *this); + Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]); + Item *value= newx Item_int(thd, trx_id); + COND *conds= newx Item_func_eq(thd, field, value); + if ((error= setup_conds(thd, this, dummy, &conds))) + return false; + select= make_select(table, 0, 0, conds, NULL, 0, &error); + if (error || !select) + return false; + // FIXME: (performance) force index 'transaction_id' + error= init_read_record(&info, thd, table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + while (!(error= info.read_record()) && !thd->killed && !thd->is_error()) + { + if (select->skip_record(thd) > 0) + return true; + } + return false; +} + +bool TR_table::query(MYSQL_TIME &commit_time, bool backwards) +{ + if (!table && open()) + return false; + SQL_SELECT_auto select; + READ_RECORD info; + int error; + List<TABLE_LIST> dummy; + SELECT_LEX &slex= thd->lex->select_lex; + Name_resolution_context_backup backup(slex.context, *this); + Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]); + Item *value= newx Item_datetime_literal(thd, &commit_time, 6); + COND *conds; + if (backwards) + conds= newx Item_func_ge(thd, field, value); + else + conds= newx Item_func_le(thd, field, value); + if ((error= setup_conds(thd, this, dummy, &conds))) + return false; + // FIXME: (performance) force index 'commit_timestamp' + select= make_select(table, 0, 0, conds, NULL, 0, &error); + if (error || !select) + return false; + error= init_read_record(&info, thd, table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + + // With PK by transaction_id the records are ordered by PK + bool found= false; + while (!(error= info.read_record()) && !thd->killed && !thd->is_error()) + { + if (select->skip_record(thd) > 0) + { + if (backwards) + return true; + found= true; + // TODO: (performance) make ORDER DESC and break after first found. + // Otherwise it is O(n) scan (+copy)! + store_record(table, record[1]); + } + else + { + if (found) + restore_record(table, record[1]); + if (!backwards) + break; + } + } + return found; +} +#undef newx + +bool TR_table::query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0, + ulonglong commit_id1, enum_tx_isolation iso_level1, + ulonglong commit_id0) +{ + if (trx_id1 == trx_id0) + { + return false; + } + + if (trx_id1 == ULONGLONG_MAX || trx_id0 == 0) + { + result= true; + return false; + } + + if (!commit_id1) + { + if (!query(trx_id1)) + return true; + + commit_id1= (*this)[FLD_COMMIT_ID]->val_int(); + iso_level1= iso_level(); + } + + if (!commit_id0) + { + if (!query(trx_id0)) + return true; + + commit_id0= (*this)[FLD_COMMIT_ID]->val_int(); + } + + // Trivial case: TX1 started after TX0 committed + if (trx_id1 > commit_id0 + // Concurrent transactions: TX1 committed after TX0 and TX1 is read (un)committed + || (commit_id1 > commit_id0 && iso_level1 < ISO_REPEATABLE_READ)) + { + result= true; + } + else // All other cases: TX1 does not see TX0 + { + result= false; + } + + return false; +} + +void TR_table::warn_schema_incorrect(const char *reason) +{ + if (MYSQL_VERSION_ID == table->s->mysql_version) + { + sql_print_error("%`s.%`s schema is incorrect: %s.", + db.str, table_name.str, reason); + } + else + { + sql_print_error("%`s.%`s schema is incorrect: %s. Created with MariaDB %d, " + "now running %d.", + db.str, table_name.str, reason, MYSQL_VERSION_ID, + static_cast<int>(table->s->mysql_version)); + } +} + +bool TR_table::check(bool error) +{ + if (error) + { + sql_print_warning("%`s.%`s does not exist (open failed).", db.str, + table_name.str); + return true; + } + + if (table->file->ht->db_type != DB_TYPE_INNODB) + { + warn_schema_incorrect("Wrong table engine (expected InnoDB)"); + return true; + } + +#define WARN_SCHEMA(...) \ + char reason[128]; \ + snprintf(reason, 128, __VA_ARGS__); \ + warn_schema_incorrect(reason); + + if (table->s->fields != FIELD_COUNT) + { + WARN_SCHEMA("Wrong field count (expected %d)", FIELD_COUNT); + return true; + } + + if (table->field[FLD_TRX_ID]->type() != MYSQL_TYPE_LONGLONG) + { + WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_TRX_ID); + return true; + } + + if (table->field[FLD_COMMIT_ID]->type() != MYSQL_TYPE_LONGLONG) + { + WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_COMMIT_ID); + return true; + } + + if (table->field[FLD_BEGIN_TS]->type() != MYSQL_TYPE_TIMESTAMP) + { + WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_BEGIN_TS); + return true; + } + + if (table->field[FLD_COMMIT_TS]->type() != MYSQL_TYPE_TIMESTAMP) + { + WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_COMMIT_TS); + return true; + } + + if (table->field[FLD_ISO_LEVEL]->type() != MYSQL_TYPE_STRING || + !(table->field[FLD_ISO_LEVEL]->flags & ENUM_FLAG)) + { + wrong_enum: + WARN_SCHEMA("Wrong field %d type (expected ENUM('READ-UNCOMMITTED', " + "'READ-COMMITTED', 'REPEATABLE-READ', 'SERIALIZABLE'))", + FLD_ISO_LEVEL); + return true; + } + + Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]); + st_typelib *typelib= iso_level->typelib; + + if (typelib->count != 4) + goto wrong_enum; + + if (strcmp(typelib->type_names[0], "READ-UNCOMMITTED") || + strcmp(typelib->type_names[1], "READ-COMMITTED") || + strcmp(typelib->type_names[2], "REPEATABLE-READ") || + strcmp(typelib->type_names[3], "SERIALIZABLE")) + { + goto wrong_enum; + } + + if (!table->key_info || !table->key_info->key_part) + goto wrong_pk; + + if (strcmp(table->key_info->key_part->field->field_name.str, "transaction_id")) + { + wrong_pk: + WARN_SCHEMA("Wrong PRIMARY KEY (expected `transaction_id`)"); + return true; + } + + return false; +} + +void vers_select_conds_t::resolve_units(bool timestamps_only) +{ + DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED); + DBUG_ASSERT(start); + if (unit_start == VERS_UNDEFINED) + { + if (start->type() == Item::FIELD_ITEM) + unit_start= VERS_TIMESTAMP; + else + unit_start= (!timestamps_only && (start->result_type() == INT_RESULT || + start->result_type() == REAL_RESULT)) ? + VERS_TRX_ID : VERS_TIMESTAMP; + } + if (end && unit_end == VERS_UNDEFINED) + { + if (start->type() == Item::FIELD_ITEM) + unit_start= VERS_TIMESTAMP; + else + unit_end= (!timestamps_only && (end->result_type() == INT_RESULT || + end->result_type() == REAL_RESULT)) ? + VERS_TRX_ID : VERS_TIMESTAMP; + } +} + Field *TABLE::find_field_by_name(LEX_CSTRING *str) const { - uint length= str->length; - for (Field **tmp= field; *tmp; tmp++) + Field **tmp; + size_t length= str->length; + if (s->name_hash.records) { - if ((*tmp)->field_name.length == length && - !my_strcasecmp(system_charset_info, (*tmp)->field_name.str, str->str)) - return *tmp; + tmp= (Field**) my_hash_search(&s->name_hash, (uchar*) str->str, length); + return tmp ? field[tmp - s->field] : NULL; + } + else + { + for (tmp= field; *tmp; tmp++) + { + if ((*tmp)->field_name.length == length && + !lex_string_cmp(system_charset_info, &(*tmp)->field_name, str)) + return *tmp; + } } return NULL; } diff --git a/sql/table.h b/sql/table.h index 83a1e8702b7..917439c243d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -17,12 +17,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_plist.h" #include "sql_list.h" /* Sql_alloc */ #include "mdl.h" #include "datadict.h" #include "sql_string.h" /* String */ +#include "lex_string.h" #ifndef MYSQL_CLIENT @@ -336,6 +336,19 @@ enum enum_vcol_update_mode VCOL_UPDATE_FOR_REPLACE }; +/* Field visibility enums */ + +enum field_visibility_t { + VISIBLE= 0, + INVISIBLE_USER, + /* automatically added by the server. Can be queried explicitly + in SELECT, otherwise invisible from anything" */ + INVISIBLE_SYSTEM, + INVISIBLE_FULL +}; + +#define INVISIBLE_MAX_BITS 3 + /** Category of table found in the table share. @@ -562,6 +575,14 @@ struct TABLE_STATISTICS_CB bool histograms_are_read; }; +class Vers_min_max_stats; + +enum vers_sys_type_t +{ + VERS_UNDEFINED= 0, + VERS_TIMESTAMP, + VERS_TRX_ID +}; /** This structure is shared between different table objects. There is one @@ -624,6 +645,16 @@ struct TABLE_SHARE LEX_CSTRING normalized_path; /* unpack_filename(path) */ LEX_CSTRING connect_string; + const char* orig_table_name; /* Original table name for this tmp table */ + const char* error_table_name() const /* Get table name for error messages */ + { + return tmp_table ? ( + orig_table_name ? + orig_table_name : + "(temporary)") : + table_name.str; + } + /* Set of keys in use, implemented as a Bitmap. Excludes keys disabled by ALTER TABLE ... DISABLE KEYS. @@ -669,6 +700,7 @@ struct TABLE_SHARE uint blob_fields; /* number of blob fields */ uint varchar_fields; /* number of varchar fields */ uint default_fields; /* number of default fields */ + uint visible_fields; /* number of visible fields */ uint default_expressions; uint table_check_constraints, field_check_constraints; @@ -695,7 +727,8 @@ struct TABLE_SHARE bool use_ext_keys; /* Extended keys can be used */ bool null_field_first; bool system; /* Set if system table (one record) */ - bool crypted; /* If .frm file is crypted */ + bool not_usable_by_query_cache; + bool no_replicate; bool crashed; bool is_view; bool can_cmp_whole_record; @@ -704,6 +737,8 @@ struct TABLE_SHARE bool vcols_need_refixing; bool check_set_initialized; bool has_update_default_function; + bool can_do_row_logging; /* 1 if table supports RBR */ + ulong table_map_id; /* for row-based replication */ /* @@ -722,14 +757,6 @@ struct TABLE_SHARE /* For sequence tables, the current sequence state */ SEQUENCE *sequence; - /* - Cache for row-based replication table share checks that does not - need to be repeated. Possible values are: -1 when cache value is - not calculated yet, 0 when table *shall not* be replicated, 1 when - table *may* be replicated. - */ - int cached_row_logging_check; - /* Name of the tablespace used for this table */ char *tablespace; @@ -743,6 +770,52 @@ struct TABLE_SHARE #endif /** + System versioning support. + */ + + vers_sys_type_t versioned; + bool vtmd; + uint16 row_start_field; + uint16 row_end_field; + uint32 hist_part_id; + Vers_min_max_stats** stat_trx; + ulonglong stat_serial; // guards check_range_constants() updates + + bool busy_rotation; + mysql_mutex_t LOCK_rotation; + mysql_cond_t COND_rotation; + mysql_rwlock_t LOCK_stat_serial; + + void vers_init() + { + hist_part_id= UINT_MAX32; + busy_rotation= false; + stat_trx= NULL; + stat_serial= 0; + mysql_mutex_init(key_TABLE_SHARE_LOCK_rotation, &LOCK_rotation, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_TABLE_SHARE_COND_rotation, &COND_rotation, NULL); + mysql_rwlock_init(key_rwlock_LOCK_stat_serial, &LOCK_stat_serial); + } + + void vers_destroy(); + + Field *vers_start_field() + { + return field[row_start_field]; + } + + Field *vers_end_field() + { + return field[row_end_field]; + } + + void vers_wait_rotation() + { + while (busy_rotation) + mysql_cond_wait(&COND_rotation, &LOCK_rotation); + } + + /** Cache the checked structure of this table. The pointer data is used to describe the structure that @@ -1001,7 +1074,7 @@ public: @retval Pointer to the copied string. @retval 0 if an error occurred. */ - char *store(const char *from, uint length) + char *store(const char *from, size_t length) { return (char*) memdup_root(&storage, from, length); } @@ -1029,6 +1102,8 @@ struct st_cond_statistic; /* Bitmap of table's fields */ typedef Bitmap<MAX_FIELDS> Field_map; +class SplM_opt_info; + struct TABLE { TABLE() {} /* Remove gcc warning */ @@ -1053,7 +1128,7 @@ public: uint32 instance; /** Table cache instance this TABLE is belonging to */ THD *in_use; /* Which thread uses this */ - uchar *record[2]; /* Pointer to records */ + uchar *record[3]; /* Pointer to records */ uchar *write_row_record; /* Used as optimisation in THD::write_row */ uchar *insert_values; /* used by INSERT ... UPDATE */ @@ -1093,6 +1168,8 @@ public: TABLE_LIST *pos_in_table_list;/* Element referring to this table */ /* Position in thd->locked_table_list under LOCK TABLES */ TABLE_LIST *pos_in_locked_tables; + /* Tables used in DEFAULT and CHECK CONSTRAINT (normally sequence tables) */ + TABLE_LIST *internal_tables; /* Not-null for temporary tables only. Non-null values means this table is @@ -1257,7 +1334,6 @@ public: If set, indicate that the table is not replicated by the server. */ bool locked_by_logger; - bool no_replicate; bool locked_by_name; bool fulltext_searched; bool no_cache; @@ -1307,7 +1383,13 @@ public: bool stats_is_read; /* Persistent statistics is read for the table */ bool histograms_are_read; MDL_ticket *mdl_ticket; - List<Field> splitting_fields; + + /* + This is used only for potentially splittable materialized tables and it + points to the info used by the optimizer to apply splitting optimization + */ + SplM_opt_info *spl_opt_info; + key_map keys_usable_for_splitting; void init(THD *thd, TABLE_LIST *tl); bool fill_item_list(List<Item> *item_list) const; @@ -1327,6 +1409,8 @@ public: void mark_columns_per_binlog_row_image(void); bool mark_virtual_col(Field *field); bool mark_virtual_columns_for_write(bool insert_fl); + bool check_virtual_columns_marked_for_read(); + bool check_virtual_columns_marked_for_write(); void mark_default_fields_for_write(bool insert_fl); void mark_columns_used_by_check_constraints(void); void mark_check_constraint_columns_for_read(void); @@ -1441,7 +1525,7 @@ public: bool prepare_triggers_for_delete_stmt_or_event(); bool prepare_triggers_for_update_stmt_or_event(); - inline Field **field_to_fill(); + Field **field_to_fill(); bool validate_default_values_of_unset_fields(THD *thd) const; bool insert_all_rows_into_tmp_table(THD *thd, @@ -1450,6 +1534,62 @@ public: bool with_cleanup); Field *find_field_by_name(LEX_CSTRING *str) const; bool export_structure(THD *thd, class Row_definition_list *defs); + bool is_splittable() { return spl_opt_info != NULL; } + void set_spl_opt_info(SplM_opt_info *spl_info); + void deny_splitting(); + void add_splitting_info_for_key_field(struct KEY_FIELD *key_field); + + /** + System Versioning support + */ + bool vers_write; + + bool versioned() const + { + DBUG_ASSERT(s); + return s->versioned; + } + + bool versioned(vers_sys_type_t type) const + { + DBUG_ASSERT(s); + DBUG_ASSERT(type); + return s->versioned == type; + } + + bool versioned_write(vers_sys_type_t type= VERS_UNDEFINED) const + { + DBUG_ASSERT(versioned() || !vers_write); + return versioned(type) ? vers_write : false; + } + + bool vers_vtmd() const + { + DBUG_ASSERT(s); + return s->versioned && s->vtmd; + } + + Field *vers_start_field() const + { + DBUG_ASSERT(s && s->versioned); + return field[s->row_start_field]; + } + + Field *vers_end_field() const + { + DBUG_ASSERT(s && s->versioned); + return field[s->row_end_field]; + } + + ulonglong vers_start_id() const; + ulonglong vers_end_id() const; + + int delete_row(); + void vers_update_fields(); + void vers_update_end(); + +/** Number of additional fields used in versioned tables */ +#define VERSIONING_FIELDS 2 }; @@ -1559,7 +1699,7 @@ typedef class Item COND; typedef struct st_schema_table { - const char* table_name; + const char *table_name; ST_FIELD_INFO *fields_info; /* for FLUSH table_name */ int (*reset_table) (); @@ -1681,8 +1821,8 @@ public: LEX_CSTRING *name(); Item *create_item(THD *thd); Field *field(); - const char *table_name(); - const char *db_name(); + const char *safe_table_name(); + const char *safe_db_name(); GRANT_INFO *grant(); }; @@ -1737,52 +1877,110 @@ class Item_in_subselect; 4) jtbm semi-join (jtbm_subselect != NULL) */ +/** last_leaf_for_name_resolutioning support. */ +struct vers_select_conds_t +{ + vers_system_time_t type; + vers_sys_type_t unit_start, unit_end; + bool from_query:1; + bool used:1; + Item *start, *end; + + void empty() + { + type= SYSTEM_TIME_UNSPECIFIED; + unit_start= unit_end= VERS_UNDEFINED; + used= from_query= false; + start= end= NULL; + } + + Item *fix_dec(Item *item); + + void init(vers_system_time_t t, vers_sys_type_t u_start= VERS_UNDEFINED, + Item * s= NULL, vers_sys_type_t u_end= VERS_UNDEFINED, + Item * e= NULL); + + void print(String *str, enum_query_type query_type); + + bool init_from_sysvar(THD *thd); + + bool operator== (vers_system_time_t b) + { + return type == b; + } + bool operator!= (vers_system_time_t b) + { + return type != b; + } + operator bool() const + { + return type != SYSTEM_TIME_UNSPECIFIED; + } + void resolve_units(bool timestamps_only); + bool user_defined() const + { + return !from_query && type != SYSTEM_TIME_UNSPECIFIED; + } +}; + struct LEX; class Index_hint; struct TABLE_LIST { TABLE_LIST() {} /* Remove gcc warning */ + enum prelocking_types + { + PRELOCK_NONE, PRELOCK_ROUTINE, PRELOCK_FK + }; + /** Prepare TABLE_LIST that consists of one table instance to use in open_and_lock_tables */ - inline void init_one_table(const char *db_name_arg, - size_t db_length_arg, - const char *table_name_arg, - size_t table_name_length_arg, - const char *alias_arg, - enum thr_lock_type lock_type_arg) +inline void init_one_table(const LEX_CSTRING *db_arg, + const LEX_CSTRING *table_name_arg, + const LEX_CSTRING *alias_arg, + enum thr_lock_type lock_type_arg) { bzero((char*) this, sizeof(*this)); - db= (char*) db_name_arg; - db_length= db_length_arg; - table_name= (char*) table_name_arg; - table_name_length= table_name_length_arg; - alias= (char*) (alias_arg ? alias_arg : table_name_arg); + 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); + DBUG_ASSERT(!alias_arg || strlen(alias_arg->str) == alias_arg->length); + db= *db_arg; + 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, table_name, + mdl_request.init(MDL_key::TABLE, db.str, table_name.str, (lock_type >= TL_WRITE_ALLOW_WRITE) ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_TRANSACTION); } - inline void init_one_table_for_prelocking(const char *db_name_arg, - size_t db_length_arg, - const char *table_name_arg, - size_t table_name_length_arg, - const char *alias_arg, - enum thr_lock_type lock_type_arg, - bool routine, - TABLE_LIST *belong_to_view_arg, - uint8 trg_event_map_arg, - TABLE_LIST ***last_ptr) + TABLE_LIST(TABLE *table_arg, thr_lock_type lock_type) + { + DBUG_ASSERT(table_arg->s); + init_one_table(&table_arg->s->db, &table_arg->s->table_name, + NULL, lock_type); + table= table_arg; + } + + inline void init_one_table_for_prelocking(const LEX_CSTRING *db_arg, + const LEX_CSTRING *table_name_arg, + const LEX_CSTRING *alias_arg, + enum thr_lock_type lock_type_arg, + prelocking_types prelocking_type, + TABLE_LIST *belong_to_view_arg, + uint8 trg_event_map_arg, + TABLE_LIST ***last_ptr) + { - init_one_table(db_name_arg, db_length_arg, table_name_arg, - table_name_length_arg, alias_arg, lock_type_arg); + init_one_table(db_arg, table_name_arg, alias_arg, lock_type_arg); cacheable_table= 1; - prelocking_placeholder= routine ? ROUTINE : FK; - open_type= routine ? OT_TEMPORARY_OR_BASE : OT_BASE_ONLY; + prelocking_placeholder= prelocking_type; + open_type= (prelocking_type == PRELOCK_ROUTINE ? + OT_TEMPORARY_OR_BASE : + OT_BASE_ONLY); belong_to_view= belong_to_view_arg; trg_event_map= trg_event_map_arg; @@ -1799,7 +1997,10 @@ struct TABLE_LIST TABLE_LIST *next_local; /* link in a global list of all queries tables */ TABLE_LIST *next_global, **prev_global; - const char *db, *alias, *table_name, *schema_table_name; + LEX_CSTRING db; + LEX_CSTRING table_name; + LEX_CSTRING schema_table_name; + LEX_CSTRING alias; const char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ @@ -2033,8 +2234,6 @@ struct TABLE_LIST thr_lock_type lock_type; uint outer_join; /* Which join type */ uint shared; /* Used in multi-upd */ - size_t db_length; - size_t table_name_length; bool updatable; /* VIEW/TABLE can be updated now */ bool straight; /* optimize with prev table */ bool updating; /* for replicate-do/ignore table */ @@ -2071,7 +2270,7 @@ struct TABLE_LIST This TABLE_LIST object is just placeholder for prelocking, it will be used for implicit LOCK TABLES only and won't be used in real statement. */ - enum { USER, ROUTINE, FK } prelocking_placeholder; + prelocking_types prelocking_placeholder; /** Indicates that if TABLE_LIST object corresponds to the table/view which requires special handling. @@ -2085,9 +2284,6 @@ struct TABLE_LIST /* Don't associate a table share. */ OPEN_STUB } open_strategy; - /* For transactional locking. */ - int lock_timeout; /* NOWAIT or WAIT [X] */ - bool lock_transactional; /* If transactional lock requested. */ /** TRUE if an alias for this table was specified in the SQL. */ bool is_alias; /** TRUE if the table is referred to in the statement using a fully @@ -2135,7 +2331,7 @@ struct TABLE_LIST View definition (SELECT-statement) in the UTF-form. */ - LEX_STRING view_body_utf8; + LEX_CSTRING view_body_utf8; /* End of view definition context. */ @@ -2197,6 +2393,12 @@ struct TABLE_LIST TABLE_LIST *find_underlying_table(TABLE *table); TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); + + /* System Versioning */ + vers_select_conds_t vers_conditions; + bool vers_vtmd_name(String &out) const; + bool vers_force_alias; + /** @brief Find the bottom in the chain of embedded table VIEWs. @@ -2324,7 +2526,7 @@ struct TABLE_LIST { DBUG_ENTER("set_merged_derived"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (alias ? alias : "<NULL>"), + (alias.str ? alias.str : "<NULL>"), get_unit())); derived_type= ((derived_type & DTYPE_MASK) | DTYPE_TABLE | DTYPE_MERGE); @@ -2339,7 +2541,7 @@ struct TABLE_LIST { DBUG_ENTER("set_materialized_derived"); DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", - (alias ? alias : "<NULL>"), + (alias.str ? alias.str : "<NULL>"), get_unit())); derived_type= ((derived_type & (derived ? DTYPE_MASK : DTYPE_VIEW)) | DTYPE_TABLE | DTYPE_MATERIALIZE); @@ -2368,7 +2570,7 @@ struct TABLE_LIST @brief Returns the name of the database that the referenced table belongs to. */ - const char *get_db_name() const { return view != NULL ? view_db.str : db; } + const char *get_db_name() const { return view != NULL ? view_db.str : db.str; } /** @brief Returns the name of the table that this TABLE_LIST represents. @@ -2376,7 +2578,7 @@ struct TABLE_LIST @details The unqualified table name or view name for a table or view, respectively. */ - const char *get_table_name() const { return view != NULL ? view_name.str : table_name; } + const char *get_table_name() const { return view != NULL ? view_name.str : table_name.str; } bool is_active_sjm(); bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); } st_select_lex_unit *get_unit(); @@ -2591,7 +2793,7 @@ typedef struct st_changed_table_list { struct st_changed_table_list *next; char *key; - uint32 key_length; + size_t key_length; } CHANGED_TABLE_LIST; @@ -2622,7 +2824,7 @@ static inline void tmp_restore_column_map(MY_BITMAP *bitmap, static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, MY_BITMAP *bitmap) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS return tmp_use_all_columns(table, bitmap); #else return 0; @@ -2632,7 +2834,7 @@ static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap, my_bitmap_map *old) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(bitmap, old); #endif } @@ -2647,7 +2849,7 @@ static inline void dbug_tmp_use_all_columns(TABLE *table, MY_BITMAP *read_set, MY_BITMAP *write_set) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS save[0]= read_set->bitmap; save[1]= write_set->bitmap; (void) tmp_use_all_columns(table, read_set); @@ -2660,7 +2862,7 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, MY_BITMAP *write_set, my_bitmap_map **old) { -#ifndef DBUG_OFF +#ifdef DBUG_ASSERT_EXISTS tmp_restore_column_map(read_set, old[0]); tmp_restore_column_map(write_set, old[1]); #endif @@ -2681,9 +2883,10 @@ size_t max_row_length(TABLE *table, const uchar *data); void init_mdl_requests(TABLE_LIST *table_list); enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, - const char *alias, uint db_stat, uint prgflag, + const LEX_CSTRING *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, - bool is_create_table); + bool is_create_table, + List<String> *partitions_to_open= NULL); bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol); bool fix_session_vcol_expr_for_read(THD *thd, Field *field, Virtual_column_info *vcol); @@ -2701,7 +2904,6 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, void open_table_error(TABLE_SHARE *share, enum open_frm_error error, int db_errno); void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form); -bool check_and_convert_db_name(LEX_STRING *db, bool preserve_lettercase); bool check_db_name(LEX_STRING *db); bool check_column_name(const char *name); bool check_table_name(const char *name, size_t length, bool check_for_path_chars); @@ -2716,10 +2918,10 @@ int closefrm(TABLE *table); void free_blobs(TABLE *table); void free_field_buffers_larger_than(TABLE *table, uint32 size); ulong get_form_pos(File file, uchar *head, TYPELIB *save_names); -void append_unescaped(String *res, const char *pos, uint length); +void append_unescaped(String *res, const char *pos, size_t length); void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys, KEY *key_info); -char *fn_rext(char *name); +const char *fn_frm_ext(const char *name); /* Check that the integer is in the internal */ static inline int set_zone(int nr,int min_zone,int max_zone) @@ -2736,25 +2938,22 @@ extern LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME; extern LEX_CSTRING GENERAL_LOG_NAME; extern LEX_CSTRING SLOW_LOG_NAME; +extern LEX_CSTRING TRANSACTION_REG_NAME; /* information schema */ extern LEX_CSTRING INFORMATION_SCHEMA_NAME; extern LEX_CSTRING MYSQL_SCHEMA_NAME; -inline bool is_infoschema_db(const char *name, size_t len) -{ - return (INFORMATION_SCHEMA_NAME.length == len && - !my_strcasecmp(system_charset_info, - INFORMATION_SCHEMA_NAME.str, name)); -} +/* table names */ +extern LEX_CSTRING MYSQL_USER_NAME, MYSQL_DB_NAME, MYSQL_PROC_NAME; -inline bool is_infoschema_db(const char *name) +inline bool is_infoschema_db(const LEX_CSTRING *name) { - return !my_strcasecmp(system_charset_info, - INFORMATION_SCHEMA_NAME.str, name); + return (INFORMATION_SCHEMA_NAME.length == name->length && + !my_strcasecmp(system_charset_info, + INFORMATION_SCHEMA_NAME.str, name->str)); } - inline void mark_as_null_row(TABLE *table) { table->null_row=1; @@ -2764,6 +2963,150 @@ inline void mark_as_null_row(TABLE *table) bool is_simple_order(ORDER *order); +class Open_tables_backup; + +/** Transaction Registry Table (TRT) + + This table holds transaction IDs, their corresponding times and other + transaction-related data which is used for transaction order resolution. + When versioned table marks its records lifetime with transaction IDs, + TRT is used to get their actual timestamps. */ + +class TR_table: public TABLE_LIST +{ + THD *thd; + Open_tables_backup *open_tables_backup; + +public: + enum field_id_t { + FLD_TRX_ID= 0, + FLD_COMMIT_ID, + FLD_BEGIN_TS, + FLD_COMMIT_TS, + FLD_ISO_LEVEL, + FIELD_COUNT + }; + + enum enabled {NO, MAYBE, YES}; + static enum enabled use_transaction_registry; + + /** + @param[in,out] Thread handle + @param[in] Current transaction is read-write. + */ + TR_table(THD *_thd, bool rw= false); + /** + Opens a transaction_registry table. + + @retval true on error, false otherwise. + */ + bool open(); + ~TR_table(); + /** + @retval current thd + */ + THD *get_thd() const { return thd; } + /** + Stores value to internal transaction_registry TABLE object. + + @param[in] field number in a TABLE + @param[in] value to store + */ + void store(uint field_id, ulonglong val); + /** + Stores value to internal transaction_registry TABLE object. + + @param[in] field number in a TABLE + @param[in] value to store + */ + void store(uint field_id, timeval ts); + /** + Update the transaction_registry right before commit. + @param start_id transaction identifier at start + @param end_id transaction identifier at commit + + @retval false on success + @retval true on error (the transaction must be rolled back) + */ + bool update(ulonglong start_id, ulonglong end_id); + // return true if found; false if not found or error + bool query(ulonglong trx_id); + /** + Gets a row from transaction_registry with the closest commit_timestamp to + first argument. We can search for a value which a lesser or greater than + first argument. Also loads a row into an internal TABLE object. + + @param[in] timestamp + @param[in] true if we search for a lesser timestamp, false if greater + @retval true if exists, false it not exists or an error occured + */ + bool query(MYSQL_TIME &commit_time, bool backwards); + /** + Checks whether transaction1 sees transaction0. + + @param[out] true if transaction1 sees transaction0, undefined on error and + when transaction1=transaction0 and false otherwise + @param[in] transaction_id of transaction1 + @param[in] transaction_id of transaction0 + @param[in] commit time of transaction1 or 0 if we want it to be queried + @param[in] isolation level (from handler.h) of transaction1 + @param[in] commit time of transaction0 or 0 if we want it to be queried + @retval true on error, false otherwise + */ + bool query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0, + ulonglong commit_id1= 0, + enum_tx_isolation iso_level1= ISO_READ_UNCOMMITTED, + ulonglong commit_id0= 0); + + /** + @retval transaction isolation level of a row from internal TABLE object. + */ + enum_tx_isolation iso_level() const; + /** + Stores transactioin isolation level to internal TABLE object. + */ + void store_iso_level(enum_tx_isolation iso_level) + { + DBUG_ASSERT(iso_level <= ISO_SERIALIZABLE); + store(FLD_ISO_LEVEL, iso_level + 1); + } + + /** + Writes a message to MariaDB log about incorrect transaction_registry schema. + + @param[in] a message explained what's incorrect in schema + */ + void warn_schema_incorrect(const char *reason); + /** + Checks whether transaction_registry table has a correct schema. + + @retval true if schema is incorrect and false otherwise + */ + bool check(bool error); + + TABLE * operator-> () const + { + return table; + } + Field * operator[] (uint field_id) const + { + DBUG_ASSERT(field_id < FIELD_COUNT); + return table->field[field_id]; + } + operator bool () const + { + return table; + } + bool operator== (const TABLE_LIST &subj) const + { + return (!cmp(&db, &subj.db) && !cmp(&table_name, &subj.table_name)); + } + bool operator!= (const TABLE_LIST &subj) const + { + return !(*this == subj); + } +}; + #endif /* MYSQL_CLIENT */ #endif /* TABLE_INCLUDED */ diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 393fe931a82..18ea7f83964 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -46,7 +46,7 @@ TABLE_SHARE::tdc.flushed is true */ -#include "my_global.h" +#include "mariadb.h" #include "lf.h" #include "table.h" #include "sql_base.h" @@ -56,7 +56,7 @@ ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */ ulong tc_size; /**< Table cache threshold for LRU eviction. */ uint32 tc_instances; -static uint32 tc_active_instances= 1; +uint32 tc_active_instances= 1; static uint32 tc_contention_warning_reported; /** Data collections. */ @@ -369,18 +369,30 @@ void tc_add_table(THD *thd, TABLE *table) mysql_mutex_unlock(&element->LOCK_table_share); mysql_mutex_lock(&tc[i].LOCK_table_cache); - if (tc[i].records == tc_size && (LRU_table= tc[i].free_tables.pop_front())) + if (tc[i].records == tc_size) { - LRU_table->s->tdc->free_tables[i].list.remove(LRU_table); - /* Needed if MDL deadlock detector chimes in before tc_remove_table() */ - LRU_table->in_use= thd; + if ((LRU_table= tc[i].free_tables.pop_front())) + { + LRU_table->s->tdc->free_tables[i].list.remove(LRU_table); + /* Needed if MDL deadlock detector chimes in before tc_remove_table() */ + LRU_table->in_use= thd; + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + /* Keep out of locked LOCK_table_cache */ + tc_remove_table(LRU_table); + } + else + { + tc[i].records++; + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + } + /* Keep out of locked LOCK_table_cache */ + status_var_increment(thd->status_var.table_open_cache_overflows); } else + { tc[i].records++; - mysql_mutex_unlock(&tc[i].LOCK_table_cache); - - if (LRU_table) - tc_remove_table(LRU_table); + mysql_mutex_unlock(&tc[i].LOCK_table_cache); + } } @@ -575,17 +587,17 @@ static void lf_alloc_destructor(uchar *arg) } -static void tdc_hash_initializer(LF_HASH *hash __attribute__((unused)), +static void tdc_hash_initializer(LF_HASH *, TDC_element *element, LEX_STRING *key) { memcpy(element->m_key, key->str, key->length); - element->m_key_length= key->length; + element->m_key_length= (uint)key->length; tdc_assert_clean_share(element); } static uchar *tdc_hash_key(const TDC_element *element, size_t *length, - my_bool not_used __attribute__((unused))) + my_bool) { *length= element->m_key_length; return (uchar*) element->m_key; @@ -815,7 +827,7 @@ retry: lf_hash_search_unpin(thd->tdc_hash_pins); DBUG_ASSERT(element); - if (!(share= alloc_table_share(tl->db, tl->table_name, key, key_length))) + if (!(share= alloc_table_share(tl->db.str, tl->table_name.str, key, key_length))) { lf_hash_delete(&tdc_hash, thd->tdc_hash_pins, key, key_length); DBUG_RETURN(0); @@ -841,7 +853,10 @@ retry: tdc_purge(false); if (out_table) + { + status_var_increment(thd->status_var.table_open_cache_misses); *out_table= 0; + } share->m_psi= PSI_CALL_get_table_share(false, share); goto end; } @@ -858,8 +873,10 @@ retry: DBUG_ASSERT(element->share); DBUG_ASSERT(!element->share->error); DBUG_ASSERT(!element->share->is_view); + status_var_increment(thd->status_var.table_open_cache_hits); DBUG_RETURN(element->share); } + status_var_increment(thd->status_var.table_open_cache_misses); } mysql_mutex_lock(&element->LOCK_table_share); diff --git a/sql/table_cache.h b/sql/table_cache.h index 2e5bb3428dc..b41665258c9 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -71,6 +71,7 @@ enum enum_tdc_remove_table_type extern ulong tdc_size; extern ulong tc_size; extern uint32 tc_instances; +extern uint32 tc_active_instances; extern bool tdc_init(void); extern void tdc_start_shutdown(void); diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 5e5a05084b3..4c6671cc2ae 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -19,6 +19,7 @@ All methods pertaining to temporary tables. */ +#include "mariadb.h" #include "sql_acl.h" /* TMP_TABLE_ACLS */ #include "sql_base.h" /* tdc_create_key */ #include "lock.h" /* mysql_lock_remove */ @@ -64,7 +65,8 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, const char *path, const char *db, const char *table_name, - bool open_in_engine) + bool open_in_engine, + bool open_internal_tables) { DBUG_ENTER("THD::create_and_open_tmp_table"); @@ -89,6 +91,15 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, /* Free the TMP_TABLE_SHARE. */ free_tmp_table_share(share, false); + DBUG_RETURN(0); + } + + /* Open any related tables */ + if (open_internal_tables && table->internal_tables && + open_and_lock_internal_tables(table, open_in_engine)) + { + drop_temporary_table(table, NULL, false); + DBUG_RETURN(0); } } @@ -252,7 +263,7 @@ TMP_TABLE_SHARE *THD::find_tmp_table_share(const TABLE_LIST *tl) @return Success A pointer to table share object Failure NULL */ -TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, uint key_length) +TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, size_t key_length) { DBUG_ENTER("THD::find_tmp_table_share"); @@ -317,7 +328,7 @@ TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, uint key_length) bool THD::open_temporary_table(TABLE_LIST *tl) { DBUG_ENTER("THD::open_temporary_table"); - DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name)); + DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db.str, tl->table_name.str)); TMP_TABLE_SHARE *share; TABLE *table= NULL; @@ -380,7 +391,7 @@ bool THD::open_temporary_table(TABLE_LIST *tl) if (tl->open_type == OT_TEMPORARY_ONLY && tl->open_strategy == TABLE_LIST::OPEN_NORMAL) { - my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name); + my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db.str, tl->table_name.str); DBUG_RETURN(true); } DBUG_RETURN(false); @@ -533,25 +544,21 @@ bool THD::close_temporary_tables() true Error */ bool THD::rename_temporary_table(TABLE *table, - const char *db, - const char *table_name) + const LEX_CSTRING *db, + const LEX_CSTRING *table_name) { - DBUG_ENTER("THD::rename_temporary_table"); - char *key; uint key_length; - TABLE_SHARE *share= table->s; + DBUG_ENTER("THD::rename_temporary_table"); if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH))) - { DBUG_RETURN(true); - } /* Temporary tables are renamed by simply changing their table definition key. */ - key_length= create_tmp_table_def_key(key, db, table_name); + key_length= create_tmp_table_def_key(key, db->str, table_name->str); share->set_table_cache_key(key, key_length); DBUG_RETURN(false); @@ -1091,19 +1098,20 @@ done: Failure NULL */ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, - const char *alias, + const char *alias_arg, bool open_in_engine) { + TABLE *table; + LEX_CSTRING alias= {alias_arg, strlen(alias_arg) }; DBUG_ENTER("THD::open_temporary_table"); - TABLE *table; if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME)))) { DBUG_RETURN(NULL); /* Out of memory */ } - if (open_table_from_share(this, share, alias, + if (open_table_from_share(this, share, &alias, open_in_engine ? (uint)HA_OPEN_KEYFILE : 0, EXTRA_RECORD, (ha_open_options | @@ -1118,6 +1126,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, table->grant.privilege= TMP_TABLE_ACLS; share->tmp_table= (table->file->has_transaction_manager() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); + share->not_usable_by_query_cache= 1; table->pos_in_table_list= 0; table->query_id= query_id; @@ -1367,8 +1376,7 @@ bool THD::log_events_and_free_tmp_shares() We are going to add ` around the table names and possible more due to special characters. */ - append_identifier(this, &s_query, share->table_name.str, - share->table_name.length); + append_identifier(this, &s_query, &share->table_name); s_query.append(','); rm_temporary_table(share->db_type(), share->path.str); free_table_share(share); diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index 94ae1a4431c..e0385c33fd8 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -17,7 +17,7 @@ /* Mallocs for used in threads */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "thr_malloc.h" diff --git a/sql/thr_malloc.h b/sql/thr_malloc.h index a36059b97b2..b56b7175ed1 100644 --- a/sql/thr_malloc.h +++ b/sql/thr_malloc.h @@ -16,8 +16,6 @@ #ifndef THR_MALLOC_INCLUDED #define THR_MALLOC_INCLUDED -#include "my_global.h" // uint, size_t - typedef struct st_mem_root MEM_ROOT; void init_sql_alloc(MEM_ROOT *root, const char *area_name, uint block_size, diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 98b2dcd1fcd..e5ac4b49a2a 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> @@ -84,17 +84,13 @@ struct Worker_thread_context void save() { -#ifdef HAVE_PSI_THREAD_INTERFACE - psi_thread = PSI_THREAD_CALL(get_thread)(); -#endif + psi_thread = PSI_CALL_get_thread(); mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys); } void restore() { -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread)(psi_thread); -#endif + PSI_CALL_set_thread(psi_thread); pthread_setspecific(THR_KEY_mysys,mysys_var); pthread_setspecific(THR_THD, 0); } @@ -144,9 +140,7 @@ static void thread_attach(THD* thd) pthread_setspecific(THR_KEY_mysys,thd->mysys_var); thd->thread_stack=(char*)&thd; thd->store_globals(); -#ifdef HAVE_PSI_THREAD_INTERFACE - PSI_THREAD_CALL(set_thread)(thd->event_scheduler.m_psi); -#endif + PSI_CALL_set_thread(thd->event_scheduler.m_psi); mysql_socket_set_thread_owner(thd->net.vio->mysql_socket); } @@ -199,7 +193,7 @@ void tp_callback(TP_connection *c) c->priority= get_priority(c); /* Read next command from client. */ - c->set_io_timeout(thd->variables.net_wait_timeout); + c->set_io_timeout(thd->get_net_wait_timeout()); c->state= TP_STATE_IDLE; if (c->start_io()) goto error; @@ -254,10 +248,8 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data) thd->event_scheduler.data= scheduler_data; /* Create new PSI thread for use with the THD. */ -#ifdef HAVE_PSI_THREAD_INTERFACE thd->event_scheduler.m_psi= - PSI_THREAD_CALL(new_thread)(key_thread_one_connection, thd, thd->thread_id); -#endif + PSI_CALL_new_thread(key_thread_one_connection, thd, thd->thread_id); /* Login. */ diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc index 400d072df3c..04a72034e5e 100644 --- a/sql/threadpool_generic.cc +++ b/sql/threadpool_generic.cc @@ -13,7 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> @@ -63,9 +63,6 @@ typedef OVERLAPPED_ENTRY native_event; #error threadpool is not available on this platform #endif -#ifdef _MSC_VER -#pragma warning (disable : 4312) -#endif static void io_poll_close(TP_file_handle fd) { diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index 855b9b38d78..adaca08982f 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -19,7 +19,7 @@ #define _WIN32_WINNT 0x0601 -#include <my_global.h> +#include "mariadb.h" #include <violite.h> #include <sql_priv.h> #include <sql_class.h> diff --git a/sql/transaction.cc b/sql/transaction.cc index a6db204c45b..d8d435e826a 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -18,13 +18,12 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "transaction.h" -#include "rpl_handler.h" #include "debug_sync.h" // DEBUG_SYNC #include "sql_acl.h" - +#include "semisync_master.h" #ifndef EMBEDDED_LIBRARY /** @@ -318,9 +317,17 @@ bool trans_commit(THD *thd) transaction, so the hooks for rollback will be called. */ if (res) - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif + } else - (void) RUN_HOOK(transaction, after_commit, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, FALSE); +#endif + } thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.reset(); thd->lex->start_transaction_opt= 0; @@ -413,7 +420,9 @@ bool trans_rollback(THD *thd) ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); res= ha_rollback_trans(thd, TRUE); - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); /* Reset the binlog transaction marker */ thd->variables.option_bits&= ~OPTION_GTID_BEGIN; @@ -526,9 +535,17 @@ bool trans_commit_stmt(THD *thd) transaction, so the hooks for rollback will be called. */ if (res) - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif + } else - (void) RUN_HOOK(transaction, after_commit, (thd, FALSE)); + { +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_commit(thd, FALSE); +#endif + } thd->transaction.stmt.reset(); @@ -567,7 +584,9 @@ bool trans_rollback_stmt(THD *thd) trans_reset_one_shot_chistics(thd); } - (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); +#ifdef HAVE_REPLICATION + repl_semisync_master.wait_after_rollback(thd, FALSE); +#endif thd->transaction.stmt.reset(); @@ -630,7 +649,7 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name) } newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length); - newsv->length= name.length; + newsv->length= (uint)name.length; /* if we'll get an error here, don't add new savepoint to the list. diff --git a/sql/transaction.h b/sql/transaction.h index b6b22053150..7e34693a2eb 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -20,7 +20,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_global.h> #include <m_string.h> class THD; diff --git a/sql/tztime.cc b/sql/tztime.cc index 45af8f24943..f7e2dcbefb8 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -29,22 +29,21 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #if !defined(TZINFO2SQL) && !defined(TESTTIME) #include "sql_priv.h" #include "unireg.h" -#include "tztime.h" #include "sql_time.h" // localtime_to_TIME #include "sql_base.h" // open_system_tables_for_read, // close_system_tables #else #include <my_time.h> -#include "tztime.h" #include <my_sys.h> #include <mysql_version.h> #include <my_getopt.h> #endif +#include "tztime.h" #include "tzfile.h" #include <m_string.h> #include <my_dir.h> @@ -167,7 +166,7 @@ static my_bool tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) { uchar *p; - int read_from_file; + ssize_t read_from_file; uint i; MYSQL_FILE *file; @@ -188,7 +187,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) uint ttisgmtcnt; char *tzinfo_buf; - read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME)); + read_from_file= (ssize_t)mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME)); if (mysql_file_fclose(file, MYF(MY_WME)) != 0) return 1; @@ -1334,7 +1333,7 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg): { uint hours= abs((int)(offset / SECS_PER_HOUR)); uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN)); - ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d", + size_t length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d", (offset>=0) ? "+" : "-", hours, minutes); name.set(name_buff, length, &my_charset_latin1); } @@ -1477,19 +1476,14 @@ static bool time_zone_tables_exist= 1; for dynamical loading of time zone descriptions. */ -static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]= +static const LEX_CSTRING tz_tables_names[MY_TZ_TABLES_COUNT]= { - { C_STRING_WITH_LEN("time_zone_name")}, - { C_STRING_WITH_LEN("time_zone")}, - { C_STRING_WITH_LEN("time_zone_transition_type")}, - { C_STRING_WITH_LEN("time_zone_transition")} + { STRING_WITH_LEN("time_zone_name")}, + { STRING_WITH_LEN("time_zone")}, + { STRING_WITH_LEN("time_zone_transition_type")}, + { STRING_WITH_LEN("time_zone_transition")} }; -/* Name of database to which those tables belong. */ - -static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")}; - - class Tz_names_entry: public Sql_alloc { public: @@ -1541,10 +1535,8 @@ tz_init_table_list(TABLE_LIST *tz_tabs) for (int i= 0; i < MY_TZ_TABLES_COUNT; i++) { - tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str; - tz_tabs[i].table_name_length= tz_tables_names[i].length; - tz_tabs[i].db= tz_tables_db_name.str; - tz_tabs[i].db_length= tz_tables_db_name.length; + tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i]; + tz_tabs[i].db= MYSQL_SCHEMA_NAME; tz_tabs[i].lock_type= TL_READ; if (i != MY_TZ_TABLES_COUNT - 1) @@ -1607,9 +1599,9 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) THD *thd; TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT]; TABLE *table; + const LEX_CSTRING tmp_table_name= { STRING_WITH_LEN("time_zone_leap_second") }; Tz_names_entry *tmp_tzname; my_bool return_val= 1; - char db[]= "mysql"; int res; DBUG_ENTER("my_tz_init"); @@ -1670,13 +1662,10 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) leap seconds shared by all time zones. */ - thd->set_db(db, sizeof(db)-1); + thd->set_db(&MYSQL_SCHEMA_NAME); bzero((char*) &tz_tables[0], sizeof(TABLE_LIST)); - tz_tables[0].alias= tz_tables[0].table_name= - (char*)"time_zone_leap_second"; - tz_tables[0].table_name_length= 21; - tz_tables[0].db= db; - tz_tables[0].db_length= sizeof(db)-1; + tz_tables[0].alias= tz_tables[0].table_name= tmp_table_name; + tz_tables[0].db= MYSQL_SCHEMA_NAME; tz_tables[0].lock_type= TL_READ; tz_init_table_list(tz_tables+1); diff --git a/sql/tztime.h b/sql/tztime.h index eb7d85c48b2..7ffc36011e1 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -89,6 +89,5 @@ extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t); static const int MY_TZ_TABLES_COUNT= 4; - #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ #endif /* TZTIME_INCLUDED */ diff --git a/sql/udf_example.c b/sql/udf_example.c index 98700953b2c..afe3c5592bc 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -125,7 +125,7 @@ typedef unsigned long long ulonglong; typedef long long longlong; #endif /*__WIN__*/ #else -#include <my_global.h> +#include "mariadb.h" #include <my_sys.h> #if defined(MYSQL_SERVER) #include <m_string.h> /* To get strmov() */ @@ -142,6 +142,10 @@ typedef long long longlong; #ifdef _WIN32 /* inet_aton needs winsock library */ #pragma comment(lib, "ws2_32") +#if _MSC_VER +/* Silence warning about deprecated functions , gethostbyname etc*/ +#pragma warning(disable : 4996) +#endif #endif #ifdef HAVE_DLOPEN diff --git a/sql/uniques.cc b/sql/uniques.cc index 7def2d79ad7..8ed1ceda6a1 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -31,7 +31,7 @@ deletes in disk order. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_sort.h" @@ -312,7 +312,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size, { size_t max_elements_in_tree; size_t last_tree_elems; - int n_full_trees; /* number of trees in unique - 1 */ + size_t n_full_trees; /* number of trees in unique - 1 */ double result; max_elements_in_tree= ((size_t) max_in_memory_size / @@ -350,9 +350,9 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size, /* Cost of merge */ if (intersect_fl) key_size+= sizeof(element_count); - double merge_cost= get_merge_many_buffs_cost(buffer, n_full_trees, - max_elements_in_tree, - last_tree_elems, key_size, + double merge_cost= get_merge_many_buffs_cost(buffer, (uint)n_full_trees, + (uint)max_elements_in_tree, + (uint)last_tree_elems, key_size, compare_factor); result += merge_cost; /* @@ -367,7 +367,7 @@ double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size, Unique::~Unique() { close_cached_file(&file); - delete_tree(&tree); + delete_tree(&tree, 0); delete_dynamic(&file_ptrs); } @@ -387,7 +387,7 @@ bool Unique::flush() (void*) this, left_root_right) || insert_dynamic(&file_ptrs, (uchar*) &file_ptr)) return 1; - delete_tree(&tree); + delete_tree(&tree, 0); return 0; } diff --git a/sql/unireg.cc b/sql/unireg.cc index 1342e4c2b66..7bb08cfbf7b 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -25,7 +25,7 @@ str is a (long) to record position where 0 is the first position. */ -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include "unireg.h" #include "sql_partition.h" // struct partition_info @@ -87,7 +87,69 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str)); } -/* +static uchar *extra2_write_field_properties(uchar *pos, + List<Create_field> &create_fields) +{ + List_iterator<Create_field> it(create_fields); + *pos++= EXTRA2_FIELD_FLAGS; + /* + always first 2 for field visibility + */ + pos= extra2_write_len(pos, create_fields.elements); + while (Create_field *cf= it++) + { + uchar flags= cf->invisible; + if (cf->flags & VERS_UPDATE_UNVERSIONED_FLAG) + flags|= VERS_OPTIMIZED_UPDATE; + *pos++= flags; + } + return pos; +} + +static const bool ROW_START = true; +static const bool ROW_END = false; + +static inline +uint16 +vers_get_field(HA_CREATE_INFO *create_info, List<Create_field> &create_fields, bool row_start) +{ + DBUG_ASSERT(create_info->versioned()); + + List_iterator<Create_field> it(create_fields); + Create_field *sql_field = NULL; + + const LString_i row_field= row_start ? create_info->vers_info.as_row.start + : create_info->vers_info.as_row.end; + DBUG_ASSERT(row_field); + + for (unsigned field_no = 0; (sql_field = it++); ++field_no) + { + if (row_field == sql_field->field_name) + { + DBUG_ASSERT(field_no <= uint16(~0U)); + return uint16(field_no); + } + } + + DBUG_ASSERT(0); /* Not Reachable */ + return 0; +} + +static inline +bool has_extra2_field_flags(List<Create_field> &create_fields) +{ + List_iterator<Create_field> it(create_fields); + while (Create_field *f= it++) + { + if (f->invisible) + return true; + if (f->flags & VERS_UPDATE_UNVERSIONED_FLAG) + return true; + } + return false; +} + +/** Create a frm (table definition) file @param thd Thread handler @@ -102,7 +164,7 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, or null LEX_CUSTRING (str==0) in case of an error. */ -LEX_CUSTRING build_frm_image(THD *thd, const char *table, +LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table, HA_CREATE_INFO *create_info, List<Create_field> &create_fields, uint keys, KEY *key_info, handler *db_file) @@ -110,12 +172,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, LEX_CSTRING str_db_type; uint reclength, key_info_length, i; ulong key_buff_length; - ulong filepos, data_offset; + size_t filepos; + ulong data_offset; uint options_len; uint gis_extra2_len= 0; uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0); - int error; + bool error; uchar *frm_ptr, *pos; LEX_CUSTRING frm= {0,0}; StringBuffer<MAX_FIELD_WIDTH> vcols; @@ -138,7 +201,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, create_info->expression_length= vcols.length() + FRM_VCOL_NEW_BASE_SIZE; error= pack_header(thd, forminfo, create_fields, create_info, - data_offset, db_file); + (ulong)data_offset, db_file); if (error) DBUG_RETURN(frm); @@ -147,7 +210,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, /* Calculate extra data segment length */ str_db_type= *hton_name(create_info->db_type); /* str_db_type */ - create_info->extra_size= (2 + str_db_type.length + + create_info->extra_size= (uint)(2 + str_db_type.length + 2 + create_info->connect_string.length); /* Partition: @@ -158,12 +221,12 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, */ create_info->extra_size+= 6; if (part_info) - create_info->extra_size+= part_info->part_info_len; + create_info->extra_size+= (uint)part_info->part_info_len; for (i= 0; i < keys; i++) { if (key_info[i].parser_name) - create_info->extra_size+= key_info[i].parser_name->length + 1; + create_info->extra_size+= (uint)key_info[i].parser_name->length + 1; } options_len= engine_table_options_frm_length(create_info->option_list, @@ -175,8 +238,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, DBUG_PRINT("info", ("Options length: %u", options_len)); if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN, - ER_TOO_LONG_TABLE_COMMENT, - table)) + ER_TOO_LONG_TABLE_COMMENT, table->str)) DBUG_RETURN(frm); /* If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes, @@ -186,7 +248,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (create_info->comment.length > TABLE_COMMENT_INLINE_MAXLEN) { forminfo[46]=255; - create_info->extra_size+= 2 + create_info->comment.length; + create_info->extra_size+= 2 + (uint)create_info->comment.length; } else { @@ -210,7 +272,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info); /* one byte for a type, one or three for a length */ - uint extra2_size= 1 + 1 + create_info->tabledef_version.length; + size_t extra2_size= 1 + 1 + create_info->tabledef_version.length; if (options_len) extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len; @@ -220,6 +282,22 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (gis_extra2_len) extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; + if (create_info->versioned()) + { + extra2_size+= 1 + 1 + 2 * sizeof(uint16); + } + + if (create_info->vtmd()) + { + extra2_size+= 1 + 1 + 1; + } + + bool has_extra2_field_flags_= has_extra2_field_flags(create_fields); + if (has_extra2_field_flags_) + { + extra2_size+= 1 + (create_fields.elements > 255 ? 3 : 1) + + create_fields.elements; + } key_buff_length= uint4korr(fileinfo+47); @@ -240,7 +318,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (frm.length > FRM_MAX_SIZE || create_info->expression_length > UINT_MAX32) { - my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table); + my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table->str); DBUG_RETURN(frm); } @@ -276,6 +354,26 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, } #endif /*HAVE_SPATIAL*/ + if (create_info->versioned()) + { + *pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME; + *pos++= 2 * sizeof(uint16); + int2store(pos, vers_get_field(create_info, create_fields, ROW_START)); + pos+= sizeof(uint16); + int2store(pos, vers_get_field(create_info, create_fields, ROW_END)); + pos+= sizeof(uint16); + } + + if (create_info->vtmd()) + { + *pos++= EXTRA2_VTMD; + *pos++= 1; + *pos++= 1; + } + + if (has_extra2_field_flags_) + pos= extra2_write_field_properties(pos, create_fields); + int4store(pos, filepos); // end of the extra2 segment pos+= 4; @@ -286,7 +384,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, my_printf_error(ER_CANT_CREATE_TABLE, "Cannot create table %`s: index information is too long. " "Decrease number of indexes or use shorter index names or shorter comments.", - MYF(0), table); + MYF(0), table->str); goto err; } @@ -487,7 +585,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, *pos++=(uchar) NAMES_SEP_CHAR; for (key=keyinfo ; key != end ; key++) { - uchar *tmp=(uchar*) strmov((char*) pos,key->name); + uchar *tmp=(uchar*) strmov((char*) pos,key->name.str); *tmp++= (uchar) NAMES_SEP_CHAR; *tmp=0; pos=tmp; @@ -548,7 +646,7 @@ static bool pack_expression(String *buf, Virtual_column_info *vcol, size_t len_off= buf->length(); buf->q_append2b(0); // to be added later buf->q_append((char)vcol->name.length); - buf->q_append(vcol->name.str, vcol->name.length); + buf->q_append(&vcol->name); size_t expr_start= buf->length(); vcol->print(buf); size_t expr_len= buf->length() - expr_start; @@ -673,10 +771,10 @@ static bool pack_header(THD *thd, uchar *forminfo, { char *dst; const char *src= field->save_interval->type_names[pos]; - uint hex_length; + size_t hex_length; length= field->save_interval->type_lengths[pos]; hex_length= length * 2; - field->interval->type_lengths[pos]= hex_length; + field->interval->type_lengths[pos]= (uint)hex_length; field->interval->type_names[pos]= dst= (char*) thd->alloc(hex_length + 1); octet2hex(dst, src, length); @@ -800,7 +898,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, ulong data_offset) { uchar *buff= *buff_arg; - uint int_count, comment_length= 0; + uint int_count; + size_t comment_length= 0; Create_field *field; DBUG_ENTER("pack_fields"); @@ -969,7 +1068,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, field->unireg_check, field->save_interval ? field->save_interval : field->interval, - &field->field_name); + &field->field_name, + field->flags); if (!regfield) { error= 1; diff --git a/sql/unireg.h b/sql/unireg.h index b0cfb3841ef..277a1e3fb40 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -49,11 +49,14 @@ #define DEFAULT_ERRMSGS my_default_lc_messages->errmsgs->errmsgs #define CURRENT_THD_ERRMSGS (current_thd)->variables.errmsgs +#ifndef mysqld_error_find_printf_error_used #define ER_DEFAULT(X) DEFAULT_ERRMSGS[((X)-ER_ERROR_FIRST) / ERRORS_PER_RANGE][(X)% ERRORS_PER_RANGE] #define ER_THD(thd,X) ((thd)->variables.errmsgs[((X)-ER_ERROR_FIRST) / ERRORS_PER_RANGE][(X) % ERRORS_PER_RANGE]) #define ER(X) ER_THD(current_thd, (X)) +#endif #define ER_THD_OR_DEFAULT(thd,X) ((thd) ? ER_THD(thd, (X)) : ER_DEFAULT(X)) + #define ME_INFO (ME_HOLDTANG | ME_NOREFRESH) #define ME_ERROR (ME_BELL | ME_NOREFRESH) #define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */ @@ -172,17 +175,25 @@ enum extra2_frm_value_type { EXTRA2_TABLEDEF_VERSION=0, EXTRA2_DEFAULT_PART_ENGINE=1, EXTRA2_GIS=2, + EXTRA2_PERIOD_FOR_SYSTEM_TIME=4, + EXTRA2_VTMD=8, #define EXTRA2_ENGINE_IMPORTANT 128 EXTRA2_ENGINE_TABLEOPTS=128, + EXTRA2_FIELD_FLAGS=129 +}; + +enum extra2_field_flags { + VERS_OPTIMIZED_UPDATE= 1 << INVISIBLE_MAX_BITS, + VERS_HIDDEN= 1 << (INVISIBLE_MAX_BITS + 1), }; int rea_create_table(THD *thd, LEX_CUSTRING *frm, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, handler *file, bool no_ha_create_table); -LEX_CUSTRING build_frm_image(THD *thd, const char *table, +LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table, HA_CREATE_INFO *create_info, List<Create_field> &create_fields, uint keys, KEY *key_info, handler *db_file); diff --git a/sql/vers_string.h b/sql/vers_string.h new file mode 100644 index 00000000000..0760613cd89 --- /dev/null +++ b/sql/vers_string.h @@ -0,0 +1,148 @@ +/* + 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef VERS_STRING_INCLUDED +#define VERS_STRING_INCLUDED + +struct Compare_strncmp +{ + int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const + { + return strncmp(a.str, b.str, a.length); + } + static CHARSET_INFO* charset() + { + return system_charset_info; + } +}; + +template <CHARSET_INFO* &CS= system_charset_info> +struct Compare_my_strcasecmp +{ + int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const + { + DBUG_ASSERT(a.str[a.length] == 0 && b.str[b.length] == 0); + return my_strcasecmp(CS, a.str, b.str); + } + static CHARSET_INFO* charset() + { + return CS; + } +}; + +typedef Compare_my_strcasecmp<files_charset_info> Compare_fs; +typedef Compare_my_strcasecmp<table_alias_charset> Compare_t; + +template <class Storage= LEX_CSTRING> +struct LEX_STRING_u : public Storage +{ + LEX_STRING_u() + { + Storage::str= NULL; + Storage::length= 0; + } + LEX_STRING_u(const char *_str, size_t _len, CHARSET_INFO *) + { + Storage::str= _str; + Storage::length= _len; + } + uint32 length() const + { + return (uint32)Storage::length; + } + const char *ptr() const + { + return Storage::str; + } + void set(const char *_str, size_t _len, CHARSET_INFO *) + { + Storage::str= _str; + Storage::length= _len; + } + const LEX_CSTRING& lex_cstring() const + { + return *this; + } + const LEX_STRING& lex_string() const + { + return *(LEX_STRING *)this; + } +}; + +template <class Compare= Compare_strncmp, class Storage= LEX_STRING_u<> > +struct XString : public Storage +{ +public: + XString() {} + XString(const char *_str, size_t _len) : + Storage(_str, _len, Compare::charset()) + { + } + XString(const LEX_STRING src) : + Storage(src.str, src.length, Compare::charset()) + { + } + XString(const LEX_CSTRING src) : + Storage(src.str, src.length, Compare::charset()) + { + } + XString(const char *_str) : + Storage(_str, strlen(_str), Compare::charset()) + { + } + bool operator== (const XString& b) const + { + return Storage::length() == b.length() && 0 == Compare()(this->lex_cstring(), b.lex_cstring()); + } + bool operator!= (const XString& b) const + { + return !(*this == b); + } + operator const char* () const + { + return Storage::ptr(); + } + operator LEX_CSTRING& () const + { + return this->lex_cstring(); + } + operator LEX_STRING () const + { + LEX_STRING res; + res.str= const_cast<char *>(this->ptr()); + res.length= this->length(); + return res; + } + operator bool () const + { + return Storage::ptr() != NULL; + } +}; + +typedef XString<> LString; +typedef XString<Compare_fs> LString_fs; +typedef XString<Compare_my_strcasecmp<> > LString_i; + +typedef XString<Compare_strncmp, String> SString; +typedef XString<Compare_fs, String> SString_fs; +typedef XString<Compare_t, String> SString_t; + + +#define XSTRING_WITH_LEN(X) (X).ptr(), (X).length() +#define DB_WITH_LEN(X) (X).db.str, (X).db.length +#define TABLE_NAME_WITH_LEN(X) (X).table_name.str, (X).table_name.length + +#endif // VERS_STRING_INCLUDED diff --git a/sql/vers_utils.h b/sql/vers_utils.h new file mode 100644 index 00000000000..52d01e36e8c --- /dev/null +++ b/sql/vers_utils.h @@ -0,0 +1,81 @@ +#ifndef VERS_UTILS_INCLUDED +#define VERS_UTILS_INCLUDED + +#include "table.h" +#include "sql_class.h" +#include "vers_string.h" + +class MDL_auto_lock +{ + THD *thd; + TABLE_LIST &table; + bool error; + +public: + MDL_auto_lock(THD *_thd, TABLE_LIST &_table) : + thd(_thd), table(_table) + { + DBUG_ASSERT(thd); + MDL_request protection_request; + if (thd->global_read_lock.can_acquire_protection()) + { + error= true; + return; + } + protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_EXPLICIT); + error= thd->mdl_context.acquire_lock(&protection_request, thd->variables.lock_wait_timeout); + if (error) + return; + + table.mdl_request.init(MDL_key::TABLE, table.db.str, table.table_name.str, MDL_EXCLUSIVE, MDL_EXPLICIT); + error= thd->mdl_context.acquire_lock(&table.mdl_request, thd->variables.lock_wait_timeout); + thd->mdl_context.release_lock(protection_request.ticket); + } + ~MDL_auto_lock() + { + if (!error) + { + DBUG_ASSERT(table.mdl_request.ticket); + thd->mdl_context.release_lock(table.mdl_request.ticket); + table.mdl_request.ticket= NULL; + } + } + bool acquire_error() const { return error; } +}; + + +class Local_da : public Diagnostics_area +{ + THD *thd; + uint sql_error; + Diagnostics_area *saved_da; + +public: + Local_da(THD *_thd, uint _sql_error= 0) : + Diagnostics_area(_thd->query_id, false, true), + thd(_thd), + sql_error(_sql_error), + saved_da(_thd->get_stmt_da()) + { + thd->set_stmt_da(this); + } + ~Local_da() + { + if (saved_da) + finish(); + } + void finish() + { + DBUG_ASSERT(saved_da && thd); + thd->set_stmt_da(saved_da); + if (is_error()) + my_error(sql_error ? sql_error : sql_errno(), MYF(0), message()); + if (warn_count() > error_count()) + saved_da->copy_non_errors_from_wi(thd, get_warning_info()); + saved_da= NULL; + } +}; + + +#endif // VERS_UTILS_INCLUDED diff --git a/sql/vtmd.cc b/sql/vtmd.cc new file mode 100644 index 00000000000..4ee43d61111 --- /dev/null +++ b/sql/vtmd.cc @@ -0,0 +1,683 @@ +#include "vtmd.h" +#include "sql_base.h" +#include "sql_class.h" +#include "sql_handler.h" // mysql_ha_rm_tables() +#include "sql_table.h" +#include "sql_select.h" +#include "table_cache.h" // tdc_remove_table() +#include "key.h" +#include "sql_show.h" +#include "sql_parse.h" +#include "sql_lex.h" +#include "sp_head.h" +#include "sp_rcontext.h" + +LString VERS_VTMD_TEMPLATE(C_STRING_WITH_LEN("vtmd_template")); + +bool +VTMD_table::create(THD *thd) +{ + Table_specification_st create_info; + TABLE_LIST src_table, table; + create_info.init(DDL_options_st::OPT_LIKE); + create_info.options|= HA_VTMD; + create_info.alias.str= vtmd_name.ptr(); + create_info.alias.length= vtmd_name.length(); + table.init_one_table(&about.db, &create_info.alias, NULL, TL_READ); + src_table.init_one_table(&MYSQL_SCHEMA_NAME, &VERS_VTMD_TEMPLATE, + &VERS_VTMD_TEMPLATE, TL_READ); + + Query_tables_backup backup(thd); + thd->lex->add_to_query_tables(&src_table); + + MDL_auto_lock mdl_lock(thd, table); + if (mdl_lock.acquire_error()) + return true; + + Reprepare_observer *reprepare_observer= thd->m_reprepare_observer; + partition_info *work_part_info= thd->work_part_info; + thd->m_reprepare_observer= NULL; + thd->work_part_info= NULL; + bool rc= mysql_create_like_table(thd, &table, &src_table, &create_info); + thd->m_reprepare_observer= reprepare_observer; + thd->work_part_info= work_part_info; + return rc; +} + +bool +VTMD_table::find_record(ulonglong row_end, bool &found) +{ + int error; + key_buf_t key; + found= false; + + DBUG_ASSERT(vtmd.table); + + if (key.allocate(vtmd.table->s->max_unique_length)) + return true; + + DBUG_ASSERT(row_end); + vtmd.table->vers_end_field()->set_notnull(); + vtmd.table->vers_end_field()->store(row_end, true); + key_copy(key, vtmd.table->record[0], vtmd.table->key_info + IDX_TRX_END, 0); + + error= vtmd.table->file->ha_index_read_idx_map(vtmd.table->record[1], IDX_TRX_END, + key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); + if (error) + { + if (error == HA_ERR_RECORD_DELETED || error == HA_ERR_KEY_NOT_FOUND) + return false; + vtmd.table->file->print_error(error, MYF(0)); + return true; + } + + restore_record(vtmd.table, record[1]); + + found= true; + return false; +} + + +bool +VTMD_table::open(THD *thd, Local_da &local_da, bool *created) +{ + if (created) + *created= false; + + if (0 == vtmd_name.length() && about.vers_vtmd_name(vtmd_name)) + return true; + + while (true) // max 2 iterations + { + LEX_CSTRING table_name= { vtmd_name.ptr(), vtmd_name.length() }; + vtmd.init_one_table(&about.db, &table_name, NULL, + TL_WRITE_CONCURRENT_INSERT); + + TABLE *res= open_log_table(thd, &vtmd, &open_tables_backup); + if (res) + return false; + + if (created && !*created && local_da.is_error() && + local_da.sql_errno() == ER_NO_SUCH_TABLE) + { + local_da.reset_diagnostics_area(); + if (create(thd)) + break; + *created= true; + } + else + break; + } + return true; +} + +bool +VTMD_table::update(THD *thd, const char* archive_name) +{ + bool result= true; + bool found= false; + bool created; + int error; + size_t an_len= 0; + ulonglong save_thd_options; + { + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + + save_thd_options= thd->variables.option_bits; + thd->variables.option_bits&= ~OPTION_BIN_LOG; + + if (open(thd, local_da, &created)) + goto open_error; + + if (!vtmd.table->versioned()) + { + my_message(ER_VERS_VTMD_ERROR, "VTMD is not versioned", MYF(0)); + goto quit; + } + + if (!created && find_record(ULONGLONG_MAX, found)) + goto quit; + + if ((error= vtmd.table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE))) + { + vtmd.table->file->print_error(error, MYF(0)); + goto quit; + } + + /* Honor next number columns if present */ + vtmd.table->next_number_field= vtmd.table->found_next_number_field; + + if (vtmd.table->s->fields != FIELD_COUNT) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` unexpected fields count: %d", MYF(0), + vtmd.table->s->db.str, vtmd.table->s->table_name.str, vtmd.table->s->fields); + goto quit; + } + + if (archive_name) + { + an_len= strlen(archive_name); + vtmd.table->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_notnull(); + } + else + { + vtmd.table->field[FLD_ARCHIVE_NAME]->set_null(); + } + vtmd.table->field[FLD_COL_RENAMES]->set_null(); + + if (found) + { + if (thd->lex->sql_command == SQLCOM_CREATE_TABLE) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` exists and not empty!", MYF(0), + vtmd.table->s->db.str, vtmd.table->s->table_name.str); + goto quit; + } + vtmd.table->mark_columns_needed_for_update(); // not needed? + if (archive_name) + { + vtmd.table->vers_write= false; + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); + vtmd.table->vers_write= true; + + if (!error) + { + if (thd->lex->sql_command == SQLCOM_DROP_TABLE) + { + error= vtmd.table->file->ha_delete_row(vtmd.table->record[0]); + } + else + { + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_ALTER_TABLE); + ulonglong row_end= (ulonglong) vtmd.table->vers_start_field()->val_int(); + store_record(vtmd.table, record[1]); + vtmd.table->field[FLD_NAME]->store(about.table_name.str, about.table_name.length, system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_null(); + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); + if (error) + goto err; + + DBUG_ASSERT(an_len); + while (true) + { // fill archive_name of last sequential renames + bool found; + if (find_record(row_end, found)) + goto quit; + if (!found || !vtmd.table->field[FLD_ARCHIVE_NAME]->is_null()) + break; + + store_record(vtmd.table, record[1]); + vtmd.table->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_notnull(); + vtmd.table->vers_write= false; + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); + vtmd.table->vers_write= true; + if (error) + goto err; + row_end= (ulonglong) vtmd.table->vers_start_field()->val_int(); + } // while (true) + } // else (thd->lex->sql_command != SQLCOM_DROP_TABLE) + } // if (!error) + } // if (archive_name) + else + { + vtmd.table->field[FLD_NAME]->store(about.table_name.str, + about.table_name.length, + system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], + vtmd.table->record[0]); + } + } // if (found) + else + { + vtmd.table->field[FLD_NAME]->store(about.table_name.str, + about.table_name.length, + system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + vtmd.table->mark_columns_needed_for_insert(); // not needed? + error= vtmd.table->file->ha_write_row(vtmd.table->record[0]); + } + + if (error) + { +err: + vtmd.table->file->print_error(error, MYF(0)); + } + else + result= local_da.is_error(); + } + +quit: + if (!result && vtmd.table->file->ht->prepare_commit_versioned) + { + DBUG_ASSERT(TR_table::use_transaction_registry); // FIXME: disable survival mode while TRT is disabled + TR_table trt(thd, true); + ulonglong trx_start_id= 0; + ulonglong trx_end_id= vtmd.table->file->ht->prepare_commit_versioned(thd, &trx_start_id); + result= trx_end_id && trt.update(trx_start_id, trx_end_id); + } + + close_log_table(thd, &open_tables_backup); + +open_error: + thd->variables.option_bits= save_thd_options; + return result; +} + +bool +VTMD_rename::move_archives(THD *thd, LString &new_db) +{ + int error; + bool rc= false; + SString_fs archive; + bool end_keyread= false; + bool index_end= false; + Open_tables_backup open_tables_backup; + key_buf_t key; + + LEX_CSTRING table_name= { vtmd_name.ptr(), vtmd_name.length() }; + vtmd.init_one_table(&about.db, &table_name, NULL, TL_READ); + + TABLE *res= open_log_table(thd, &vtmd, &open_tables_backup); + if (!res) + return true; + + if (key.allocate(vtmd.table->key_info[IDX_ARCHIVE_NAME].key_length)) + { + close_log_table(thd, &open_tables_backup); + return true; + } + + if ((error= vtmd.table->file->ha_start_keyread(IDX_ARCHIVE_NAME))) + goto err; + end_keyread= true; + + if ((error= vtmd.table->file->ha_index_init(IDX_ARCHIVE_NAME, true))) + goto err; + index_end= true; + + error= vtmd.table->file->ha_index_first(vtmd.table->record[0]); + while (!error) + { + if (!vtmd.table->field[FLD_ARCHIVE_NAME]->is_null()) + { + vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&archive); + key_copy(key, + vtmd.table->record[0], + &vtmd.table->key_info[IDX_ARCHIVE_NAME], + vtmd.table->key_info[IDX_ARCHIVE_NAME].key_length, + false); + error= vtmd.table->file->ha_index_read_map( + vtmd.table->record[0], + key, + vtmd.table->key_info[IDX_ARCHIVE_NAME].ext_key_part_map, + HA_READ_PREFIX_LAST); + if (!error) + { + if ((rc= move_table(thd, archive, new_db))) + break; + + error= vtmd.table->file->ha_index_next(vtmd.table->record[0]); + } + } + else + { + archive.length(0); + error= vtmd.table->file->ha_index_next(vtmd.table->record[0]); + } + } + + if (error && error != HA_ERR_END_OF_FILE) + { +err: + vtmd.table->file->print_error(error, MYF(0)); + rc= true; + } + + if (index_end) + vtmd.table->file->ha_index_end(); + if (end_keyread) + vtmd.table->file->ha_end_keyread(); + + close_log_table(thd, &open_tables_backup); + return rc; +} + +bool +VTMD_rename::move_table(THD *thd, SString_fs &table_name, LString &new_db) +{ + handlerton *table_hton= NULL; + LEX_CSTRING tbl_name= { table_name.ptr(), table_name.length() }; + LEX_CSTRING db_name= { new_db.ptr(), new_db.length() }; + if (!ha_table_exists(thd, &about.db, &tbl_name, &table_hton) || !table_hton) + { + push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "`%s.%s` archive doesn't exist", + about.db.str, tbl_name.str); + return false; + } + + if (ha_table_exists(thd, &db_name, &tbl_name)) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` archive already exists!", + MYF(0), + db_name.str, tbl_name.str); + return true; + } + + TABLE_LIST tl; + tl.init_one_table(&about.db, &tbl_name, NULL, TL_WRITE_ONLY); + tl.mdl_request.set_type(MDL_EXCLUSIVE); + + mysql_ha_rm_tables(thd, &tl); + if (lock_table_names(thd, &tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, about.db.str, table_name, false); + + bool rc= mysql_rename_table(table_hton, + &about.db, &tbl_name, &db_name, &tbl_name, + NO_FK_CHECKS); + if (!rc) + query_cache_invalidate3(thd, &tl, 0); + + return rc; +} + +bool +VTMD_rename::try_rename(THD *thd, LString new_db, LString new_alias, const char *archive_name) +{ + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + TABLE_LIST new_table; + + if (check_exists(thd)) + return true; + + LEX_CSTRING new_db_name= { XSTRING_WITH_LEN(new_db) }; + LEX_CSTRING new_tbl_name= { XSTRING_WITH_LEN(new_alias) }; + + new_table.init_one_table(&new_db_name, &new_tbl_name, NULL, TL_READ); + + if (new_table.vers_vtmd_name(vtmd_new_name)) + return true; + + LEX_CSTRING new_name= { vtmd_new_name.ptr(), vtmd_new_name.length() }; + + if (ha_table_exists(thd, &new_db_name, &new_name)) + { + if (exists) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` table already exists!", + MYF(0), + new_db_name.str, new_name.str); + return true; + } + push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "`%s.%s` table already exists!", + new_db_name.str, new_name.str); + return false; + } + + if (!exists) + return false; + + bool same_db= true; + if (LString_fs(DB_WITH_LEN(about)) != LString_fs(new_db)) + { + // Move archives before VTMD so if the operation is interrupted, it could be continued. + if (move_archives(thd, new_db)) + return true; + same_db= false; + } + + TABLE_LIST vtmd_tl; + LEX_CSTRING table_name= { vtmd_name.ptr(), vtmd_name.length() }; + vtmd_tl.init_one_table(&about.db, &table_name, NULL, TL_WRITE_ONLY); + vtmd_tl.mdl_request.set_type(MDL_EXCLUSIVE); + + mysql_ha_rm_tables(thd, &vtmd_tl); + if (lock_table_names(thd, &vtmd_tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, about.db.str, vtmd_name, false); + if (local_da.is_error()) // just safety check + return true; + bool rc= mysql_rename_table(hton, &about.db, &table_name, + &new_db_name, &new_name, + NO_FK_CHECKS); + if (!rc) + { + query_cache_invalidate3(thd, &vtmd_tl, 0); + if (same_db || archive_name || + new_alias != LString(TABLE_NAME_WITH_LEN(about))) + { + local_da.finish(); + VTMD_table new_vtmd(new_table); + rc= new_vtmd.update(thd, archive_name); + } + } + return rc; +} + +bool +VTMD_rename::revert_rename(THD *thd, LString new_db) +{ + DBUG_ASSERT(hton); + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + + TABLE_LIST vtmd_tl; + LEX_CSTRING new_name= { XSTRING_WITH_LEN(vtmd_new_name) }; + LEX_CSTRING v_name= { XSTRING_WITH_LEN(vtmd_name) }; + LEX_CSTRING new_db_name= { XSTRING_WITH_LEN(new_db) }; + + vtmd_tl.init_one_table(&about.db, &new_name, NULL, TL_WRITE_ONLY); + vtmd_tl.mdl_request.set_type(MDL_EXCLUSIVE); + mysql_ha_rm_tables(thd, &vtmd_tl); + if (lock_table_names(thd, &vtmd_tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, new_db, vtmd_new_name, false); + + bool rc= mysql_rename_table(hton, &new_db_name, &new_name, + &new_db_name, &v_name, + NO_FK_CHECKS); + + if (!rc) + query_cache_invalidate3(thd, &vtmd_tl, 0); + + return rc; +} + +void +VTMD_table::archive_name( + THD* thd, + const char* table_name, + char* new_name, + size_t new_name_size) +{ + const MYSQL_TIME now= thd->query_start_TIME(); + my_snprintf(new_name, new_name_size, "%s_%04d%02d%02d_%02d%02d%02d_%06lu", + table_name, now.year, now.month, now.day, now.hour, now.minute, + now.second, (ulong) now.second_part); +} + +bool +VTMD_table::find_archive_name(THD *thd, String &out) +{ + READ_RECORD info; + int error; + SQL_SELECT *select= NULL; + COND *conds= NULL; + List<TABLE_LIST> dummy; + SELECT_LEX &select_lex= thd->lex->select_lex; + + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + if (open(thd, local_da)) + return true; + + Name_resolution_context &ctx= thd->lex->select_lex.context; + TABLE_LIST *table_list= ctx.table_list; + TABLE_LIST *first_name_resolution_table= ctx.first_name_resolution_table; + table_map map = vtmd.table->map; + ctx.table_list= &vtmd; + ctx.first_name_resolution_table= &vtmd; + vtmd.table->map= 1; + + vtmd.vers_conditions= about.vers_conditions; + if ((error= select_lex.vers_setup_conds(thd, &vtmd, &conds)) || + (error= setup_conds(thd, &vtmd, dummy, &conds))) + goto err; + + select= make_select(vtmd.table, 0, 0, conds, NULL, 0, &error); + if (error) + goto loc_err; + + error= init_read_record(&info, thd, vtmd.table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + if (error) + goto loc_err; + + while (!(error= info.read_record()) && !thd->killed && !thd->is_error()) + { + if (!select || select->skip_record(thd) > 0) + { + vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&out); + break; + } + } + + if (error < 0) + my_error(ER_NO_SUCH_TABLE, MYF(0), about.db.str, about.alias.str); + +loc_err: + end_read_record(&info); +err: + delete select; + ctx.table_list= table_list; + ctx.first_name_resolution_table= first_name_resolution_table; + vtmd.table->map= map; + close_log_table(thd, &open_tables_backup); + DBUG_ASSERT(!error || local_da.is_error()); + return error; +} + +static +bool +get_vtmd_tables(THD *thd, const char *db, + size_t db_length, Dynamic_array<LEX_CSTRING *> &table_names) +{ + LOOKUP_FIELD_VALUES lookup_field_values= { + {db, db_length}, {C_STRING_WITH_LEN("%_vtmd")}, false, true}; + + int res= make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, + &lookup_field_values.db_value); + + return res; +} + +bool +VTMD_table::get_archive_tables(THD *thd, const char *db, size_t db_length, + Dynamic_array<String> &result) +{ + Dynamic_array<LEX_CSTRING *> vtmd_tables; + if (get_vtmd_tables(thd, db, db_length, vtmd_tables)) + return true; + + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + for (uint i= 0; i < vtmd_tables.elements(); i++) + { + LEX_CSTRING table_name= *vtmd_tables.at(i); + Open_tables_backup open_tables_backup; + TABLE_LIST table_list; + LEX_CSTRING db_name= {db, db_length}; + table_list.init_one_table(&db_name, &table_name, NULL, TL_READ); + + TABLE *table= open_log_table(thd, &table_list, &open_tables_backup); + if (!table || !table->vers_vtmd()) + { + if (table) + close_log_table(thd, &open_tables_backup); + else + { + if (local_da.is_error() && local_da.sql_errno() == ER_NOT_LOG_TABLE) + local_da.reset_diagnostics_area(); + else + return true; + } + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "Table `%s.%s` is not a VTMD table", + db, table_name.str); + continue; + } + + READ_RECORD read_record; + int error= 0; + SQL_SELECT *sql_select= make_select(table, 0, 0, NULL, NULL, 0, &error); + if (error) + { + close_log_table(thd, &open_tables_backup); + return true; + } + error= init_read_record(&read_record, thd, table, sql_select, NULL, 1, 1, false); + if (error) + { + delete sql_select; + close_log_table(thd, &open_tables_backup); + return true; + } + + while (!(error= read_record.read_record())) + { + Field *field= table->field[FLD_ARCHIVE_NAME]; + if (field->is_null()) + continue; + + String archive_name; + field->val_str(&archive_name); + archive_name.set_ascii(strmake_root(thd->mem_root, archive_name.c_ptr(), + archive_name.length()), + archive_name.length()); + result.push(archive_name); + } + // check for EOF + if (!thd->is_error()) + error= 0; + + end_read_record(&read_record); + delete sql_select; + close_log_table(thd, &open_tables_backup); + } + + return false; +} + +bool VTMD_table::setup_select(THD* thd) +{ + SString archive_name; + if (find_archive_name(thd, archive_name)) + return true; + + if (archive_name.length() == 0) + return false; + + thd->make_lex_string(&about.table_name, archive_name.ptr(), + archive_name.length()); + DBUG_ASSERT(!about.mdl_request.ticket); + about.mdl_request.init(MDL_key::TABLE, about.db.str, about.table_name.str, + about.mdl_request.type, about.mdl_request.duration); + about.vers_force_alias= true; + // Since we modified SELECT_LEX::table_list, we need to invalidate current SP + if (thd->spcont) + { + DBUG_ASSERT(thd->spcont->m_sp); + thd->spcont->m_sp->set_sp_cache_version(0); + } + return false; +} diff --git a/sql/vtmd.h b/sql/vtmd.h new file mode 100644 index 00000000000..ea5450a8841 --- /dev/null +++ b/sql/vtmd.h @@ -0,0 +1,190 @@ +#ifndef VTMD_INCLUDED +#define VTMD_INCLUDED + +#include <mysqld_error.h> + +#include "mariadb.h" +#include "sql_priv.h" + +#include "my_sys.h" +#include "table.h" +#include "unireg.h" + +#include "vers_utils.h" + +class key_buf_t +{ + uchar* buf; + + key_buf_t(const key_buf_t&); // disabled + key_buf_t& operator= (const key_buf_t&); // disabled + +public: + key_buf_t() : buf(NULL) + {} + + ~key_buf_t() + { + if (buf) + my_free(buf); + } + + bool allocate(size_t alloc_size) + { + DBUG_ASSERT(!buf); + buf= static_cast<uchar *>(my_malloc(alloc_size, MYF(0))); + if (!buf) + { + my_message(ER_VERS_VTMD_ERROR, "failed to allocate key buffer", MYF(0)); + return true; + } + return false; + } + + operator uchar* () + { + DBUG_ASSERT(buf); + return reinterpret_cast<uchar *>(buf); + } +}; + +class THD; + +class VTMD_table +{ + Open_tables_backup open_tables_backup; + +protected: + TABLE_LIST vtmd; + TABLE_LIST &about; + SString_t vtmd_name; + +private: + VTMD_table(const VTMD_table&); // prohibit copying references + +public: + enum { + FLD_START= 0, + FLD_END, + FLD_NAME, + FLD_ARCHIVE_NAME, + FLD_COL_RENAMES, + FIELD_COUNT + }; + + enum { + IDX_TRX_END= 0, + IDX_ARCHIVE_NAME + }; + + VTMD_table(TABLE_LIST &_about) : + about(_about) + { + vtmd.table= NULL; + } + + bool create(THD *thd); + bool find_record(ulonglong row_end, bool &found); + bool open(THD *thd, Local_da &local_da, bool *created= NULL); + bool update(THD *thd, const char* archive_name= NULL); + bool setup_select(THD *thd); + + static void archive_name(THD *thd, const char *table_name, char *new_name, size_t new_name_size); + void archive_name(THD *thd, char *new_name, size_t new_name_size) + { + archive_name(thd, about.table_name.str, new_name, new_name_size); + } + + bool find_archive_name(THD *thd, String &out); + static bool get_archive_tables(THD *thd, const char *db, size_t db_length, + Dynamic_array<String> &result); +}; + +class VTMD_exists : public VTMD_table +{ +protected: + handlerton *hton; + +public: + bool exists; + +public: + VTMD_exists(TABLE_LIST &_about) : + VTMD_table(_about), + hton(NULL), + exists(false) + {} + + bool check_exists(THD *thd); // returns error status +}; + +class VTMD_rename : public VTMD_exists +{ + SString_t vtmd_new_name; + +public: + VTMD_rename(TABLE_LIST &_about) : + VTMD_exists(_about) + {} + + bool try_rename(THD *thd, LString new_db, LString new_alias, const char* archive_name= NULL); + bool revert_rename(THD *thd, LString new_db); + +private: + bool move_archives(THD *thd, LString &new_db); + bool move_table(THD *thd, SString_fs &table_name, LString &new_db); +}; + +class VTMD_drop : public VTMD_exists +{ + char archive_name_[NAME_CHAR_LEN]; + +public: + VTMD_drop(TABLE_LIST &_about) : + VTMD_exists(_about) + { + *archive_name_= 0; + } + + const char* archive_name(THD *thd) + { + VTMD_table::archive_name(thd, archive_name_, sizeof(archive_name_)); + return archive_name_; + } + + const char* archive_name() const + { + DBUG_ASSERT(*archive_name_); + return archive_name_; + } + + bool update(THD *thd) + { + DBUG_ASSERT(*archive_name_); + return VTMD_exists::update(thd, archive_name_); + } +}; + + +inline +bool +VTMD_exists::check_exists(THD *thd) +{ + LEX_CSTRING name; + if (about.vers_vtmd_name(vtmd_name)) + return true; + + name.str= vtmd_name.ptr(); + name.length= vtmd_name.length(); + exists= ha_table_exists(thd, &about.db, &name, &hton); + + if (exists && !hton) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` handlerton empty!", MYF(0), + about.db.str, vtmd_name.ptr()); + return true; + } + return false; +} + +#endif // VTMD_INCLUDED diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 958add48b5a..a5c39887cbd 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -13,6 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_priv.h" #include "wsrep_binlog.h" // wsrep_dump_rbr_buf() #include "wsrep_xid.h" diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc index ad5ef9c5c55..4efd6703d03 100644 --- a/sql/wsrep_binlog.cc +++ b/sql/wsrep_binlog.cc @@ -13,6 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_binlog.h" #include "wsrep_priv.h" #include "log.h" @@ -30,13 +31,13 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len) { *buf= NULL; *buf_len= 0; - my_off_t const saved_pos(my_b_tell(cache)); + DBUG_ENTER("wsrep_write_cache_buf"); if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) { WSREP_ERROR("failed to initialize io-cache"); - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } uint length = my_b_bytes_in_cache(cache); @@ -85,7 +86,7 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len) goto cleanup; } - return 0; + DBUG_RETURN(0); error: if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0)) @@ -96,7 +97,7 @@ cleanup: my_free(*buf); *buf= NULL; *buf_len= 0; - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } #define STACK_SIZE 4096 /* 4K - for buffer preallocated on the stack: @@ -120,6 +121,7 @@ wsrep_append_data(wsrep_t* const wsrep, struct wsrep_buf const buff = { data, len }; wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1, WSREP_DATA_ORDERED, true)); + DBUG_DUMP("buff", (uchar*) data, len); if (rc != WSREP_OK) { WSREP_WARN("append_data() returned %d", rc); @@ -143,11 +145,12 @@ static int wsrep_write_cache_once(wsrep_t* const wsrep, size_t* const len) { my_off_t const saved_pos(my_b_tell(cache)); + DBUG_ENTER("wsrep_write_cache_once"); if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) { WSREP_ERROR("failed to initialize io-cache"); - return ER_ERROR_ON_WRITE; + DBUG_RETURN(ER_ERROR_ON_WRITE); } int err(WSREP_OK); @@ -229,7 +232,7 @@ cleanup: } my_free(heap_buf); - return err; + DBUG_RETURN(err); } /* @@ -246,11 +249,12 @@ static int wsrep_write_cache_inc(wsrep_t* const wsrep, size_t* const len) { my_off_t const saved_pos(my_b_tell(cache)); + DBUG_ENTER("wsrep_write_cache_inc"); if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) { WSREP_ERROR("failed to initialize io-cache"); - return WSREP_TRX_ERROR; + DBUG_RETURN(WSREP_TRX_ERROR); } int err(WSREP_OK); @@ -294,7 +298,7 @@ cleanup: WSREP_ERROR("failed to reinitialize io-cache"); } - return err; + DBUG_RETURN(err); } /* diff --git a/sql/wsrep_check_opts.cc b/sql/wsrep_check_opts.cc index 8da791b3429..bf4ce7c9d90 100644 --- a/sql/wsrep_check_opts.cc +++ b/sql/wsrep_check_opts.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "mysqld.h" #include "sys_vars_shared.h" #include "wsrep.h" diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 0aa7f9b0aad..c83f64e9cb4 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -13,7 +13,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <my_global.h> +#include "mariadb.h" #include <sql_class.h> #include <mysql/service_wsrep.h> diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index b0d446ad6ea..8044b7a3548 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include "sql_base.h" #include "rpl_filter.h" @@ -257,8 +258,7 @@ static int wsrep_rollback(handlerton *hton, THD *thd, bool all) { DBUG_PRINT("wsrep", ("setting rollback fail")); WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s", - (long long)thd->real_id, (thd->db ? thd->db : "(null)"), - thd->query()); + (long long)thd->real_id, thd->get_db(), thd->query()); } wsrep_cleanup_transaction(thd); } @@ -299,7 +299,7 @@ int wsrep_commit(handlerton *hton, THD *thd, bool all) { DBUG_PRINT("wsrep", ("setting rollback fail")); WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s", - (long long)thd->real_id, (thd->db ? thd->db : "(null)"), + (long long)thd->real_id, thd->get_db(), thd->query()); } } @@ -373,6 +373,11 @@ wsrep_run_wsrep_commit(THD *thd, bool all) mysql_mutex_lock(&LOCK_wsrep_replaying); + DBUG_PRINT("info", ("wsrep_replaying: %d wsrep_conflict_state: %d killed: %d shutdown_in_progress: %d", + (int) wsrep_replaying, (int) thd->wsrep_conflict_state, + (int) thd->killed, + (int) shutdown_in_progress)); + while (wsrep_replaying > 0 && thd->wsrep_conflict_state == NO_CONFLICT && thd->killed == NOT_KILLED && @@ -436,6 +441,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all) } } + DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d", + rcode, thd->wsrep_conflict_state)); + if (data_len == 0) { if (thd->get_stmt_da()->is_ok() && @@ -467,7 +475,7 @@ wsrep_run_wsrep_commit(THD *thd, bool all) "QUERY: %s\n" " => Skipping replication", (longlong) thd->thread_id, data_len, - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); rcode = WSREP_TRX_FAIL; } else if (!rcode) @@ -481,10 +489,12 @@ wsrep_run_wsrep_commit(THD *thd, bool all) 0ULL : WSREP_FLAG_PA_UNSAFE), &thd->wsrep_trx_meta); + DBUG_PRINT("info", ("rcode after pre_commit: %d", rcode)); + if (rcode == WSREP_TRX_MISSING) { WSREP_WARN("Transaction missing in provider, thd: %lld schema: %s SQL: %s", (longlong) thd->thread_id, - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); rcode = WSREP_TRX_FAIL; } else if (rcode == WSREP_BF_ABORT) { WSREP_DEBUG("thd: %lld seqno: %lld BF aborted by provider, will replay", @@ -511,6 +521,9 @@ wsrep_run_wsrep_commit(THD *thd, bool all) DEBUG_SYNC(thd, "wsrep_after_replication"); + DBUG_PRINT("info", ("rcode: %d wsrep_conflict_state: %d", + rcode, thd->wsrep_conflict_state)); + switch(rcode) { case 0: /* diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 6c5eaf4a60c..91a77c65604 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include <sql_class.h> #include <sql_parse.h> @@ -246,6 +247,17 @@ static void wsrep_log_cb(wsrep_log_level_t level, const char *msg) { } } +void wsrep_log(void (*fun)(const char *, ...), const char *format, ...) +{ + va_list args; + char msg[1024]; + va_start(args, format); + vsnprintf(msg, sizeof(msg) - 1, format, args); + va_end(args); + (fun)("WSREP: %s", msg); +} + + static void wsrep_log_states (wsrep_log_level_t const level, const wsrep_uuid_t* const group_uuid, wsrep_seqno_t const group_seqno, @@ -1164,7 +1176,7 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd, } ka->keys[ka->keys_len].key_parts_num= 2; ++ka->keys_len; - if (!wsrep_prepare_key_for_isolation(table->db, table->table_name, + 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)) { @@ -1306,7 +1318,7 @@ static int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len) if (wsrep_alter_query_string(thd, &log_query)) { WSREP_WARN("events alter string failed: schema: %s, query: %s", - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); return 1; } return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len); @@ -1320,22 +1332,17 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) SELECT_LEX *select_lex= &lex->select_lex; TABLE_LIST *first_table= select_lex->table_list.first; TABLE_LIST *views = first_table; - + LEX_USER *definer; String buff; - const LEX_STRING command[3]= - {{ C_STRING_WITH_LEN("CREATE ") }, - { C_STRING_WITH_LEN("ALTER ") }, - { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; + const LEX_CSTRING command[3]= + {{ STRING_WITH_LEN("CREATE ") }, + { STRING_WITH_LEN("ALTER ") }, + { STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view->mode].str, - command[thd->lex->create_view->mode].length); - - LEX_USER *definer; + buff.append(&command[thd->lex->create_view->mode]); if (lex->definer) - { definer= get_current_user(thd, lex->definer); - } else { /* @@ -1363,25 +1370,23 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) view_store_options(thd, views, &buff); buff.append(STRING_WITH_LEN("VIEW ")); /* Test if user supplied a db (ie: we did not use thd->db) */ - if (views->db && views->db[0] && - (thd->db == NULL || strcmp(views->db, thd->db))) + if (views->db.str && views->db.str[0] && + (thd->db.str == NULL || cmp(&views->db, &thd->db))) { - append_identifier(thd, &buff, views->db, - views->db_length); + append_identifier(thd, &buff, &views->db); buff.append('.'); } - append_identifier(thd, &buff, views->table_name, - views->table_name_length); + append_identifier(thd, &buff, &views->table_name); if (lex->view_list.elements) { - List_iterator_fast<LEX_STRING> names(lex->view_list); - LEX_STRING *name; + List_iterator_fast<LEX_CSTRING> names(lex->view_list); + LEX_CSTRING *name; int i; for (i= 0; (name= names++); i++) { buff.append(i ? ", " : "("); - append_identifier(thd, &buff, name->str, name->length); + append_identifier(thd, &buff, name); } buff.append(')'); } @@ -1467,7 +1472,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, { for (TABLE_LIST* table= first_table; table; table= table->next_global) { - if (!thd->find_temporary_table(table->db, table->table_name)) + if (!thd->find_temporary_table(table->db.str, table->table_name.str)) { return true; } @@ -1551,7 +1556,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. Check wsrep " "connection state and retry the query.", ret, - (thd->db ? thd->db : "(null)"), + thd->get_db(), (thd->query()) ? thd->query() : "void"); my_message(ER_LOCK_DEADLOCK, "WSREP replication failed. Check " "your wsrep connection state and retry the query.", MYF(0)); @@ -1587,7 +1592,7 @@ static void wsrep_TOI_end(THD *thd) { else { WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s", ret, - (thd->db ? thd->db : "(null)"), + thd->get_db(), (thd->query()) ? thd->query() : "void"); } } @@ -1602,7 +1607,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) if (ret != WSREP_OK) { WSREP_WARN("RSU desync failed %d for schema: %s, query: %s", - ret, (thd->db ? thd->db : "(null)"), thd->query()); + ret, thd->get_db(), thd->query()); my_error(ER_LOCK_DEADLOCK, MYF(0)); return(ret); } @@ -1615,8 +1620,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) { /* no can do, bail out from DDL */ WSREP_WARN("RSU failed due to pending transactions, schema: %s, query %s", - (thd->db ? thd->db : "(null)"), - thd->query()); + thd->get_db(), thd->query()); mysql_mutex_lock(&LOCK_wsrep_replaying); wsrep_replaying--; mysql_mutex_unlock(&LOCK_wsrep_replaying); @@ -1625,7 +1629,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) if (ret != WSREP_OK) { WSREP_WARN("resync failed %d for schema: %s, query: %s", - ret, (thd->db ? thd->db : "(null)"), thd->query()); + ret, thd->get_db(), thd->query()); } my_error(ER_LOCK_DEADLOCK, MYF(0)); @@ -1636,8 +1640,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) if (seqno == WSREP_SEQNO_UNDEFINED) { WSREP_WARN("pause failed %lld for schema: %s, query: %s", (long long)seqno, - (thd->db ? thd->db : "(null)"), - thd->query()); + thd->get_db(), thd->query()); return(1); } WSREP_DEBUG("paused at %lld", (long long)seqno); @@ -1660,15 +1663,14 @@ static void wsrep_RSU_end(THD *thd) if (ret != WSREP_OK) { WSREP_WARN("resume failed %d for schema: %s, query: %s", ret, - (thd->db ? thd->db : "(null)"), - thd->query()); + thd->get_db(), thd->query()); } ret = wsrep->resync(wsrep); if (ret != WSREP_OK) { WSREP_WARN("resync failed %d for schema: %s, query: %s", ret, - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); return; } @@ -1691,9 +1693,7 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, if (thd->wsrep_conflict_state == MUST_ABORT) { WSREP_INFO("thread: %lld schema: %s query: %s has been aborted due to multi-master conflict", - (longlong) thd->thread_id, - (thd->db ? thd->db : "(null)"), - thd->query()); + (longlong) thd->thread_id, thd->get_db(), thd->query()); mysql_mutex_unlock(&thd->LOCK_wsrep_thd); return WSREP_TRX_FAIL; } @@ -1938,7 +1938,6 @@ pthread_handler_t start_wsrep_THD(void *arg) close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); - goto error; } @@ -1961,7 +1960,6 @@ pthread_handler_t start_wsrep_THD(void *arg) close_connection(thd, ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_status); MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); - delete thd; goto error; } @@ -2005,13 +2003,8 @@ pthread_handler_t start_wsrep_THD(void *arg) // at server shutdown } - if (thread_handling > SCHEDULER_ONE_THREAD_PER_CONNECTION) - { - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); - delete thd; - } + unlink_not_visible_thd(thd); + delete thd; my_thread_end(); return(NULL); @@ -2339,7 +2332,7 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) saved_mode)) { WSREP_WARN("SP create string failed: schema: %s, query: %s", - (thd->db ? thd->db : "(null)"), thd->query()); + thd->get_db(), thd->query()); return 1; } @@ -2585,8 +2578,8 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, } else if (!(thd->find_temporary_table(src_table))) { - /* this is straight CREATE TABLE LIKE... eith no tmp tables */ - WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL); + /* this is straight CREATE TABLE LIKE... with no tmp tables */ + WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL); } else { @@ -2609,7 +2602,7 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table, thd->wsrep_TOI_pre_query= query.ptr(); thd->wsrep_TOI_pre_query_len= query.length(); - WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL); + WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL); thd->wsrep_TOI_pre_query= NULL; thd->wsrep_TOI_pre_query_len= 0; @@ -2667,11 +2660,10 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len) append_definer(thd, &stmt_query, &definer_user, &definer_host); LEX_CSTRING stmt_definition; - uint not_used; stmt_definition.str= (char*) thd->lex->stmt_definition_begin; stmt_definition.length= thd->lex->stmt_definition_end - thd->lex->stmt_definition_begin; - trim_whitespace(thd->charset(), &stmt_definition, ¬_used); + trim_whitespace(thd->charset(), &stmt_definition); stmt_query.append(stmt_definition.str, stmt_definition.length); @@ -2734,6 +2726,7 @@ void wsrep_unlock_rollback() my_bool wsrep_aborting_thd_contains(THD *thd) { + mysql_mutex_assert_owner(&LOCK_wsrep_rollback); wsrep_aborting_thd_t abortees = wsrep_aborting_thd; while (abortees) { @@ -2746,6 +2739,7 @@ my_bool wsrep_aborting_thd_contains(THD *thd) void wsrep_aborting_thd_enqueue(THD *thd) { + mysql_mutex_assert_owner(&LOCK_wsrep_rollback); wsrep_aborting_thd_t aborting = (wsrep_aborting_thd_t) my_malloc(sizeof(struct wsrep_aborting_thd), MYF(0)); aborting->aborting_thd = thd; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 0502a29527f..6ad13cd6f6a 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -48,7 +48,7 @@ struct wsrep_thd_shadow { enum wsrep_exec_mode wsrep_exec_mode; Vio *vio; ulong tx_isolation; - char *db; + const char *db; size_t db_length; my_hrtime_t user_time; longlong row_count_func; @@ -193,13 +193,8 @@ extern wsrep_seqno_t wsrep_locked_seqno; ? wsrep_forced_binlog_format : (ulong)(my_format)) // prefix all messages with "WSREP" -#define WSREP_LOG(fun, ...) \ - do { \ - char msg[1024] = {'\0'}; \ - snprintf(msg, sizeof(msg) - 1, ## __VA_ARGS__); \ - fun("WSREP: %s", msg); \ - } while(0) - +void wsrep_log(void (*fun)(const char *, ...), const char *format, ...); +#define WSREP_LOG(fun, ...) wsrep_log(fun, ## __VA_ARGS__) #define WSREP_LOG_CONFLICT_THD(thd, role) \ WSREP_LOG(sql_print_information, \ "%s: \n " \ diff --git a/sql/wsrep_notify.cc b/sql/wsrep_notify.cc index 92c685ba485..92bcc8eda43 100644 --- a/sql/wsrep_notify.cc +++ b/sql/wsrep_notify.cc @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include <mysqld.h> #include "wsrep_priv.h" #include "wsrep_utils.h" diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index ba611fed0c7..41044085625 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -13,11 +13,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "mariadb.h" #include "wsrep_sst.h" - #include <mysqld.h> #include <m_ctype.h> -#include <my_sys.h> #include <strfunc.h> #include <sql_class.h> #include <set_var.h> diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 061ff795fc3..e8dda53c95f 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -13,8 +13,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "mariadb.h" #include "wsrep_thd.h" - #include "transaction.h" #include "rpl_rli.h" #include "log_event.h" @@ -164,22 +164,23 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow) thd->variables.tx_isolation = ISO_READ_COMMITTED; thd->tx_isolation = ISO_READ_COMMITTED; - shadow->db = thd->db; - shadow->db_length = thd->db_length; + shadow->db = thd->db.str; + shadow->db_length = thd->db.length; shadow->user_time = thd->user_time; shadow->row_count_func= thd->get_row_count_func(); - thd->reset_db(NULL, 0); + thd->reset_db(&null_clex_str); } static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow) { + LEX_CSTRING db= {shadow->db, shadow->db_length }; thd->variables.option_bits = shadow->options; thd->server_status = shadow->server_status; thd->wsrep_exec_mode = shadow->wsrep_exec_mode; thd->net.vio = shadow->vio; thd->variables.tx_isolation = shadow->tx_isolation; thd->user_time = shadow->user_time; - thd->reset_db(shadow->db, shadow->db_length); + thd->reset_db(&db); delete thd->system_thread_info.rpl_sql_info; delete thd->wsrep_rgi->rli->mi; @@ -322,8 +323,7 @@ void wsrep_replay_transaction(THD *thd) break; default: WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s", - rcode, - (thd->db ? thd->db : "(null)"), + rcode, thd->get_db(), thd->query() ? thd->query() : "void"); /* we're now in inconsistent state, must abort */ diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index 1a358877a35..3c341e222b3 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -20,6 +20,7 @@ #define _GNU_SOURCE // POSIX_SPAWN_USEVFORK flag #endif +#include "mariadb.h" #include "wsrep_utils.h" #include "wsrep_mysqld.h" diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 34c5865548c..c54a5481187 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -605,9 +605,8 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var) if (new_wsrep_desync) { ret = wsrep->desync (wsrep); if (ret != WSREP_OK) { - WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret, - (thd->db ? thd->db : "(null)"), - thd->query()); + WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", + ret, thd->get_db(), thd->query()); my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query()); return true; } @@ -615,8 +614,7 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var) ret = wsrep->resync (wsrep); if (ret != WSREP_OK) { WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret, - (thd->db ? thd->db : "(null)"), - thd->query()); + thd->get_db(), thd->query()); my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query()); return true; } diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc index 2ff6ea0b32e..5af39bfc230 100644 --- a/sql/wsrep_xid.cc +++ b/sql/wsrep_xid.cc @@ -16,6 +16,7 @@ //! @file some utility functions and classes not directly related to replication +#include "mariadb.h" #include "wsrep_xid.h" #include "sql_class.h" #include "wsrep_mysqld.h" // for logging macros |