diff --git a/mysql-test/suite/innodb/r/tmpdir.result b/mysql-test/suite/innodb/r/tmpdir.result new file mode 100644 index 00000000000..f9475c1b7f8 --- /dev/null +++ b/mysql-test/suite/innodb/r/tmpdir.result @@ -0,0 +1,51 @@ +# +# Bug #19183565 CREATE DYNAMIC INNODB_TMPDIR VARIABLE TO CONTROL +# WHERE INNODB WRITES TEMP FILES +# +# If innodb_tmpdir is NULL or "", temporary file will be created in +# server configuration variable location(--tmpdir) +create table t1(a int primary key)engine=innodb; +show session variables like 'innodb_tmpdir'; +Variable_name Value +innodb_tmpdir +alter table t1 add column b int not null; +set global innodb_tmpdir=NULL; +# Connection con1 +show session variables like 'innodb_tmpdir'; +Variable_name Value +innodb_tmpdir +alter table t1 add key(b); +drop table t1; +# innodb_tmpdir with invalid path. +create table t1(a int primary key)engine=innodb; +set global innodb_tmpdir='wrong_value'; +ERROR 42000: Variable 'innodb_tmpdir' can't be set to the value of 'wrong_value' +show warnings; +Level Code Message +Warning 1210 InnoDB: Path doesn't exist. +Error 1231 Variable 'innodb_tmpdir' can't be set to the value of 'wrong_value' +drop table t1; +# innodb_tmpdir with mysql data directory path. +create table t1(a text, b text, fulltext(a,b))engine=innodb; +insert into t1 values('test1', 'test2'); +insert into t1 values('text1', 'text2'); +set global innodb_tmpdir = @@global.datadir; +ERROR 42000: Variable 'innodb_tmpdir' can't be set to the value of 'MYSQL_DATADIR' +show warnings; +Level Code Message +Warning 1210 InnoDB: Path Location should not be same as mysql data directory location. +Error 1231 DATADIR/data/' +drop table t1; +# innodb_tmpdir with valid location. +create table t1(a text, b text, fulltext(a,b))engine=innodb; +insert into t1 values('test1', 'test2'); +insert into t1 values('text1', 'text2'); +set @tmpdir = @@global.tmpdir; +set global innodb_tmpdir = @tmpdir; +show session variables like 'innodb_tmpdir'; +Variable_name Value +innodb_tmpdir +# Connection con3 +alter table t1 add fulltext(b); +set global innodb_tmpdir=NULL; +drop table t1; diff --git a/mysql-test/suite/innodb/t/tmpdir.test b/mysql-test/suite/innodb/t/tmpdir.test new file mode 100644 index 00000000000..76ec83e5af4 --- /dev/null +++ b/mysql-test/suite/innodb/t/tmpdir.test @@ -0,0 +1,68 @@ +--source include/have_innodb.inc +--source include/count_sessions.inc + +if (`select plugin_auth_version <= "5.6.26-MariaDB-76.0" from information_schema.plugins where plugin_name='innodb'`) +{ + --skip Not fixed in XtraDB as of 5.6.26-MariaDB-76.0 or earlier +} + +--echo # +--echo # Bug #19183565 CREATE DYNAMIC INNODB_TMPDIR VARIABLE TO CONTROL +--echo # WHERE INNODB WRITES TEMP FILES +--echo # + +--echo # If innodb_tmpdir is NULL or "", temporary file will be created in +--echo # server configuration variable location(--tmpdir) + +create table t1(a int primary key)engine=innodb; +show session variables like 'innodb_tmpdir'; +alter table t1 add column b int not null; +set global innodb_tmpdir=NULL; +--echo # Connection con1 +connect (con1,localhost,root); +show session variables like 'innodb_tmpdir'; +alter table t1 add key(b); +connection default; +disconnect con1; +drop table t1; + +--echo # innodb_tmpdir with invalid path. + +create table t1(a int primary key)engine=innodb; +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_tmpdir='wrong_value'; +show warnings; +drop table t1; + + +--echo # innodb_tmpdir with mysql data directory path. + +let $MYSQLD_DATADIR= `select @@datadir`; +create table t1(a text, b text, fulltext(a,b))engine=innodb; +insert into t1 values('test1', 'test2'); +insert into t1 values('text1', 'text2'); +--replace_result $MYSQLD_DATADIR MYSQL_DATADIR +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_tmpdir = @@global.datadir; +--replace_regex /.*mysqld.1/DATADIR/ +show warnings; +drop table t1; + +--echo # innodb_tmpdir with valid location. +let $MYSQL_TMP_DIR= `select @@tmpdir`; +create table t1(a text, b text, fulltext(a,b))engine=innodb; +insert into t1 values('test1', 'test2'); +insert into t1 values('text1', 'text2'); +set @tmpdir = @@global.tmpdir; +set global innodb_tmpdir = @tmpdir; +show session variables like 'innodb_tmpdir'; +--echo # Connection con3 +connect (con3,localhost,root); +# Following alter using innodb_tmpdir as a path to create temporary files +alter table t1 add fulltext(b); +disconnect con3; +connection default; +set global innodb_tmpdir=NULL; +drop table t1; + +--source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/sys_vars/r/innodb_tmpdir_basic.result b/mysql-test/suite/sys_vars/r/innodb_tmpdir_basic.result new file mode 100644 index 00000000000..be10c93b56b --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_tmpdir_basic.result @@ -0,0 +1,35 @@ +SET @start_global_value = @@global.innodb_tmpdir; +SELECT @start_global_value; +@start_global_value +NULL +select @@session.innodb_tmpdir; +@@session.innodb_tmpdir +NULL +show global variables like 'innodb_tmpdir'; +Variable_name Value +innodb_tmpdir +show session variables like 'innodb_tmpdir'; +Variable_name Value +innodb_tmpdir +select * from information_schema.global_variables where variable_name='innodb_tmpdir'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_TMPDIR +select * from information_schema.session_variables where variable_name='innodb_tmpdir'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_TMPDIR +set global innodb_tmpdir=@@global.tmpdir; +set session innodb_tmpdir=@@global.tmpdir; +set global innodb_tmpdir=1.1; +ERROR 42000: Incorrect argument type to variable 'innodb_tmpdir' +set global innodb_tmpdir=1e1; +ERROR 42000: Incorrect argument type to variable 'innodb_tmpdir' +set global innodb_tmpdir=repeat('a',1000); +ERROR 42000: Variable 'innodb_tmpdir' can't be set to the value of 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +show warnings; +Level Code Message +Warning 1210 Path length should not exceed 512 bytes +Error 1231 Variable 'innodb_tmpdir' can't be set to the value of 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SET @@global.innodb_tmpdir = @start_global_value; +SELECT @@global.innodb_tmpdir; +@@global.innodb_tmpdir +NULL diff --git a/mysql-test/suite/sys_vars/t/innodb_tmpdir_basic.test b/mysql-test/suite/sys_vars/t/innodb_tmpdir_basic.test new file mode 100644 index 00000000000..8c643c0e849 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_tmpdir_basic.test @@ -0,0 +1,48 @@ +--source include/have_innodb.inc + +if (`select plugin_auth_version <= "5.6.26-MariaDB-76.0" from information_schema.plugins where plugin_name='innodb'`) +{ + --skip Not fixed in XtraDB as of 5.6.26-MariaDB-76.0 or earlier +} + +SET @start_global_value = @@global.innodb_tmpdir; +SELECT @start_global_value; + +# +# exists as global and session +# +select @@session.innodb_tmpdir; + +show global variables like 'innodb_tmpdir'; +show session variables like 'innodb_tmpdir'; + +select * from information_schema.global_variables where variable_name='innodb_tmpdir'; +select * from information_schema.session_variables where variable_name='innodb_tmpdir'; +# +# Show that it is writable +# + +set global innodb_tmpdir=@@global.tmpdir; +set session innodb_tmpdir=@@global.tmpdir; + +# +# incorrect types +# +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_tmpdir=1.1; +--error ER_WRONG_TYPE_FOR_VAR +set global innodb_tmpdir=1e1; + +# +# path len more than 512 +# +--error ER_WRONG_VALUE_FOR_VAR +set global innodb_tmpdir=repeat('a',1000); +show warnings; + +# +# Cleanup +# + +SET @@global.innodb_tmpdir = @start_global_value; +SELECT @@global.innodb_tmpdir; diff --git a/storage/innobase/buf/buf0dump.cc b/storage/innobase/buf/buf0dump.cc index bf757dc5d89..249761b23c0 100644 --- a/storage/innobase/buf/buf0dump.cc +++ b/storage/innobase/buf/buf0dump.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2011, 2015, 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 @@ -167,6 +167,25 @@ buf_load_status( va_end(ap); } +/** Returns the directory path where the buffer pool dump file will be created. +@return directory path */ +static +const char* +get_buf_dump_dir() +{ + const char* dump_dir; + + /* The dump file should be created in the default data directory if + innodb_data_home_dir is set as an empty string. */ + if (strcmp(srv_data_home, "") == 0) { + dump_dir = fil_path_to_mysql_datadir; + } else { + dump_dir = srv_data_home; + } + + return(dump_dir); +} + /*****************************************************************//** Perform a buffer pool dump into the file specified by innodb_buffer_pool_filename. If any errors occur then the value of @@ -190,7 +209,7 @@ buf_dump( int ret; ut_snprintf(full_filename, sizeof(full_filename), - "%s%c%s", srv_data_home, SRV_PATH_SEPARATOR, + "%s%c%s", get_buf_dump_dir(), SRV_PATH_SEPARATOR, srv_buf_dump_filename); ut_snprintf(tmp_filename, sizeof(tmp_filename), @@ -463,7 +482,7 @@ buf_load() buf_load_abort_flag = FALSE; ut_snprintf(full_filename, sizeof(full_filename), - "%s%c%s", srv_data_home, SRV_PATH_SEPARATOR, + "%s%c%s", get_buf_dump_dir(), SRV_PATH_SEPARATOR, srv_buf_dump_filename); buf_load_status(STATUS_NOTICE, diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index b5e97a2c3f4..1e280a6bfa7 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1107,7 +1107,7 @@ dict_init(void) &dict_operation_lock, SYNC_DICT_OPERATION); if (!srv_read_only_mode) { - dict_foreign_err_file = os_file_create_tmpfile(); + dict_foreign_err_file = os_file_create_tmpfile(NULL); ut_a(dict_foreign_err_file); mutex_create(dict_foreign_err_mutex_key, diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 1eac9e0df51..b073398f8ec 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2009, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2009, 2015, 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 @@ -1436,7 +1436,6 @@ on the leaf page. when comparing records @param[out] n_diff number of distinct records @param[out] n_external_pages number of external pages -@param[in,out] mtr mini-transaction @return number of distinct records on the leaf page */ static void @@ -1444,8 +1443,7 @@ dict_stats_analyze_index_below_cur( const btr_cur_t* cur, ulint n_prefix, ib_uint64_t* n_diff, - ib_uint64_t* n_external_pages, - mtr_t* mtr) + ib_uint64_t* n_external_pages) { dict_index_t* index; ulint space; @@ -1459,6 +1457,7 @@ dict_stats_analyze_index_below_cur( ulint* offsets2; ulint* offsets_rec; ulint size; + mtr_t mtr; index = btr_cur_get_index(cur); @@ -1497,12 +1496,14 @@ dict_stats_analyze_index_below_cur( function without analyzing any leaf pages */ *n_external_pages = 0; + mtr_start(&mtr); + /* descend to the leaf level on the B-tree */ for (;;) { block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL /* no guessed block */, - BUF_GET, __FILE__, __LINE__, mtr); + BUF_GET, __FILE__, __LINE__, &mtr); page = buf_block_get_frame(block); @@ -1524,6 +1525,8 @@ dict_stats_analyze_index_below_cur( ut_a(*n_diff > 0); if (*n_diff == 1) { + mtr_commit(&mtr); + /* page has all keys equal and the end of the page was reached by dict_stats_scan_page(), no need to descend to the leaf level */ @@ -1548,7 +1551,7 @@ dict_stats_analyze_index_below_cur( } /* make sure we got a leaf page as a result from the above loop */ - ut_ad(btr_page_get_level(page, mtr) == 0); + ut_ad(btr_page_get_level(page, &mtr) == 0); /* scan the leaf page and find the number of distinct keys, when looking only at the first n_prefix columns; also estimate @@ -1565,6 +1568,7 @@ dict_stats_analyze_index_below_cur( __func__, page_no, n_diff); #endif + mtr_commit(&mtr); mem_heap_free(heap); } @@ -1774,8 +1778,7 @@ dict_stats_analyze_index_for_n_prefix( dict_stats_analyze_index_below_cur(btr_pcur_get_btr_cur(&pcur), n_prefix, &n_diff_on_leaf_page, - &n_external_pages, - mtr); + &n_external_pages); /* We adjust n_diff_on_leaf_page here to avoid counting one record twice - once as the last on some page and once diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index e096b8bf6d6..2a0aa4daf12 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -580,7 +580,7 @@ fts_zip_read_word( #ifdef UNIV_DEBUG ulint i; #endif - byte len = 0; + short len = 0; void* null = NULL; byte* ptr = word->f_str; int flush = Z_NO_FLUSH; @@ -590,7 +590,7 @@ fts_zip_read_word( return(NULL); } - zip->zp->next_out = &len; + zip->zp->next_out = reinterpret_cast(&len); zip->zp->avail_out = sizeof(len); while (zip->status == Z_OK && zip->zp->avail_out > 0) { @@ -688,11 +688,12 @@ fts_fetch_index_words( fts_zip_t* zip = static_cast(user_arg); que_node_t* exp = sel_node->select_list; dfield_t* dfield = que_node_get_val(exp); - byte len = (byte) dfield_get_len(dfield); + short len = static_cast(dfield_get_len(dfield)); void* data = dfield_get_data(dfield); /* Skip the duplicate words. */ - if (zip->word.f_len == len && !memcmp(zip->word.f_str, data, len)) { + if (zip->word.f_len == static_cast(len) + && !memcmp(zip->word.f_str, data, len)) { return(TRUE); } @@ -706,7 +707,7 @@ fts_fetch_index_words( ut_a(zip->zp->next_in == NULL); /* The string is prefixed by len. */ - zip->zp->next_in = &len; + zip->zp->next_in = reinterpret_cast(&len); zip->zp->avail_in = sizeof(len); /* Compress the word, create output blocks as necessary. */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 0d4f98cedf3..1c04826ab28 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -505,6 +505,71 @@ ib_cb_t innodb_api_cb[] = { (ib_cb_t) ib_trx_read_only }; + +/** + Test a file path whether it is same as mysql data directory path. + + @param path null terminated character string + + @return + @retval TRUE The path is different from mysql data directory. + @retval FALSE The path is same as mysql data directory. +*/ +static bool is_mysql_datadir_path(const char *path) +{ + if (path == NULL) + return false; + + char mysql_data_dir[FN_REFLEN], path_dir[FN_REFLEN]; + convert_dirname(path_dir, path, NullS); + convert_dirname(mysql_data_dir, mysql_unpacked_real_data_home, NullS); + size_t mysql_data_home_len= dirname_length(mysql_data_dir); + size_t path_len = dirname_length(path_dir); + + if (path_len < mysql_data_home_len) + return true; + + if (!lower_case_file_system) + return(memcmp(mysql_data_dir, path_dir, mysql_data_home_len)); + + return(files_charset_info->coll->strnncoll(files_charset_info, + (uchar *) path_dir, path_len, + (uchar *) mysql_data_dir, + mysql_data_home_len, + TRUE)); + +} + + +static int mysql_tmpfile_path(const char *path, const char *prefix) +{ + DBUG_ASSERT(path != NULL); + DBUG_ASSERT((strlen(path) + strlen(prefix)) <= FN_REFLEN); + + char filename[FN_REFLEN]; + File fd = create_temp_file(filename, path, prefix, +#ifdef __WIN__ + O_BINARY | O_TRUNC | O_SEQUENTIAL | + O_SHORT_LIVED | +#endif /* __WIN__ */ + O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY, + MYF(MY_WME)); + if (fd >= 0) { +#ifndef __WIN__ + /* + This can be removed once the following bug is fixed: + Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option + (file not removed) (Unix) + */ + unlink(filename); +#endif /* !__WIN__ */ + } + + return fd; +} + + + /*************************************************************//** Check whether valid argument given to innodb_ft_*_stopword_table. This function is registered as a callback with MySQL. @@ -520,6 +585,108 @@ innodb_stopword_table_validate( for update function */ struct st_mysql_value* value); /*!< in: incoming string */ +/** Validate passed-in "value" is a valid directory name. +This function is registered as a callback with MySQL. +@param[in,out] thd thread handle +@param[in] var pointer to system variable +@param[out] save immediate result for update +@param[in] value incoming string +@return 0 for valid name */ +static +int +innodb_tmpdir_validate( + THD* thd, + struct st_mysql_sys_var* var, + void* save, + struct st_mysql_value* value) +{ + + char* alter_tmp_dir; + char* innodb_tmp_dir; + char buff[OS_FILE_MAX_PATH]; + int len = sizeof(buff); + char tmp_abs_path[FN_REFLEN + 2]; + + ut_ad(save != NULL); + ut_ad(value != NULL); + + if (check_global_access(thd, FILE_ACL)) { + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "InnoDB: FILE Permissions required"); + *static_cast(save) = NULL; + return(1); + } + + alter_tmp_dir = (char*) value->val_str(value, buff, &len); + + if (!alter_tmp_dir) { + *static_cast(save) = alter_tmp_dir; + return(0); + } + + if (strlen(alter_tmp_dir) > FN_REFLEN) { + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "Path length should not exceed %d bytes", FN_REFLEN); + *static_cast(save) = NULL; + return(1); + } + + my_realpath(tmp_abs_path, alter_tmp_dir, 0); + size_t tmp_abs_len = strlen(tmp_abs_path); + + if (my_access(tmp_abs_path, F_OK)) { + + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "InnoDB: Path doesn't exist."); + *static_cast(save) = NULL; + return(1); + } else if (my_access(tmp_abs_path, R_OK | W_OK)) { + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "InnoDB: Server doesn't have permission in " + "the given location."); + *static_cast(save) = NULL; + return(1); + } + + MY_STAT stat_info_dir; + + if (my_stat(tmp_abs_path, &stat_info_dir, MYF(0))) { + if ((stat_info_dir.st_mode & S_IFDIR) != S_IFDIR) { + + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "Given path is not a directory. "); + *static_cast(save) = NULL; + return(1); + } + } + + if (!is_mysql_datadir_path(tmp_abs_path)) { + + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_ARGUMENTS, + "InnoDB: Path Location should not be same as " + "mysql data directory location."); + *static_cast(save) = NULL; + return(1); + } + + innodb_tmp_dir = static_cast( + thd_memdup(thd, tmp_abs_path, tmp_abs_len + 1)); + *static_cast(save) = innodb_tmp_dir; + return(0); +} + /** "GEN_CLUST_INDEX" is the name reserved for InnoDB default system clustered index when there is no primary key. */ const char innobase_index_reserve_name[] = "GEN_CLUST_INDEX"; @@ -572,6 +739,11 @@ static MYSQL_THDVAR_STR(ft_user_stopword_table, "User supplied stopword table name, effective in the session level.", innodb_stopword_table_validate, NULL, NULL); +static MYSQL_THDVAR_STR(tmpdir, + PLUGIN_VAR_OPCMDARG|PLUGIN_VAR_MEMALLOC, + "Directory for temporary non-tablespace files.", + innodb_tmpdir_validate, NULL, NULL); + static SHOW_VAR innodb_status_variables[]= { {"buffer_pool_dump_status", (char*) &export_vars.innodb_buffer_pool_dump_status, SHOW_CHAR}, @@ -1306,6 +1478,26 @@ thd_supports_xa( return(THDVAR(thd, support_xa)); } +/** Get the value of innodb_tmpdir. +@param[in] thd thread handle, or NULL to query + the global innodb_tmpdir. +@retval NULL if innodb_tmpdir="" */ +UNIV_INTERN +const char* +thd_innodb_tmpdir( + THD* thd) +{ +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(false)); +#endif /* UNIV_SYNC_DEBUG */ + + const char* tmp_dir = THDVAR(thd, tmpdir); + if (tmp_dir != NULL && *tmp_dir == '\0') { + tmp_dir = NULL; + } + + return(tmp_dir); +} /******************************************************************//** Returns the lock wait timeout for the current connection. @return the lock wait timeout, in seconds */ @@ -1833,13 +2025,14 @@ innobase_get_lower_case_table_names(void) return(lower_case_table_names); } -/*********************************************************************//** -Creates a temporary file. +/** Create a temporary file in the location specified by the parameter +path. If the path is null, then it will be created in tmpdir. +@param[in] path location for creating temporary file @return temporary file descriptor, or < 0 on error */ UNIV_INTERN int -innobase_mysql_tmpfile(void) -/*========================*/ +innobase_mysql_tmpfile( + const char* path) { int fd2 = -1; File fd; @@ -1849,7 +2042,11 @@ innobase_mysql_tmpfile(void) return(-1); ); - fd = mysql_tmpfile("ib"); + if (path == NULL) { + fd = mysql_tmpfile("ib"); + } else { + fd = mysql_tmpfile_path(path, "ib"); + } if (fd >= 0) { /* Copy the file descriptor, so that the additional resources @@ -2776,6 +2973,13 @@ ha_innobase::reset_template(void) ut_ad(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED); ut_ad(prebuilt->magic_n2 == prebuilt->magic_n); + /* Force table to be freed in close_thread_table(). */ + DBUG_EXECUTE_IF("free_table_in_fts_query", + if (prebuilt->in_fts_query) { + table->m_needs_reopen = true; + } + ); + prebuilt->keep_other_fields_on_keyread = 0; prebuilt->read_just_key = 0; prebuilt->in_fts_query = 0; @@ -15774,15 +15978,12 @@ innobase_fts_close_ranking( { fts_result_t* result; - ((NEW_FT_INFO*) fts_hdl)->ft_prebuilt->in_fts_query = false; - result = ((NEW_FT_INFO*) fts_hdl)->ft_result; fts_query_free_result(result); my_free((uchar*) fts_hdl); - return; } @@ -17103,6 +17304,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(sync_array_size), MYSQL_SYSVAR(compression_failure_threshold_pct), MYSQL_SYSVAR(compression_pad_pct_max), + MYSQL_SYSVAR(simulate_comp_failures), #ifdef UNIV_DEBUG MYSQL_SYSVAR(trx_rseg_n_slots_debug), MYSQL_SYSVAR(limit_optimistic_insert_debug), @@ -17110,7 +17312,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(fil_make_page_dirty_debug), MYSQL_SYSVAR(saved_page_number_debug), #endif /* UNIV_DEBUG */ - MYSQL_SYSVAR(simulate_comp_failures), + MYSQL_SYSVAR(tmpdir), NULL }; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 49a9c424287..4e2c42c09f0 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -2712,6 +2712,10 @@ prepare_inplace_alter_table_dict( ctx->num_to_add_index = ha_alter_info->index_add_count; + ut_ad(ctx->prebuilt->trx->mysql_thd != NULL); + const char* path = thd_innodb_tmpdir( + ctx->prebuilt->trx->mysql_thd); + index_defs = innobase_create_key_defs( ctx->heap, ha_alter_info, altered_table, ctx->num_to_add_index, num_fts_index, @@ -3054,8 +3058,10 @@ prepare_inplace_alter_table_dict( error = DB_OUT_OF_MEMORY; goto error_handling;); rw_lock_x_lock(&ctx->add_index[a]->lock); + bool ok = row_log_allocate(ctx->add_index[a], - NULL, true, NULL, NULL); + NULL, true, NULL, + NULL, path); rw_lock_x_unlock(&ctx->add_index[a]->lock); if (!ok) { @@ -3081,7 +3087,7 @@ prepare_inplace_alter_table_dict( clust_index, ctx->new_table, !(ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX), - ctx->add_cols, ctx->col_map); + ctx->add_cols, ctx->col_map, path); rw_lock_x_unlock(&clust_index->lock); if (!ok) { @@ -4058,6 +4064,7 @@ ok_exit: files and merge sort. */ DBUG_EXECUTE_IF("innodb_OOM_inplace_alter", error = DB_OUT_OF_MEMORY; goto oom;); + error = row_merge_build_indexes( prebuilt->trx, prebuilt->table, ctx->new_table, diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 9a1fb61348e..ad553432247 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2006, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2006, 2015, 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 @@ -341,6 +341,15 @@ thd_supports_xa( THD* thd); /*!< in: thread handle, or NULL to query the global innodb_supports_xa */ +/** Get status of innodb_tmpdir. +@param[in] thd thread handle, or NULL to query + the global innodb_tmpdir. +@retval NULL if innodb_tmpdir="" */ +UNIV_INTERN +const char* +thd_innodb_tmpdir( + THD* thd); + /******************************************************************//** Returns the lock wait timeout for the current connection. @return the lock wait timeout, in seconds */ diff --git a/storage/innobase/include/os0file.h b/storage/innobase/include/os0file.h index 5077c9e37eb..6eea39639dc 100644 --- a/storage/innobase/include/os0file.h +++ b/storage/innobase/include/os0file.h @@ -432,14 +432,19 @@ UNIV_INTERN void os_io_init_simple(void); /*===================*/ -/***********************************************************************//** -Creates a temporary file. This function is like tmpfile(3), but -the temporary file is created in the MySQL temporary directory. -@return temporary file handle, or NULL on error */ + +/** Create a temporary file. This function is like tmpfile(3), but +the temporary file is created in the given parameter path. If the path +is null then it will create the file in the mysql server configuration +parameter (--tmpdir). +@param[in] path location for creating temporary file +@return temporary file handle, or NULL on error */ +UNIV_INTERN FILE* -os_file_create_tmpfile(void); -/*========================*/ +os_file_create_tmpfile( + const char* path); + #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** The os_file_opendir() function opens a directory stream corresponding to the @@ -1245,14 +1250,14 @@ os_file_get_status( file can be opened in RW mode */ #if !defined(UNIV_HOTBACKUP) -/*********************************************************************//** -Creates a temporary file that will be deleted on close. -This function is defined in ha_innodb.cc. -@return temporary file descriptor, or < 0 on error */ +/** Create a temporary file in the location specified by the parameter +path. If the path is null, then it will be created in tmpdir. +@param[in] path location for creating temporary file +@return temporary file descriptor, or < 0 on error */ UNIV_INTERN int -innobase_mysql_tmpfile(void); -/*========================*/ +innobase_mysql_tmpfile( + const char* path); #endif /* !UNIV_HOTBACKUP */ diff --git a/storage/innobase/include/row0log.h b/storage/innobase/include/row0log.h index 62715fe8808..5eed390aced 100644 --- a/storage/innobase/include/row0log.h +++ b/storage/innobase/include/row0log.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2011, 2015, 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 @@ -51,8 +51,9 @@ row_log_allocate( const dtuple_t* add_cols, /*!< in: default values of added columns, or NULL */ - const ulint* col_map)/*!< in: mapping of old column + const ulint* col_map,/*!< in: mapping of old column numbers to new ones, or NULL if !table */ + const char* path) /*!< in: where to create temporary file */ __attribute__((nonnull(1), warn_unused_result)); /******************************************************//** diff --git a/storage/innobase/include/row0merge.h b/storage/innobase/include/row0merge.h index de353d46202..e3d95d7295a 100644 --- a/storage/innobase/include/row0merge.h +++ b/storage/innobase/include/row0merge.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2005, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2005, 2015, 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 @@ -171,14 +171,14 @@ void row_merge_drop_temp_indexes(void); /*=============================*/ -/*********************************************************************//** -Creates temporary merge files, and if UNIV_PFS_IO defined, register -the file descriptor with Performance Schema. +/** Create temporary merge files in the given paramater path, and if +UNIV_PFS_IO defined, register the file descriptor with Performance Schema. +@param[in] path location for creating temporary merge files. @return File descriptor */ UNIV_INTERN int -row_merge_file_create_low(void) -/*===========================*/ +row_merge_file_create_low( + const char* path) __attribute__((warn_unused_result)); /*********************************************************************//** Destroy a merge file. And de-register the file from Performance Schema @@ -349,15 +349,17 @@ row_merge_buf_empty( /*================*/ row_merge_buf_t* buf) /*!< in,own: sort buffer */ __attribute__((warn_unused_result, nonnull)); -/*********************************************************************//** -Create a merge file. + +/** Create a merge file in the given location. +@param[out] merge_file merge file structure +@param[in] path location for creating temporary file @return file descriptor, or -1 on failure */ UNIV_INTERN int row_merge_file_create( -/*==================*/ - merge_file_t* merge_file) /*!< out: merge file structure */ - __attribute__((nonnull)); + merge_file_t* merge_file, + const char* path); + /*********************************************************************//** Merge disk files. @return DB_SUCCESS or error code */ diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 3f737089ac3..8347425c5c0 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -44,7 +44,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 5 #define INNODB_VERSION_MINOR 6 -#define INNODB_VERSION_BUGFIX 27 +#define INNODB_VERSION_BUGFIX 29 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 4099ca1932f..91173b95ca7 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -633,7 +633,7 @@ lock_sys_create( lock_sys->rec_hash = hash_create(n_cells); if (!srv_read_only_mode) { - lock_latest_err_file = os_file_create_tmpfile(); + lock_latest_err_file = os_file_create_tmpfile(NULL); ut_a(lock_latest_err_file); } } diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index ce80a01210a..c5ba4280c17 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -756,17 +756,19 @@ os_io_init_simple(void) } } -/***********************************************************************//** -Creates a temporary file. This function is like tmpfile(3), but -the temporary file is created in the MySQL temporary directory. -@return temporary file handle, or NULL on error */ +/** Create a temporary file. This function is like tmpfile(3), but +the temporary file is created in the given parameter path. If the path +is null then it will create the file in the mysql server configuration +parameter (--tmpdir). +@param[in] path location for creating temporary file +@return temporary file handle, or NULL on error */ UNIV_INTERN FILE* -os_file_create_tmpfile(void) -/*========================*/ +os_file_create_tmpfile( + const char* path) { FILE* file = NULL; - int fd = innobase_mysql_tmpfile(); + int fd = innobase_mysql_tmpfile(path); ut_ad(!srv_read_only_mode); @@ -3658,7 +3660,7 @@ os_aio_native_aio_supported(void) return(FALSE); } else if (!srv_read_only_mode) { /* Now check if tmpdir supports native aio ops. */ - fd = innobase_mysql_tmpfile(); + fd = innobase_mysql_tmpfile(NULL); if (fd < 0) { ib_logf(IB_LOG_LEVEL_WARN, diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index 621a14d27c2..d8d35319d48 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2010, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2010, 2015, 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 @@ -220,6 +220,9 @@ row_fts_psort_info_init( common_info->merge_event = os_event_create(); common_info->opt_doc_id_size = opt_doc_id_size; + ut_ad(trx->mysql_thd != NULL); + const char* path = thd_innodb_tmpdir(trx->mysql_thd); + /* There will be FTS_NUM_AUX_INDEX number of "sort buckets" for each parallel sort thread. Each "sort bucket" holds records for a particular "FTS index partition" */ @@ -241,8 +244,8 @@ row_fts_psort_info_init( psort_info[j].merge_buf[i] = row_merge_buf_create( dup->index); - if (row_merge_file_create(psort_info[j].merge_file[i]) - < 0) { + if (row_merge_file_create(psort_info[j].merge_file[i], + path) < 0) { goto func_exit; } @@ -610,6 +613,11 @@ fts_parallel_tokenization( ulint retried = 0; dberr_t error = DB_SUCCESS; + ut_ad(psort_info->psort_common->trx->mysql_thd != NULL); + + const char* path = thd_innodb_tmpdir( + psort_info->psort_common->trx->mysql_thd); + ut_ad(psort_info); buf = psort_info->merge_buf; @@ -840,7 +848,7 @@ exit: continue; } - tmpfd[i] = row_merge_file_create_low(); + tmpfd[i] = row_merge_file_create_low(path); if (tmpfd[i] < 0) { error = DB_OUT_OF_MEMORY; goto func_exit; diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 032a3a0619c..8d592968c3e 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -194,8 +194,25 @@ struct row_log_t { or by index->lock X-latch only */ row_log_buf_t head; /*!< reader context; protected by MDL only; modifiable by row_log_apply_ops() */ + const char* path; /*!< where to create temporary file during + log operation */ }; +/** Create the file or online log if it does not exist. +@param[in,out] log online rebuild log +@return file descriptor. */ +static __attribute__((warn_unused_result)) +int +row_log_tmpfile( + row_log_t* log) +{ + DBUG_ENTER("row_log_tmpfile"); + if (log->fd < 0) { + log->fd = row_merge_file_create_low(log->path); + } + + DBUG_RETURN(log->fd); +} /** Allocate the memory for the log buffer. @param[in,out] log_buf Buffer used for log operation @@ -340,6 +357,12 @@ row_log_online_op( log->tail.buf, avail_size); } UNIV_MEM_ASSERT_RW(log->tail.block, srv_sort_buf_size); + + if (row_log_tmpfile(log) < 0) { + log->error = DB_OUT_OF_MEMORY; + goto err_exit; + } + ret = os_file_write( "(modification log)", OS_FILE_FROM_FD(log->fd), @@ -450,6 +473,12 @@ row_log_table_close_func( log->tail.buf, avail); } UNIV_MEM_ASSERT_RW(log->tail.block, srv_sort_buf_size); + + if (row_log_tmpfile(log) < 0) { + log->error = DB_OUT_OF_MEMORY; + goto err_exit; + } + ret = os_file_write( "(modification log)", OS_FILE_FROM_FD(log->fd), @@ -469,6 +498,7 @@ write_failed: log->tail.total += size; UNIV_MEM_INVALID(log->tail.buf, sizeof log->tail.buf); +err_exit: mutex_exit(&log->mutex); } @@ -2536,7 +2566,8 @@ corruption: if (index->online_log->head.blocks) { #ifdef HAVE_FTRUNCATE /* Truncate the file in order to save space. */ - if (ftruncate(index->online_log->fd, 0) == -1) { + if (index->online_log->fd != -1 + && ftruncate(index->online_log->fd, 0) == -1) { fprintf(stderr, "InnoDB: Error: Truncate of file " "\'%s\' failed with error %d:%s\n", index->name + 1, errno, strerror(errno)); @@ -2855,8 +2886,9 @@ row_log_allocate( const dtuple_t* add_cols, /*!< in: default values of added columns, or NULL */ - const ulint* col_map)/*!< in: mapping of old column + const ulint* col_map,/*!< in: mapping of old column numbers to new ones, or NULL if !table */ + const char* path) /*!< in: where to create temporary file */ { row_log_t* log; DBUG_ENTER("row_log_allocate"); @@ -2875,11 +2907,7 @@ row_log_allocate( DBUG_RETURN(false); } - log->fd = row_merge_file_create_low(); - if (log->fd < 0) { - ut_free(log); - DBUG_RETURN(false); - } + log->fd = -1; mutex_create(index_online_log_key, &log->mutex, SYNC_INDEX_ONLINE_LOG); log->blobs = NULL; @@ -2894,6 +2922,7 @@ row_log_allocate( log->tail.block = log->head.block = NULL; log->head.blocks = log->head.bytes = 0; log->head.total = 0; + log->path = path; dict_index_set_online_status(index, ONLINE_INDEX_CREATION); index->online_log = log; @@ -3371,7 +3400,8 @@ corruption: if (index->online_log->head.blocks) { #ifdef HAVE_FTRUNCATE /* Truncate the file in order to save space. */ - if (ftruncate(index->online_log->fd, 0) == -1) { + if (index->online_log->fd != -1 + && ftruncate(index->online_log->fd, 0) == -1) { fprintf(stderr, "InnoDB: Error: Truncate of file " "\'%s\' failed with error %d:%s\n", index->name + 1, errno, strerror(errno)); diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 6dd88b00464..9abb95b6ebb 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1241,47 +1241,95 @@ row_merge_write_eof( return(&block[0]); } -/********************************************************************//** -Reads clustered index of the table and create temporary files +/** Create a temporary file if it has not been created already. +@param[in,out] tmpfd temporary file handle +@param[in] path path to create temporary file +@return file descriptor, or -1 on failure */ +static __attribute__((warn_unused_result)) +int +row_merge_tmpfile_if_needed( + int* tmpfd, + const char* path) +{ + if (*tmpfd < 0) { + *tmpfd = row_merge_file_create_low(path); + } + + return(*tmpfd); +} + +/** Create a temporary file for merge sort if it was not created already. +@param[in,out] file merge file structure +@param[in,out] tmpfd temporary file structure +@param[in] nrec number of records in the file +@param[in] path path to create temporary files +@return file descriptor, or -1 on failure */ +static __attribute__((warn_unused_result)) +int +row_merge_file_create_if_needed( + merge_file_t* file, + int* tmpfd, + ulint nrec, + const char* path) +{ + ut_ad(file->fd < 0 || *tmpfd >=0); + if (file->fd < 0 && row_merge_file_create(file, path) >= 0) { + if (row_merge_tmpfile_if_needed(tmpfd, path) < 0) { + return(-1); + } + + file->n_rec = nrec; + } + + ut_ad(file->fd < 0 || *tmpfd >=0); + return(file->fd); +} + +/** Reads clustered index of the table and create temporary files containing the index entries for the indexes to be built. -@return DB_SUCCESS or error */ +@param[in] trx transaction +@param[in,out] table MySQL table object, for reporting erroneous + records +@param[in] old_table table where rows are read from +@param[in] new_table table where indexes are created; identical to + old_table unless creating a PRIMARY KEY +@param[in] online true if creating indexes online +@param[in] index indexes to be created +@param[in] fts_sort_idx full-text index to be created, or NULL +@param[in] psort_info parallel sort info for fts_sort_idx creation, + or NULL +@param[in] files temporary files +@param[in] key_numbers MySQL key numbers to create +@param[in] n_index number of indexes to create +@param[in] add_cols default values of added columns, or NULL +@param[in] col_map mapping of old column numbers to new ones, or + NULL if old_table == new_table +@param[in] add_autoinc number of added AUTO_INCREMENT columns, or + ULINT_UNDEFINED if none is added +@param[in,out] sequence autoinc sequence +@param[in,out] block file buffer +@param[in,out] tmpfd temporary file handle +return DB_SUCCESS or error */ static __attribute__((nonnull(1,2,3,4,6,9,10,16), warn_unused_result)) dberr_t row_merge_read_clustered_index( -/*===========================*/ - trx_t* trx, /*!< in: transaction */ - struct TABLE* table, /*!< in/out: MySQL table object, - for reporting erroneous records */ - const dict_table_t* old_table,/*!< in: table where rows are - read from */ - const dict_table_t* new_table,/*!< in: table where indexes are - created; identical to old_table - unless creating a PRIMARY KEY */ - bool online, /*!< in: true if creating indexes - online */ - dict_index_t** index, /*!< in: indexes to be created */ + trx_t* trx, + struct TABLE* table, + const dict_table_t* old_table, + const dict_table_t* new_table, + bool online, + dict_index_t** index, dict_index_t* fts_sort_idx, - /*!< in: full-text index to be created, - or NULL */ fts_psort_t* psort_info, - /*!< in: parallel sort info for - fts_sort_idx creation, or NULL */ - merge_file_t* files, /*!< in: temporary files */ + merge_file_t* files, const ulint* key_numbers, - /*!< in: MySQL key numbers to create */ - ulint n_index,/*!< in: number of indexes to create */ + ulint n_index, const dtuple_t* add_cols, - /*!< in: default values of - added columns, or NULL */ - const ulint* col_map,/*!< in: mapping of old column - numbers to new ones, or NULL - if old_table == new_table */ + const ulint* col_map, ulint add_autoinc, - /*!< in: number of added - AUTO_INCREMENT column, or - ULINT_UNDEFINED if none is added */ - ib_sequence_t& sequence,/*!< in/out: autoinc sequence */ - row_merge_block_t* block) /*!< in/out: file buffer */ + ib_sequence_t& sequence, + row_merge_block_t* block, + int* tmpfd) { dict_index_t* clust_index; /* Clustered index */ mem_heap_t* row_heap; /* Heap memory to create @@ -1313,6 +1361,9 @@ row_merge_read_clustered_index( DEBUG_FTS_SORT_PRINT("FTS_SORT: Start Create Index\n"); #endif + ut_ad(trx->mysql_thd != NULL); + const char* path = thd_innodb_tmpdir(trx->mysql_thd); + /* Create and initialize memory for record buffers */ merge_buf = static_cast( @@ -1777,13 +1828,25 @@ write_buffers: dict_index_get_lock(buf->index)); } - row_merge_buf_write(buf, file, block); + if (buf->n_tuples > 0) { - if (!row_merge_write(file->fd, file->offset++, - block)) { - err = DB_TEMP_FILE_WRITE_FAILURE; - trx->error_key_num = i; - break; + if (row_merge_file_create_if_needed( + file, tmpfd, buf->n_tuples, path) < 0) { + err = DB_OUT_OF_MEMORY; + trx->error_key_num = i; + break; + } + + ut_ad(file->n_rec > 0); + + row_merge_buf_write(buf, file, block); + + if (!row_merge_write(file->fd, file->offset++, + block)) { + err = DB_TEMP_FILE_WRITE_FAILURE; + trx->error_key_num = i; + break; + } } UNIV_MEM_INVALID(&block[0], srv_sort_buf_size); @@ -1827,6 +1890,7 @@ write_buffers: func_exit: mtr_commit(&mtr); + mem_heap_free(row_heap); if (nonnull) { @@ -3057,14 +3121,15 @@ row_merge_drop_temp_indexes(void) trx_free_for_background(trx); } -/*********************************************************************//** -Creates temporary merge files, and if UNIV_PFS_IO defined, register -the file descriptor with Performance Schema. -@return file descriptor, or -1 on failure */ + +/** Create temporary merge files in the given paramater path, and if +UNIV_PFS_IO defined, register the file descriptor with Performance Schema. +@param[in] path location for creating temporary merge files. +@return File descriptor */ UNIV_INTERN int -row_merge_file_create_low(void) -/*===========================*/ +row_merge_file_create_low( + const char* path) { int fd; #ifdef UNIV_PFS_IO @@ -3078,7 +3143,7 @@ row_merge_file_create_low(void) "Innodb Merge Temp File", __FILE__, __LINE__); #endif - fd = innobase_mysql_tmpfile(); + fd = innobase_mysql_tmpfile(path); #ifdef UNIV_PFS_IO register_pfs_file_open_end(locker, fd); #endif @@ -3091,16 +3156,18 @@ row_merge_file_create_low(void) return(fd); } -/*********************************************************************//** -Create a merge file. + +/** Create a merge file in the given location. +@param[out] merge_file merge file structure +@param[in] path location for creating temporary file @return file descriptor, or -1 on failure */ UNIV_INTERN int row_merge_file_create( -/*==================*/ - merge_file_t* merge_file) /*!< out: merge file structure */ + merge_file_t* merge_file, + const char* path) { - merge_file->fd = row_merge_file_create_low(); + merge_file->fd = row_merge_file_create_low(path); merge_file->offset = 0; merge_file->n_rec = 0; @@ -3620,10 +3687,6 @@ row_merge_build_indexes( } for (i = 0; i < n_indexes; i++) { - if (row_merge_file_create(&merge_files[i]) < 0) { - error = DB_OUT_OF_MEMORY; - goto func_exit; - } if (indexes[i]->type & DICT_FTS) { ibool opt_doc_id_size = FALSE; @@ -3652,13 +3715,6 @@ row_merge_build_indexes( } } - tmpfd = row_merge_file_create_low(); - - if (tmpfd < 0) { - error = DB_OUT_OF_MEMORY; - goto func_exit; - } - /* Reset the MySQL row buffer that is used when reporting duplicate keys. */ innobase_rec_reset(table); @@ -3670,7 +3726,7 @@ row_merge_build_indexes( trx, table, old_table, new_table, online, indexes, fts_sort_idx, psort_info, merge_files, key_numbers, n_indexes, add_cols, col_map, - add_autoinc, sequence, block); + add_autoinc, sequence, block, &tmpfd); if (error != DB_SUCCESS) { @@ -3751,7 +3807,7 @@ wait_again: #ifdef FTS_INTERNAL_DIAG_PRINT DEBUG_FTS_SORT_PRINT("FTS_SORT: Complete Insert\n"); #endif - } else { + } else if (merge_files[i].fd != -1) { row_merge_dup_t dup = { sort_idx, table, col_map, 0}; diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 121c8b210b8..36ff8d9b7ab 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. @@ -1733,6 +1733,8 @@ exit_func: /*********************************************************************//** A thread which prints warnings about semaphore waits which have lasted too long. These can be used to track bugs which cause hangs. +Note: In order to make sync_arr_wake_threads_if_sema_free work as expected, +we should avoid waiting any mutexes in this function! @return a dummy parameter */ extern "C" UNIV_INTERN os_thread_ret_t @@ -1772,23 +1774,21 @@ loop: /* Try to track a strange bug reported by Harald Fuchs and others, where the lsn seems to decrease at times */ - /* We have to use nowait to ensure we don't block */ - new_lsn= log_get_lsn_nowait(); + if (log_peek_lsn(&new_lsn)) { + if (new_lsn < old_lsn) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Error: old log sequence number " LSN_PF + " was greater\n" + "InnoDB: than the new log sequence number " LSN_PF "!\n" + "InnoDB: Please submit a bug report" + " to http://bugs.mysql.com\n", + old_lsn, new_lsn); + ut_ad(0); + } - if (new_lsn && new_lsn < old_lsn) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: old log sequence number " LSN_PF - " was greater\n" - "InnoDB: than the new log sequence number " LSN_PF "!\n" - "InnoDB: Please submit a bug report" - " to http://bugs.mysql.com\n", - old_lsn, new_lsn); - ut_ad(0); - } - - if (new_lsn) old_lsn = new_lsn; + } if (difftime(time(NULL), srv_last_monitor_time) > 60) { /* We referesh InnoDB Monitor values so that averages are diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 07d339209fa..32682de6be4 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -1894,7 +1894,7 @@ innobase_start_or_create_for_mysql(void) } } else { srv_monitor_file_name = NULL; - srv_monitor_file = os_file_create_tmpfile(); + srv_monitor_file = os_file_create_tmpfile(NULL); if (!srv_monitor_file) { return(DB_ERROR); @@ -1904,7 +1904,7 @@ innobase_start_or_create_for_mysql(void) mutex_create(srv_dict_tmpfile_mutex_key, &srv_dict_tmpfile_mutex, SYNC_DICT_OPERATION); - srv_dict_tmpfile = os_file_create_tmpfile(); + srv_dict_tmpfile = os_file_create_tmpfile(NULL); if (!srv_dict_tmpfile) { return(DB_ERROR); @@ -1913,7 +1913,7 @@ innobase_start_or_create_for_mysql(void) mutex_create(srv_misc_tmpfile_mutex_key, &srv_misc_tmpfile_mutex, SYNC_ANY_LATCH); - srv_misc_tmpfile = os_file_create_tmpfile(); + srv_misc_tmpfile = os_file_create_tmpfile(NULL); if (!srv_misc_tmpfile) { return(DB_ERROR);