summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2023-01-27 13:54:14 +0100
committerOleksandr Byelkin <sanja@mariadb.com>2023-01-27 13:54:14 +0100
commit7fa02f5c0ba4c3060cbff8cf9d1e472d268f3dad (patch)
treeb16c43d92abcb99316b2de31770c567e1c70bfa6
parent672cdcbb93a7355c715f3e232d4c5852209f30b5 (diff)
parentc8f9bb2718c4ed7b464504c54df961bfeb2cccca (diff)
downloadmariadb-git-7fa02f5c0ba4c3060cbff8cf9d1e472d268f3dad.tar.gz
Merge branch '10.4' into 10.5
-rw-r--r--CREDITS1
-rw-r--r--client/mysql.cc3
-rw-r--r--client/mysql_plugin.c84
-rw-r--r--client/mysqldump.c2
-rw-r--r--client/mysqltest.cc15
-rw-r--r--cmake/install_macros.cmake4
-rw-r--r--dbug/dbug.c2
-rw-r--r--extra/innochecksum.cc8
-rw-r--r--extra/mariabackup/backup_copy.cc49
-rw-r--r--extra/mariabackup/xbcloud.cc7
-rw-r--r--extra/mariabackup/xtrabackup.cc11
-rw-r--r--include/m_string.h38
-rw-r--r--mysql-test/main/cte_recursive.result172
-rw-r--r--mysql-test/main/cte_recursive.test123
-rw-r--r--mysql-test/main/ctype_utf8.result175
-rw-r--r--mysql-test/main/ctype_utf8.test158
-rw-r--r--mysql-test/main/derived_cond_pushdown.result63
-rw-r--r--mysql-test/main/derived_cond_pushdown.test49
-rw-r--r--mysql-test/main/long_unique_bugs.result105
-rw-r--r--mysql-test/main/long_unique_bugs.test200
-rw-r--r--mysql-test/main/rowid_filter.result58
-rw-r--r--mysql-test/main/select.result64
-rw-r--r--mysql-test/main/select.test40
-rw-r--r--mysql-test/main/select_jcl6.result64
-rw-r--r--mysql-test/main/select_pkeycache.result64
-rw-r--r--mysql-test/main/subselect2.result2
-rw-r--r--mysql-test/main/type_timestamp.result22
-rw-r--r--mysql-test/main/type_timestamp.test21
-rw-r--r--mysql-test/main/win.result46
-rw-r--r--mysql-test/main/win.test40
-rw-r--r--mysql-test/main/win_orderby.result63
-rw-r--r--mysql-test/main/win_orderby.test55
-rw-r--r--mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYDbin0 -> 40 bytes
-rw-r--r--mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYIbin0 -> 2048 bytes
-rw-r--r--mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.frmbin0 -> 970 bytes
-rw-r--r--mysql-test/suite/binlog/r/binlog_recovery_after_checksum_change.result8
-rw-r--r--mysql-test/suite/binlog/t/innodb_rc_insert_before_delete.test89
-rw-r--r--mysql-test/suite/encryption/r/tempfiles_encrypted.result46
-rw-r--r--mysql-test/suite/galera/t/galera_gcache_recover_manytrx.test5
-rw-r--r--mysql-test/suite/mariabackup/incremental_drop_db.result30
-rw-r--r--mysql-test/suite/mariabackup/incremental_drop_db.test68
-rw-r--r--mysql-test/suite/plugins/r/server_audit.result7
-rw-r--r--mysql-test/suite/plugins/t/server_audit.test5
-rw-r--r--mysql-test/suite/rpl/include/create_or_drop_sync_func.inc75
-rw-r--r--mysql-test/suite/rpl/r/rpl_delayed_parallel_slave_sbm.result60
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_analyze.result76
-rw-r--r--mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm-slave.opt1
-rw-r--r--mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm.test133
-rw-r--r--mysql-test/suite/rpl/t/rpl_parallel_analyze.test84
-rw-r--r--sql/CMakeLists.txt9
-rw-r--r--sql/discover.h2
-rw-r--r--sql/field.cc73
-rw-r--r--sql/field.h14
-rw-r--r--sql/ha_partition.cc15
-rw-r--r--sql/handler.cc32
-rw-r--r--sql/handler.h1
-rw-r--r--sql/item.cc2
-rw-r--r--sql/item.h13
-rw-r--r--sql/item_cmpfunc.cc83
-rw-r--r--sql/item_cmpfunc.h3
-rw-r--r--sql/item_func.cc20
-rw-r--r--sql/item_func.h12
-rw-r--r--sql/item_strfunc.cc12
-rw-r--r--sql/item_strfunc.h1
-rw-r--r--sql/item_sum.h9
-rw-r--r--sql/mysql_install_db.cc2
-rw-r--r--sql/mysqld.cc15
-rw-r--r--sql/rpl_parallel.cc3
-rw-r--r--sql/slave.cc31
-rw-r--r--sql/slave.h12
-rw-r--r--sql/sql_admin.cc41
-rw-r--r--sql/sql_alter.cc2
-rw-r--r--sql/sql_class.cc14
-rw-r--r--sql/sql_class.h31
-rw-r--r--sql/sql_cte.cc91
-rw-r--r--sql/sql_cte.h5
-rw-r--r--sql/sql_insert.cc3
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_lex.h7
-rw-r--r--sql/sql_list.h2
-rw-r--r--sql/sql_parse.cc16
-rw-r--r--sql/sql_plugin.cc51
-rw-r--r--sql/sql_select.cc88
-rw-r--r--sql/sql_select.h22
-rw-r--r--sql/sql_table.cc20
-rw-r--r--sql/sql_table.h4
-rw-r--r--sql/sql_type.h26
-rw-r--r--sql/table.cc17
-rw-r--r--sql/table.h16
-rw-r--r--sql/wsrep_mysqld.cc22
-rw-r--r--sql/wsrep_sst.cc11
-rw-r--r--storage/connect/array.cpp6
-rw-r--r--storage/connect/bson.cpp8
-rw-r--r--storage/connect/bsonudf.cpp20
-rw-r--r--storage/connect/filamdbf.cpp2
-rw-r--r--storage/connect/filamfix.cpp17
-rw-r--r--storage/connect/filamgz.cpp19
-rw-r--r--storage/connect/filamtxt.cpp10
-rw-r--r--storage/connect/filamvct.cpp39
-rw-r--r--storage/connect/filamzip.cpp10
-rw-r--r--storage/connect/javaconn.cpp27
-rw-r--r--storage/connect/json.cpp8
-rw-r--r--storage/connect/jsonudf.cpp21
-rw-r--r--storage/connect/myconn.cpp12
-rw-r--r--storage/mroonga/vendor/groonga/lib/alloc.c2
-rw-r--r--storage/spider/mysql-test/spider/bugfix/r/mdev_26541.result20
-rw-r--r--storage/spider/mysql-test/spider/bugfix/r/mdev_30370.result3
-rw-r--r--storage/spider/mysql-test/spider/bugfix/t/mdev_26541.test40
-rw-r--r--storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test5
-rw-r--r--storage/spider/spd_conn.cc19
-rw-r--r--storage/spider/spd_table.cc12
-rw-r--r--storage/spider/spd_trx.cc232
-rw-r--r--strings/json_lib.c2
m---------wsrep-lib0
114 files changed, 3235 insertions, 649 deletions
diff --git a/CREDITS b/CREDITS
index 9534d3e6e83..35604064980 100644
--- a/CREDITS
+++ b/CREDITS
@@ -9,6 +9,7 @@ MariaDB Corporation https://www.mariadb.com (2013)
Microsoft https://microsoft.com/ (2017)
ServiceNow https://servicenow.com (2019)
SIT https://sit.org (2022)
+Tencent Cloud https://cloud.tencent.com (2017)
Development Bank of Singapore https://dbs.com (2016)
IBM https://www.ibm.com (2017)
Automattic https://automattic.com (2019)
diff --git a/client/mysql.cc b/client/mysql.cc
index 697f4d54ab3..b1d2bc8def6 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -3606,7 +3606,10 @@ print_table_data(MYSQL_RES *result)
{
print_field_types(result);
if (!mysql_num_rows(result))
+ {
+ my_afree((uchar*) num_flag);
return;
+ }
mysql_field_seek(result,0);
}
separator.copy("+",1,charset_info);
diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c
index a00f5e4450b..f6a08558722 100644
--- a/client/mysql_plugin.c
+++ b/client/mysql_plugin.c
@@ -569,14 +569,14 @@ static int file_exists(char * filename)
@retval int error = 1, success = 0
*/
-static int search_dir(const char * base_path, const char *tool_name,
+static int search_dir(const char *base_path, const char *tool_name,
const char *subdir, char *tool_path)
{
char new_path[FN_REFLEN];
char source_path[FN_REFLEN];
- strcpy(source_path, base_path);
- strcat(source_path, subdir);
+ safe_strcpy(source_path, sizeof(source_path), base_path);
+ safe_strcat(source_path, sizeof(source_path), subdir);
fn_format(new_path, tool_name, source_path, "", MY_UNPACK_FILENAME);
if (file_exists(new_path))
{
@@ -632,7 +632,7 @@ static int load_plugin_data(char *plugin_name, char *config_file)
FILE *file_ptr;
char path[FN_REFLEN];
char line[1024];
- char *reason= 0;
+ const char *reason= 0;
char *res;
int i= -1;
@@ -643,14 +643,14 @@ static int load_plugin_data(char *plugin_name, char *config_file)
}
if (!file_exists(opt_plugin_ini))
{
- reason= (char *)"File does not exist.";
+ reason= "File does not exist.";
goto error;
}
file_ptr= fopen(opt_plugin_ini, "r");
if (file_ptr == NULL)
{
- reason= (char *)"Cannot open file.";
+ reason= "Cannot open file.";
goto error;
}
@@ -660,17 +660,20 @@ static int load_plugin_data(char *plugin_name, char *config_file)
/* Read plugin components */
while (i < 16)
{
+ size_t line_len;
+
res= fgets(line, sizeof(line), file_ptr);
+ line_len= strlen(line);
+
/* strip /n */
- if (line[strlen(line)-1] == '\n')
- {
- line[strlen(line)-1]= '\0';
- }
+ if (line[line_len - 1] == '\n')
+ line[line_len - 1]= '\0';
+
if (res == NULL)
{
if (i < 1)
{
- reason= (char *)"Bad format in plugin configuration file.";
+ reason= "Bad format in plugin configuration file.";
fclose(file_ptr);
goto error;
}
@@ -683,14 +686,19 @@ static int load_plugin_data(char *plugin_name, char *config_file)
if (i == -1) /* if first pass, read this line as so_name */
{
/* Add proper file extension for soname */
- strcat(line, FN_SOEXT);
+ if (safe_strcpy(line + line_len - 1, sizeof(line), FN_SOEXT))
+ {
+ reason= "Plugin name too long.";
+ fclose(file_ptr);
+ goto error;
+ }
/* save so_name */
plugin_data.so_name= my_strdup(PSI_NOT_INSTRUMENTED, line, MYF(MY_WME|MY_ZEROFILL));
i++;
}
else
{
- if (strlen(line) > 0)
+ if (line_len > 0)
{
plugin_data.components[i]= my_strdup(PSI_NOT_INSTRUMENTED, line, MYF(MY_WME));
i++;
@@ -779,14 +787,13 @@ static int check_options(int argc, char **argv, char *operation)
/* read the plugin config file and check for match against argument */
else
{
- if (strlen(argv[i]) + 4 + 1 > FN_REFLEN)
+ if (safe_strcpy(plugin_name, sizeof(plugin_name), argv[i]) ||
+ safe_strcpy(config_file, sizeof(config_file), argv[i]) ||
+ safe_strcat(config_file, sizeof(config_file), ".ini"))
{
fprintf(stderr, "ERROR: argument is too long.\n");
return 1;
}
- strcpy(plugin_name, argv[i]);
- strcpy(config_file, argv[i]);
- strcat(config_file, ".ini");
}
}
@@ -855,35 +862,30 @@ static int check_options(int argc, char **argv, char *operation)
static int process_options(int argc, char *argv[], char *operation)
{
int error= 0;
- int i= 0;
/* Parse and execute command-line options */
if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
- goto exit;
+ return error;
/* If the print defaults option used, exit. */
if (opt_print_defaults)
- {
- error= -1;
- goto exit;
- }
+ return -1;
/* Add a trailing directory separator if not present */
if (opt_basedir)
{
- i= (int)strlength(opt_basedir);
- if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2)
+ size_t basedir_len= strlength(opt_basedir);
+ if (opt_basedir[basedir_len - 1] != FN_LIBCHAR ||
+ opt_basedir[basedir_len - 1] != FN_LIBCHAR2)
{
char buff[FN_REFLEN];
- memset(buff, 0, sizeof(buff));
+ if (basedir_len + 2 > FN_REFLEN)
+ return -1;
- strncpy(buff, opt_basedir, sizeof(buff) - 1);
-#ifdef __WIN__
- strncat(buff, "/", sizeof(buff) - strlen(buff) - 1);
-#else
- strncat(buff, FN_DIRSEP, sizeof(buff) - strlen(buff) - 1);
-#endif
- buff[sizeof(buff) - 1]= 0;
+ memcpy(buff, opt_basedir, basedir_len);
+ buff[basedir_len]= '/';
+ buff[basedir_len + 1]= '\0';
+
my_free(opt_basedir);
opt_basedir= my_strdup(PSI_NOT_INSTRUMENTED, buff, MYF(MY_FAE));
}
@@ -895,10 +897,7 @@ static int process_options(int argc, char *argv[], char *operation)
generated when the defaults were read from the file, exit.
*/
if (!opt_no_defaults && ((error= get_default_values())))
- {
- error= -1;
- goto exit;
- }
+ return -1;
/*
Check to ensure required options are present and validate the operation.
@@ -906,11 +905,9 @@ static int process_options(int argc, char *argv[], char *operation)
read a configuration file named <plugin_name>.ini from the --plugin-dir
or --plugin-ini location if the --plugin-ini option presented.
*/
- strcpy(operation, "");
- if ((error = check_options(argc, argv, operation)))
- {
- goto exit;
- }
+ operation[0]= '\0';
+ if ((error= check_options(argc, argv, operation)))
+ return error;
if (opt_verbose)
{
@@ -922,8 +919,7 @@ static int process_options(int argc, char *argv[], char *operation)
printf("# lc_messages_dir = %s\n", opt_lc_messages_dir);
}
-exit:
- return error;
+ return 0;
}
diff --git a/client/mysqldump.c b/client/mysqldump.c
index ee1870ce1d4..e236fdf82f2 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -2515,7 +2515,7 @@ static uint dump_events_for_db(char *db)
if (mysql_query_with_error_report(mysql, &event_list_res, "show events"))
DBUG_RETURN(0);
- strcpy(delimiter, ";");
+ safe_strcpy(delimiter, sizeof(delimiter), ";");
if (mysql_num_rows(event_list_res) > 0)
{
if (opt_xml)
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 636ab06d745..97a19f2128c 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -6167,7 +6167,9 @@ int do_done(struct st_command *command)
if (*cur_block->delim)
{
/* Restore "old" delimiter after false if block */
- strcpy (delimiter, cur_block->delim);
+ if (safe_strcpy(delimiter, sizeof(delimiter), cur_block->delim))
+ die("Delimiter too long, truncated");
+
delimiter_length= strlen(delimiter);
}
/* Pop block from stack, goto next line */
@@ -6422,10 +6424,12 @@ void do_block(enum block_cmd cmd, struct st_command* command)
if (cur_block->ok)
{
cur_block->delim[0]= '\0';
- } else
+ }
+ else
{
/* Remember "old" delimiter if entering a false if block */
- strcpy (cur_block->delim, delimiter);
+ if (safe_strcpy(cur_block->delim, sizeof(cur_block->delim), delimiter))
+ die("Delimiter too long, truncated");
}
DBUG_PRINT("info", ("OK: %d", cur_block->ok));
@@ -11769,9 +11773,8 @@ static int setenv(const char *name, const char *value, int overwrite)
char *envvar= (char *)malloc(buflen);
if(!envvar)
return ENOMEM;
- strcpy(envvar, name);
- strcat(envvar, "=");
- strcat(envvar, value);
+
+ snprintf(envvar, buflen, "%s=%s", name, value);
putenv(envvar);
return 0;
}
diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake
index fba0bd03dac..344f0b8ecbb 100644
--- a/cmake/install_macros.cmake
+++ b/cmake/install_macros.cmake
@@ -179,8 +179,8 @@ IF(WIN32)
OPTION(SIGNCODE "Sign executables and dlls with digital certificate" OFF)
MARK_AS_ADVANCED(SIGNCODE)
IF(SIGNCODE)
- SET(SIGNTOOL_PARAMETERS
- /a /t http://timestamp.globalsign.com/?signature=sha2
+ SET(SIGNTOOL_PARAMETERS
+ /a /fd SHA256 /t http://timestamp.globalsign.com/?signature=sha2
CACHE STRING "parameters for signtool (list)")
IF(NOT SIGNTOOL_EXECUTABLE)
FILE(GLOB path_list
diff --git a/dbug/dbug.c b/dbug/dbug.c
index 17567585bfd..9c1371730be 100644
--- a/dbug/dbug.c
+++ b/dbug/dbug.c
@@ -508,7 +508,7 @@ static int DbugParse(CODE_STATE *cs, const char *control)
stack->delay= stack->next->delay;
stack->maxdepth= stack->next->maxdepth;
stack->sub_level= stack->next->sub_level;
- strcpy(stack->name, stack->next->name);
+ safe_strcpy(stack->name, sizeof(stack->name), stack->next->name);
stack->out_file= stack->next->out_file;
stack->out_file->used++;
if (stack->next == &init_settings)
diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc
index 5ac48f57fb1..1a6cbb3bfa1 100644
--- a/extra/innochecksum.cc
+++ b/extra/innochecksum.cc
@@ -837,7 +837,7 @@ parse_page(
{
unsigned long long id;
uint16_t undo_page_type;
- char str[20]={'\0'};
+ const char *str;
ulint n_recs;
uint32_t page_no, left_page_no, right_page_no;
ulint data_bytes;
@@ -845,11 +845,7 @@ parse_page(
ulint size_range_id;
/* Check whether page is doublewrite buffer. */
- if(skip_page) {
- strcpy(str, "Double_write_buffer");
- } else {
- strcpy(str, "-");
- }
+ str = skip_page ? "Double_write_buffer" : "-";
switch (fil_page_get_type(page)) {
diff --git a/extra/mariabackup/backup_copy.cc b/extra/mariabackup/backup_copy.cc
index 697dd2c837f..76300fc2c17 100644
--- a/extra/mariabackup/backup_copy.cc
+++ b/extra/mariabackup/backup_copy.cc
@@ -57,6 +57,9 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
#include "backup_copy.h"
#include "backup_mysql.h"
#include <btr0btr.h>
+#ifdef _WIN32
+#include <direct.h> /* rmdir */
+#endif
#define ROCKSDB_BACKUP_DIR "#rocksdb"
@@ -1618,7 +1621,49 @@ bool backup_finish()
return(true);
}
-bool
+
+/*
+ Drop all empty database directories in the base backup
+ that do not exists in the icremental backup.
+
+ This effectively re-plays all DROP DATABASE statements happened
+ in between base backup and incremental backup creation time.
+
+ Note, only checking if base_dir/db/ is empty is not enough,
+ because inc_dir/db/db.opt might have been dropped for some reasons,
+ which may also result into empty base_dir/db/.
+
+ Only the fact that at the same time:
+ - base_dir/db/ exists
+ - inc_dir/db/ does not exist
+ means that DROP DATABASE happened.
+*/
+static void
+ibx_incremental_drop_databases(const char *base_dir,
+ const char *inc_dir)
+{
+ datadir_node_t node;
+ datadir_node_init(&node);
+ datadir_iter_t *it = datadir_iter_new(base_dir);
+
+ while (datadir_iter_next(it, &node)) {
+ if (node.is_empty_dir) {
+ char path[FN_REFLEN];
+ snprintf(path, sizeof(path), "%s/%s",
+ inc_dir, node.filepath_rel);
+ if (!directory_exists(path, false)) {
+ msg("Removing %s", node.filepath);
+ rmdir(node.filepath);
+ }
+ }
+
+ }
+ datadir_iter_free(it);
+ datadir_node_free(&node);
+}
+
+
+static bool
ibx_copy_incremental_over_full()
{
const char *ext_list[] = {"frm", "isl", "MYD", "MYI", "MAD", "MAI",
@@ -1701,6 +1746,8 @@ ibx_copy_incremental_over_full()
}
copy_or_move_dir(path, ROCKSDB_BACKUP_DIR, true, true);
}
+ ibx_incremental_drop_databases(xtrabackup_target_dir,
+ xtrabackup_incremental_dir);
}
diff --git a/extra/mariabackup/xbcloud.cc b/extra/mariabackup/xbcloud.cc
index fed937be834..cee76e5f3d7 100644
--- a/extra/mariabackup/xbcloud.cc
+++ b/extra/mariabackup/xbcloud.cc
@@ -1676,8 +1676,11 @@ container_list_add_object(container_list *list, const char *name,
list->object_count += object_count_step;
}
assert(list->idx <= list->object_count);
- strcpy(list->objects[list->idx].name, name);
- strcpy(list->objects[list->idx].hash, hash);
+ safe_strcpy(list->objects[list->idx].name,
+ sizeof(list->objects[list->idx].name), name);
+ safe_strcpy(list->objects[list->idx].hash,
+ sizeof(list->objects[list->idx].hash), hash);
+
list->objects[list->idx].bytes = bytes;
++list->idx;
}
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index 2c8b83d3a67..13bfe129b3b 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -4409,11 +4409,13 @@ static bool xtrabackup_backup_low()
return false;
}
- if(!xtrabackup_incremental) {
- strcpy(metadata_type, "full-backuped");
+ if (!xtrabackup_incremental) {
+ safe_strcpy(metadata_type, sizeof(metadata_type),
+ "full-backuped");
metadata_from_lsn = 0;
} else {
- strcpy(metadata_type, "incremental");
+ safe_strcpy(metadata_type, sizeof(metadata_type),
+ "incremental");
metadata_from_lsn = incremental_lsn;
}
metadata_last_lsn = log_copy_scanned_lsn;
@@ -6074,7 +6076,8 @@ static bool xtrabackup_prepare_func(char** argv)
if (ok) {
char filename[FN_REFLEN];
- strcpy(metadata_type, "log-applied");
+ safe_strcpy(metadata_type, sizeof(metadata_type),
+ "log-applied");
if(xtrabackup_incremental
&& metadata_to_lsn < incremental_to_lsn)
diff --git a/include/m_string.h b/include/m_string.h
index e4c11d24c0b..7857993bc92 100644
--- a/include/m_string.h
+++ b/include/m_string.h
@@ -226,6 +226,44 @@ static inline void lex_string_set3(LEX_CSTRING *lex_str, const char *c_str,
lex_str->length= len;
}
+/*
+ Copies src into dst and ensures dst is a NULL terminated C string.
+
+ Returns 1 if the src string was truncated due to too small size of dst.
+ Returns 0 if src completely fit within dst. Pads the remaining dst with '\0'
+
+ Note: dst_size must be > 0
+*/
+static inline int safe_strcpy(char *dst, size_t dst_size, const char *src)
+{
+ memset(dst, '\0', dst_size);
+ strncpy(dst, src, dst_size - 1);
+ /*
+ If the first condition is true, we are guaranteed to have src length
+ >= (dst_size - 1), hence safe to access src[dst_size - 1].
+ */
+ if (dst[dst_size - 2] != '\0' && src[dst_size - 1] != '\0')
+ return 1; /* Truncation of src. */
+ return 0;
+}
+
+/*
+ Appends src to dst and ensures dst is a NULL terminated C string.
+
+ Returns 1 if the src string was truncated due to too small size of dst.
+ Returns 0 if src completely fit within the remaining dst space. Pads the
+ remaining dst with '\0'.
+
+ Note: dst_size must be > 0
+*/
+static inline int safe_strcat(char *dst, size_t dst_size, const char *src)
+{
+ size_t init_len= strlen(dst);
+ if (unlikely(init_len >= dst_size - 1))
+ return 1;
+ return safe_strcpy(dst + init_len, dst_size - init_len, src);
+}
+
#ifdef __cplusplus
static inline char *safe_str(char *str)
{ return str ? str : const_cast<char*>(""); }
diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result
index b3a2c66e3d0..dd82ef46229 100644
--- a/mysql-test/main/cte_recursive.result
+++ b/mysql-test/main/cte_recursive.result
@@ -5603,6 +5603,178 @@ r
3
drop table t1,t2,t3,x;
#
+# MDEV-30248: Embedded non-recursive CTE referring to base table 'x'
+# within a CTE with name 'x' used in a subquery from
+# select list of another CTE
+#
+CREATE TABLE x (a int) ENGINE=MyISAM;
+INSERT INTO x VALUES (3),(7),(1);
+CREATE TABLE t1 (b int) ENGINE=MYISAM;
+INSERT INTO t1 VALUES (1);
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+c
+1
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT b FROM t1)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+c
+1
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH y AS (SELECT a FROM x AS t) SELECT b FROM t1)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+c
+1
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH y(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM y)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+c
+3
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM x)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+c
+3
+WITH x AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT x.c from x;
+c
+1
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 2 AS b)
+SELECT r1.b FROM x AS r1, x AS r2 WHERE r1.b=r2.b
+) AS c
+)
+SELECT cte.c from cte;
+c
+2
+DROP TABLE x;
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT b FROM t1)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH y AS (SELECT a FROM x AS t) SELECT b FROM t1)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH y(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM y)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM x)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT cte.c FROM cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH x AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+SELECT b FROM x AS r
+) AS c
+)
+SELECT x.c from x;
+ERROR 42S02: Table 'test.x' doesn't exist
+WITH cte AS
+(
+SELECT
+(
+WITH x AS
+(WITH x AS (SELECT a FROM x AS t) SELECT 2 AS b)
+SELECT r1.b FROM x AS r1, x AS r2 WHERE r1.b=r2.b
+) AS c
+)
+SELECT cte.c from cte;
+ERROR 42S02: Table 'test.x' doesn't exist
+DROP TABLE t1;
+#
# End of 10.3 tests
#
#
diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test
index 8efbdbf9677..ebea3b96754 100644
--- a/mysql-test/main/cte_recursive.test
+++ b/mysql-test/main/cte_recursive.test
@@ -3875,6 +3875,129 @@ select * from cte;
drop table t1,t2,t3,x;
--echo #
+--echo # MDEV-30248: Embedded non-recursive CTE referring to base table 'x'
+--echo # within a CTE with name 'x' used in a subquery from
+--echo # select list of another CTE
+--echo #
+
+CREATE TABLE x (a int) ENGINE=MyISAM;
+INSERT INTO x VALUES (3),(7),(1);
+CREATE TABLE t1 (b int) ENGINE=MYISAM;
+INSERT INTO t1 VALUES (1);
+
+let $q1=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT cte.c FROM cte;
+eval $q1;
+
+let $q2=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH x AS (SELECT a FROM x AS t) SELECT b FROM t1)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT cte.c FROM cte;
+eval $q2;
+
+let $q3=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH y AS (SELECT a FROM x AS t) SELECT b FROM t1)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT cte.c FROM cte;
+eval $q3;
+
+
+let $q4=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH y(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM y)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT cte.c FROM cte;
+eval $q4;
+
+let $q5=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH x(b) AS (SELECT a FROM x AS t LIMIT 1) SELECT b FROM x)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT cte.c FROM cte;
+eval $q5;
+
+let $q6=
+WITH x AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH x AS (SELECT a FROM x AS t) SELECT 1 AS b)
+ SELECT b FROM x AS r
+ ) AS c
+)
+SELECT x.c from x;
+eval $q6;
+
+let $q7=
+WITH cte AS
+(
+ SELECT
+ (
+ WITH x AS
+ (WITH x AS (SELECT a FROM x AS t) SELECT 2 AS b)
+ SELECT r1.b FROM x AS r1, x AS r2 WHERE r1.b=r2.b
+ ) AS c
+)
+SELECT cte.c from cte;
+eval $q7;
+
+
+DROP TABLE x;
+
+--ERROR ER_NO_SUCH_TABLE
+eval $q1;
+--ERROR ER_NO_SUCH_TABLE
+eval $q2;
+--ERROR ER_NO_SUCH_TABLE
+eval $q3;
+--ERROR ER_NO_SUCH_TABLE
+eval $q4;
+--ERROR ER_NO_SUCH_TABLE
+eval $q5;
+--ERROR ER_NO_SUCH_TABLE
+eval $q6;
+--ERROR ER_NO_SUCH_TABLE
+eval $q7;
+
+DROP TABLE t1;
+
+--echo #
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/ctype_utf8.result b/mysql-test/main/ctype_utf8.result
index 704b7415ea1..46b9a25b332 100644
--- a/mysql-test/main/ctype_utf8.result
+++ b/mysql-test/main/ctype_utf8.result
@@ -11383,6 +11383,181 @@ a
# End of 10.3 tests
#
#
+# Start of 10.4 tests
+#
+#
+# MDEV-27653 long uniques don't work with unicode collations
+#
+SET NAMES utf8mb3;
+CREATE TABLE t1 (
+a CHAR(30) COLLATE utf8mb3_general_ci,
+UNIQUE KEY(a) USING HASH
+);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+CREATE TABLE t1 (
+a CHAR(30) COLLATE utf8mb3_general_ci,
+UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`(10)) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+CREATE TABLE t1 (
+a VARCHAR(30) COLLATE utf8mb3_general_ci,
+UNIQUE KEY(a) USING HASH
+);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+CREATE TABLE t1 (
+a VARCHAR(30) COLLATE utf8mb3_general_ci,
+UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`(10)) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+CREATE TABLE t1 (a TEXT COLLATE utf8mb3_general_ci UNIQUE);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+CREATE TABLE t1 (
+a LONGTEXT COLLATE utf8mb3_general_ci,
+UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` longtext CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`(10)) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+INSERT INTO t1 VALUES ('a');
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+SELECT * FROM t1;
+a
+a
+DROP TABLE t1;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+ä 2
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check error Upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it!
+INSERT INTO t1 VALUES ('A');
+ERROR 23000: Duplicate entry 'A' for key 'a'
+INSERT INTO t1 VALUES ('Ä');
+ERROR 23000: Duplicate entry 'Ä' for key 'a'
+INSERT INTO t1 VALUES ('Ấ');
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+ä 2
+Ấ 3
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check error Upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it!
+ALTER TABLE t1 FORCE;
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+DELETE FROM t1 WHERE OCTET_LENGTH(a)>1;
+ALTER TABLE t1 FORCE;
+INSERT INTO t1 VALUES ('ä');
+ERROR 23000: Duplicate entry 'ä' for key 'a'
+DROP TABLE t1;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+ä 2
+ALTER IGNORE TABLE t1 FORCE;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+DROP TABLE t1;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
+ UNIQUE KEY `a` (`a`) USING HASH
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+ä 2
+REPAIR TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 repair Warning Number of rows changed from 2 to 1
+test.t1 repair status OK
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+a OCTET_LENGTH(a)
+a 1
+DROP TABLE t1;
+#
+# End of 10.4 tests
+#
+#
# Start of 10.5 tests
#
#
diff --git a/mysql-test/main/ctype_utf8.test b/mysql-test/main/ctype_utf8.test
index 1e3cbffa622..3461a5cb206 100644
--- a/mysql-test/main/ctype_utf8.test
+++ b/mysql-test/main/ctype_utf8.test
@@ -2314,6 +2314,164 @@ VALUES (_latin1 0xDF) UNION VALUES(_utf8'a' COLLATE utf8_bin);
--echo #
+--echo # Start of 10.4 tests
+--echo #
+
+--echo #
+--echo # MDEV-27653 long uniques don't work with unicode collations
+--echo #
+
+SET NAMES utf8mb3;
+
+# CHAR
+
+CREATE TABLE t1 (
+ a CHAR(30) COLLATE utf8mb3_general_ci,
+ UNIQUE KEY(a) USING HASH
+);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (
+ a CHAR(30) COLLATE utf8mb3_general_ci,
+ UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+
+# VARCHAR
+
+CREATE TABLE t1 (
+ a VARCHAR(30) COLLATE utf8mb3_general_ci,
+ UNIQUE KEY(a) USING HASH
+);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (
+ a VARCHAR(30) COLLATE utf8mb3_general_ci,
+ UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+
+# TEXT
+
+CREATE TABLE t1 (a TEXT COLLATE utf8mb3_general_ci UNIQUE);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (
+ a LONGTEXT COLLATE utf8mb3_general_ci,
+ UNIQUE KEY(a(10)) USING HASH
+);
+SHOW CREATE TABLE t1;
+INSERT INTO t1 VALUES ('a');
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+
+# Testing upgrade:
+# Prior to MDEV-27653, the UNIQUE HASH function errorneously
+# took into account string octet length.
+# Old tables should still open and work, but with wrong results.
+
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm $MYSQLD_DATADIR/test/t1.frm;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD $MYSQLD_DATADIR/test/t1.MYD;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI $MYSQLD_DATADIR/test/t1.MYI;
+SHOW CREATE TABLE t1;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+CHECK TABLE t1;
+
+# There is already a one byte value 'a' in the table
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('A');
+
+# There is already a two-byte value 'ä' in the table
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('Ä');
+
+# There were no three-byte values in the table so far.
+# The below value violates UNIQUE, but it gets inserted.
+# This is wrong but expected for a pre-MDEV-27653 table.
+INSERT INTO t1 VALUES ('Ấ');
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+CHECK TABLE t1;
+
+# ALTER FORCE fails: it tries to rebuild the table
+# with a correct UNIQUE HASH function, but there are duplicates!
+--error ER_DUP_ENTRY
+ALTER TABLE t1 FORCE;
+
+# Let's remove all duplicate values, so only the one-byte 'a' stays.
+# ALTER..FORCE should work after that.
+DELETE FROM t1 WHERE OCTET_LENGTH(a)>1;
+ALTER TABLE t1 FORCE;
+
+# Make sure that 'a' and 'ä' cannot co-exists any more,
+# because the table was recreated with a correct UNIQUE HASH function.
+--error ER_DUP_ENTRY
+INSERT INTO t1 VALUES ('ä');
+DROP TABLE t1;
+
+#
+# Testing an old table with ALTER IGNORE.
+# The table is expected to rebuild with a new hash function,
+# duplicates go away.
+#
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm $MYSQLD_DATADIR/test/t1.frm;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD $MYSQLD_DATADIR/test/t1.MYD;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI $MYSQLD_DATADIR/test/t1.MYI;
+SHOW CREATE TABLE t1;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+ALTER IGNORE TABLE t1 FORCE;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+DROP TABLE t1;
+
+#
+# Testing an old table with REPAIR.
+# The table is expected to rebuild with a new hash function,
+# duplicates go away.
+#
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm $MYSQLD_DATADIR/test/t1.frm;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD $MYSQLD_DATADIR/test/t1.MYD;
+copy_file std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI $MYSQLD_DATADIR/test/t1.MYI;
+SHOW CREATE TABLE t1;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+REPAIR TABLE t1;
+SELECT a, OCTET_LENGTH(a) FROM t1 ORDER BY BINARY a;
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.4 tests
+--echo #
+
+
+--echo #
--echo # Start of 10.5 tests
--echo #
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 275459bd868..4fc9a0a213f 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -18162,6 +18162,69 @@ DROP TABLE transaction_items;
DROP TABLE transactions;
DROP TABLE charges;
DROP TABLE ledgers;
+#
+# MDEV-30081: Splitting from a constant mergeable derived table
+# used in inner part of an outer join.
+#
+CREATE TABLE t1 ( id int PRIMARY KEY ) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (3),(4),(7);
+CREATE TABLE t2 (
+id int, id1 int, wid int, PRIMARY KEY (id), KEY (id1), KEY (wid)
+) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (4,4,6),(7,7,7);
+CREATE TABLE t3 (
+wid int, wtid int, otid int, oid int,
+PRIMARY KEY (wid), KEY (wtid), KEY (otid), KEY (oid)
+) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (6,30,6,6),(7,17,7,7);
+CREATE TABLE t4 ( id int, a int, PRIMARY KEY (id), KEY (a) ) ENGINE=MyISAM;
+INSERT INTO t4 VALUES (1,17),(2,15),(3,49),(4,3),(5,45),(6,38),(7,17);
+CREATE TABLE t5 (
+id int, id1 int, PRIMARY KEY (id), KEY id1 (id1)
+) ENGINE=MyISAM ;
+INSERT INTO t5 VALUES (1,17),(2,15),(3,49),(4,3),(5,45),(6,38),(7,17);
+ANALYZE TABLE t1,t2,t3,t4,t5;
+Table Op Msg_type Msg_text
+test.t1 analyze status Engine-independent statistics collected
+test.t1 analyze status OK
+test.t2 analyze status Engine-independent statistics collected
+test.t2 analyze status OK
+test.t3 analyze status Engine-independent statistics collected
+test.t3 analyze status OK
+test.t4 analyze status Engine-independent statistics collected
+test.t4 analyze status OK
+test.t5 analyze status Engine-independent statistics collected
+test.t5 analyze status OK
+CREATE VIEW v1 AS (SELECT id1 FROM t5 GROUP BY id1);
+SELECT t3.*, t1.id AS t1_id, t2.id AS t2_id, dt.*, v1.*
+FROM
+t1, t2, t3
+LEFT JOIN
+(SELECT t4.* FROM t4 WHERE t4.a=3) dt
+ON t3.oid = dt.id AND t3.otid = 14
+LEFT JOIN v1
+ON (v1.id1 = dt.a)
+WHERE t3.oid = t1.id AND t3.oid = t2.id AND t3.wid = 7;
+wid wtid otid oid t1_id t2_id id a id1
+7 17 7 7 7 7 NULL NULL NULL
+EXPLAIN SELECT t3.*, t1.id AS t1_id, t2.id AS t2_id, dt.*, v1.*
+FROM
+t1, t2, t3
+LEFT JOIN
+(SELECT t4.* FROM t4 WHERE t4.a=3) dt
+ON t3.oid = dt.id AND t3.otid = 14
+LEFT JOIN v1
+ON (v1.id1 = dt.a)
+WHERE t3.oid = t1.id AND t3.oid = t2.id AND t3.wid = 7;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 const PRIMARY,oid PRIMARY 4 const 1
+1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
+1 PRIMARY t2 const PRIMARY PRIMARY 4 const 1 Using index
+1 PRIMARY t4 const PRIMARY,a NULL NULL NULL 1 Impossible ON condition
+1 PRIMARY <derived3> ref key0 key0 5 const 0 Using where
+3 LATERAL DERIVED t5 ref id1 id1 5 const 0 Using index
+DROP VIEW v1;
+DROP TABLE t1,t2,t3,t4,t5;
# End of 10.3 tests
#
# MDEV-18679: materialized view with SELECT S containing materialized
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index bd38716f2a6..6cfe23b7866 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -3870,6 +3870,55 @@ DROP TABLE transactions;
DROP TABLE charges;
DROP TABLE ledgers;
+
+--echo #
+--echo # MDEV-30081: Splitting from a constant mergeable derived table
+--echo # used in inner part of an outer join.
+--echo #
+
+ CREATE TABLE t1 ( id int PRIMARY KEY ) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (3),(4),(7);
+
+CREATE TABLE t2 (
+ id int, id1 int, wid int, PRIMARY KEY (id), KEY (id1), KEY (wid)
+) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (4,4,6),(7,7,7);
+
+CREATE TABLE t3 (
+ wid int, wtid int, otid int, oid int,
+ PRIMARY KEY (wid), KEY (wtid), KEY (otid), KEY (oid)
+) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (6,30,6,6),(7,17,7,7);
+
+CREATE TABLE t4 ( id int, a int, PRIMARY KEY (id), KEY (a) ) ENGINE=MyISAM;
+INSERT INTO t4 VALUES (1,17),(2,15),(3,49),(4,3),(5,45),(6,38),(7,17);
+
+CREATE TABLE t5 (
+ id int, id1 int, PRIMARY KEY (id), KEY id1 (id1)
+) ENGINE=MyISAM ;
+INSERT INTO t5 VALUES (1,17),(2,15),(3,49),(4,3),(5,45),(6,38),(7,17);
+
+ANALYZE TABLE t1,t2,t3,t4,t5;
+
+CREATE VIEW v1 AS (SELECT id1 FROM t5 GROUP BY id1);
+
+let $q=
+SELECT t3.*, t1.id AS t1_id, t2.id AS t2_id, dt.*, v1.*
+FROM
+ t1, t2, t3
+ LEFT JOIN
+ (SELECT t4.* FROM t4 WHERE t4.a=3) dt
+ ON t3.oid = dt.id AND t3.otid = 14
+ LEFT JOIN v1
+ ON (v1.id1 = dt.a)
+WHERE t3.oid = t1.id AND t3.oid = t2.id AND t3.wid = 7;
+
+eval $q;
+eval EXPLAIN $q;
+
+DROP VIEW v1;
+DROP TABLE t1,t2,t3,t4,t5;
+
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/long_unique_bugs.result b/mysql-test/main/long_unique_bugs.result
index 4b47deee7f1..2cb54e0acff 100644
--- a/mysql-test/main/long_unique_bugs.result
+++ b/mysql-test/main/long_unique_bugs.result
@@ -1,3 +1,6 @@
+#
+# MDEV-18707 Server crash in my_hash_sort_bin, ASAN heap-use-after-free in Field::is_null, server hang, corrupted double-linked list
+#
create table t1 (a int, b int, c int, d int, e int);
insert into t1 () values
(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),
@@ -11,6 +14,9 @@ load data infile 'load.data' into table tmp;
delete from tmp;
drop table t1;
drop table tmp;
+#
+# MDEV-18712 InnoDB indexes are inconsistent with what defined in .frm for table after rebuilding table with index on blob
+#
create table t1 (b blob) engine=innodb;
alter table t1 add unique (b);
alter table t1 force;
@@ -21,12 +27,18 @@ t1 CREATE TABLE `t1` (
UNIQUE KEY `b` (`b`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
drop table t1;
+#
+# MDEV-18713 Assertion `strcmp(share->unique_file_name,filename) || share->last_version' failed in test_if_reopen upon REPLACE into table with key on blob
+#
create table t1 (pk int, b blob, primary key(pk), unique(b)) engine=myisam;
insert into t1 values (1,'foo');
replace into t1 (pk) values (1);
alter table t1 force;
replace into t1 (pk) values (1);
drop table t1;
+#
+# MDEV-18722 Assertion `templ->mysql_null_bit_mask' failed in row_sel_store_mysql_rec upon modifying indexed column into blob
+#
create table t1 (t time, unique(t)) engine=innodb;
insert into t1 values (null),(null);
alter ignore table t1 modify t text not null default '';
@@ -34,6 +46,9 @@ Warnings:
Warning 1265 Data truncated for column 't' at row 1
Warning 1265 Data truncated for column 't' at row 2
drop table t1;
+#
+# MDEV-18720 Assertion `inited==NONE' failed in ha_index_init upon update on versioned table with key on blob
+#
create table t1 ( pk int, f text, primary key (pk), unique(f)) with system versioning;
insert into t1 values (1,'foo');
update t1 set f = 'bar';
@@ -50,20 +65,32 @@ pk f row_end > DATE'2030-01-01'
1 foo 0
1 bar 0
drop table t1;
+#
+# MDEV-18747 InnoDB: Failing assertion: table->get_ref_count() == 0 upon dropping temporary table with unique blob
+#
create temporary table t1 (f blob, unique(f)) engine=innodb;
insert into t1 values (1);
replace into t1 values (1);
drop table t1;
+#
+# MDEV-18748 REPLACE doesn't work with unique blobs on MyISAM table
+#
create table t (b blob, unique(b)) engine=myisam;
insert into t values ('foo');
replace into t values ('foo');
drop table t;
+#
+# MDEV-18790 Server crash in fields_in_hash_keyinfo after unsuccessful attempt to drop BLOB with long index
+#
CREATE TABLE t1 (f INT, x BLOB, UNIQUE (x));
INSERT INTO t1 VALUES (1,'foo');
ALTER TABLE t1 DROP x, ALGORITHM=INPLACE;
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY
UPDATE t1 SET x = 'bar';
DROP TABLE t1;
+#
+# MDEV-18799 Long unique does not work after failed alter table
+#
create table t1(a blob unique , b blob);
insert into t1 values(1,1),(2,1);
alter table t1 add unique(b);
@@ -85,16 +112,26 @@ Index_comment
insert into t1 values(1,1);
ERROR 23000: Duplicate entry '1' for key 'a'
DROP TABLE t1;
+#
+# MDEV-18792 ASAN unknown-crash in _mi_pack_key upon UPDATE after failed ALTER on a table with long BLOB key
+#
CREATE TABLE t1 (a TEXT, b INT, UNIQUE(a)) ENGINE=MyISAM;
ALTER TABLE t1 DROP x;
ERROR 42000: Can't DROP COLUMN `x`; check that it exists
UPDATE t1 SET b = 0 WHERE a = 'foo';
DROP TABLE t1;
+#
+# MDEV-18793 Assertion `0' failed in row_sel_convert_mysql_key_to_innobase, ASAN unknown-crash in
+# row_mysql_store_col_in_innobase_format, warning " InnoDB: Using a partial-field key prefix in search"
+#
CREATE TABLE t1 (a TEXT, b INT, UNIQUE(a)) ENGINE=InnoDB;
ALTER TABLE t1 DROP x;
ERROR 42000: Can't DROP COLUMN `x`; check that it exists
UPDATE t1 SET b = 0 WHERE a = 'foo';
DROP TABLE t1;
+#
+# MDEV-18795 InnoDB: Failing assertion: field->prefix_len > 0 upon DML on table with BLOB index
+#
CREATE TEMPORARY TABLE t1 (f BLOB, UNIQUE(f)) ENGINE=InnoDB ROW_FORMAT=COMPACT;
ALTER TABLE t1 ADD KEY (f);
ERROR HY000: Index column size too large. The maximum column size is 767 bytes
@@ -102,17 +139,29 @@ TRUNCATE TABLE t1;
SELECT * FROM t1 WHERE f LIKE 'foo';
f
DROP TABLE t1;
+#
+# MDEV-18798 InnoDB: No matching column for `DB_ROW_HASH_1`and server crash in
+# ha_innobase::commit_inplace_alter_table upon ALTER on table with UNIQUE key
+#
CREATE TABLE t1 (a INT, UNIQUE ind USING HASH (a)) ENGINE=InnoDB;
ALTER TABLE t1 CHANGE COLUMN IF EXISTS b a INT;
Warnings:
Note 1054 Unknown column 'b' in 't1'
DROP TABLE t1;
+#
+# MDEV-18801 InnoDB: Failing assertion: field->col->mtype == type or ASAN heap-buffer-overflow
+# in row_sel_convert_mysql_key_to_innobase upon SELECT on table with long index
+#
CREATE TABLE t1 (f VARCHAR(4096), UNIQUE(f)) ENGINE=InnoDB;
ALTER TABLE t1 DROP x;
ERROR 42000: Can't DROP COLUMN `x`; check that it exists
SELECT * FROM t1 WHERE f LIKE 'foo';
f
DROP TABLE t1;
+#
+# MDEV-18800 Server crash in instant_alter_column_possible or
+# Assertion `!pk->has_virtual()' failed in instant_alter_column_possible upon adding key
+#
CREATE TABLE t1 (pk INT, PRIMARY KEY USING HASH (pk)) ENGINE=InnoDB;
show keys from t1;;
Table t1
@@ -130,6 +179,9 @@ Comment
Index_comment
ALTER TABLE t1 ADD INDEX (pk);
DROP TABLE t1;
+#
+# MDEV-18922 Alter on long unique varchar column makes result null
+#
CREATE TABLE t1 (b int, a varchar(4000));
INSERT INTO t1 VALUES (1, 2),(2,3),(3,4);
ALTER TABLE t1 ADD UNIQUE INDEX (a);
@@ -144,6 +196,10 @@ a
3
4
drop table t1;
+#
+# MDEV-18809 Server crash in fields_in_hash_keyinfo or Assertion `key_info->key_part->field->flags
+# & (1<< 30)' failed in setup_keyinfo_hash
+#
CREATE TABLE t1 (f VARCHAR(4096), UNIQUE(f)) ENGINE=InnoDB;
ALTER TABLE t1 DROP KEY f, ADD INDEX idx1(f), ALGORITHM=INSTANT;
ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY
@@ -159,6 +215,9 @@ insert into t1 values(1,1);
ERROR 23000: Duplicate entry '1-1' for key 'a'
alter table t1 add column c int;
drop table t1;
+#
+# MDEV-18889 Long unique on virtual fields crashes server
+#
create table t1(a blob , b blob as (a) unique);
insert into t1 values(1, default);
insert into t1 values(1, default);
@@ -172,6 +231,9 @@ insert into t1(a,b) values(2,2);
insert into t1(a,b) values(2,3);
insert into t1(a,b) values(3,2);
drop table t1;
+#
+# MDEV-18888 Server crashes in Item_field::register_field_in_read_map upon MODIFY COLUMN
+#
CREATE TABLE t1 (
a CHAR(128),
b CHAR(128) AS (a),
@@ -187,6 +249,9 @@ c varchar(5000),
UNIQUE(c,b(64))
) ENGINE=InnoDB;
drop table t1;
+#
+# MDEV-18967 Load data in system version with long unique does not work
+#
CREATE TABLE t1 (data VARCHAR(4), unique(data) using hash) with system versioning;
INSERT INTO t1 VALUES ('A');
SELECT * INTO OUTFILE 'load.data' from t1;
@@ -196,6 +261,9 @@ select * from t1;
data
A
DROP TABLE t1;
+#
+# MDEV-18901 Wrong results after ADD UNIQUE INDEX(blob_column)
+#
CREATE TABLE t1 (data VARCHAR(7961)) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('f'), ('o'), ('o');
SELECT * INTO OUTFILE 'load.data' from t1;
@@ -214,12 +282,16 @@ SELECT * FROM t1;
data
f
o
+# This should be equivalent to the REPLACE above
LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1;
SELECT * FROM t1;
data
f
o
DROP TABLE t1;
+#
+# MDEV-18953 Hash index on partial char field not working
+#
create table t1 (
c char(10) character set utf8mb4,
unique key a using hash (c(1))
@@ -236,10 +308,16 @@ ERROR 23000: Duplicate entry 'Ð' for key 'a'
insert into t1 values ('ббб');
ERROR 23000: Duplicate entry 'Ð' for key 'a'
drop table t1;
+#
+# MDEV-18904 Assertion `m_part_spec.start_part >= m_part_spec.end_part' failed in ha_partition::index_read_idx_map
+#
CREATE TABLE t1 (a INT, UNIQUE USING HASH (a)) PARTITION BY HASH (a) PARTITIONS 2;
INSERT INTO t1 VALUES (2);
REPLACE INTO t1 VALUES (2);
DROP TABLE t1;
+#
+# MDEV-18820 Assertion `lock_table_has(trx, index->table, LOCK_IX)' failed in lock_rec_insert_check_and_lock upon INSERT into table with blob key'
+#
set innodb_lock_wait_timeout= 10;
CREATE TABLE t1 (
id int primary key,
@@ -266,11 +344,20 @@ ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
disconnect con1;
connection default;
DROP TABLE t1, t2;
+#
+# MDEV-18791 Wrong error upon creating Aria table with long index on BLOB
+#
CREATE TABLE t1 (a TEXT, UNIQUE(a)) ENGINE=Aria;
ERROR 42000: Specified key was too long; max key length is 2000 bytes
+#
+# MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes
+#
create table t1(a int, unique(a) using hash);
#BULK insert > 100 rows (MI_MIN_ROWS_TO_DISABLE_INDEXES)
drop table t1;
+#
+# MDEV-21804 Assertion `marked_for_read()' failed upon INSERT into table with long unique blob under binlog_row_image=NOBLOB
+#
SET binlog_row_image= NOBLOB;
CREATE TABLE t1 (pk INT PRIMARY KEY, a text ,UNIQUE(a) using hash);
INSERT INTO t1 VALUES (1,'foo');
@@ -278,6 +365,9 @@ create table t2(id int primary key, a blob, b varchar(20) as (LEFT(a,2)));
INSERT INTO t2 VALUES (1, 'foo', default);
DROP TABLE t1, t2;
SET binlog_row_image= FULL;
+#
+# MDEV-22719 Long unique keys are not created when individual key_part->length < max_key_length but SUM(key_parts->length) > max_key_length
+#
CREATE TABLE t1 (a int, b VARCHAR(1000), UNIQUE (a,b)) ENGINE=MyISAM;
show index from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
@@ -315,6 +405,9 @@ ERROR 23000: Duplicate entry '1' for key 'v2'
update t1,t2 set v1 = v2 , v5 = 0;
ERROR 23000: Duplicate entry '-128' for key 'v1'
drop table t1, t2;
+#
+# MDEV-23264 Unique blobs allow duplicate values upon UPDATE
+#
CREATE TABLE t1 (f TEXT UNIQUE);
INSERT INTO t1 VALUES (NULL),(NULL);
UPDATE t1 SET f = '';
@@ -344,6 +437,18 @@ partition n0 values less than (10),
partition n1 values less than (50));
drop table t1;
#
+# MDEV-29199 Unique hash key is ignored upon INSERT ... SELECT into non-empty MyISAM table
+#
+create table t1 (a int, b text, unique(b)) engine=MyISAM;
+insert into t1 values (0,'aa');
+insert into t1 (a,b) select 1,'xxx' from seq_1_to_5;
+ERROR 23000: Duplicate entry 'xxx' for key 'b'
+select * from t1;
+a b
+0 aa
+1 xxx
+drop table t1;
+#
# End of 10.4 tests
#
#
diff --git a/mysql-test/main/long_unique_bugs.test b/mysql-test/main/long_unique_bugs.test
index f2160bb7f68..86003a6881c 100644
--- a/mysql-test/main/long_unique_bugs.test
+++ b/mysql-test/main/long_unique_bugs.test
@@ -1,9 +1,10 @@
--source include/have_innodb.inc
--source include/have_partition.inc
+--source include/have_sequence.inc
-#
-# MDEV-18707 Server crash in my_hash_sort_bin, ASAN heap-use-after-free in Field::is_null, server hang, corrupted double-linked list
-#
+--echo #
+--echo # MDEV-18707 Server crash in my_hash_sort_bin, ASAN heap-use-after-free in Field::is_null, server hang, corrupted double-linked list
+--echo #
create table t1 (a int, b int, c int, d int, e int);
insert into t1 () values
(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),
@@ -20,18 +21,18 @@ drop table t1;
--remove_file $datadir/test/load.data
drop table tmp;
-#
-# MDEV-18712 InnoDB indexes are inconsistent with what defined in .frm for table after rebuilding table with index on blob
-#
+--echo #
+--echo # MDEV-18712 InnoDB indexes are inconsistent with what defined in .frm for table after rebuilding table with index on blob
+--echo #
create table t1 (b blob) engine=innodb;
alter table t1 add unique (b);
alter table t1 force;
show create table t1;
drop table t1;
-#
-# MDEV-18713 Assertion `strcmp(share->unique_file_name,filename) || share->last_version' failed in test_if_reopen upon REPLACE into table with key on blob
-#
+--echo #
+--echo # MDEV-18713 Assertion `strcmp(share->unique_file_name,filename) || share->last_version' failed in test_if_reopen upon REPLACE into table with key on blob
+--echo #
create table t1 (pk int, b blob, primary key(pk), unique(b)) engine=myisam;
insert into t1 values (1,'foo');
replace into t1 (pk) values (1);
@@ -39,17 +40,17 @@ alter table t1 force;
replace into t1 (pk) values (1);
drop table t1;
-#
-# MDEV-18722 Assertion `templ->mysql_null_bit_mask' failed in row_sel_store_mysql_rec upon modifying indexed column into blob
-#
+--echo #
+--echo # MDEV-18722 Assertion `templ->mysql_null_bit_mask' failed in row_sel_store_mysql_rec upon modifying indexed column into blob
+--echo #
create table t1 (t time, unique(t)) engine=innodb;
insert into t1 values (null),(null);
alter ignore table t1 modify t text not null default '';
drop table t1;
-#
-# MDEV-18720 Assertion `inited==NONE' failed in ha_index_init upon update on versioned table with key on blob
-#
+--echo #
+--echo # MDEV-18720 Assertion `inited==NONE' failed in ha_index_init upon update on versioned table with key on blob
+--echo #
create table t1 ( pk int, f text, primary key (pk), unique(f)) with system versioning;
insert into t1 values (1,'foo');
update t1 set f = 'bar';
@@ -59,25 +60,25 @@ select * from t1;
select pk, f, row_end > DATE'2030-01-01' from t1 for system_time all;
drop table t1;
-#
-# MDEV-18747 InnoDB: Failing assertion: table->get_ref_count() == 0 upon dropping temporary table with unique blob
-#
+--echo #
+--echo # MDEV-18747 InnoDB: Failing assertion: table->get_ref_count() == 0 upon dropping temporary table with unique blob
+--echo #
create temporary table t1 (f blob, unique(f)) engine=innodb;
insert into t1 values (1);
replace into t1 values (1);
drop table t1;
-#
-# MDEV-18748 REPLACE doesn't work with unique blobs on MyISAM table
-#
+--echo #
+--echo # MDEV-18748 REPLACE doesn't work with unique blobs on MyISAM table
+--echo #
create table t (b blob, unique(b)) engine=myisam;
insert into t values ('foo');
replace into t values ('foo');
drop table t;
-#
-# MDEV-18790 Server crash in fields_in_hash_keyinfo after unsuccessful attempt to drop BLOB with long index
-#
+--echo #
+--echo # MDEV-18790 Server crash in fields_in_hash_keyinfo after unsuccessful attempt to drop BLOB with long index
+--echo #
CREATE TABLE t1 (f INT, x BLOB, UNIQUE (x));
INSERT INTO t1 VALUES (1,'foo');
--error ER_ALTER_OPERATION_NOT_SUPPORTED
@@ -85,9 +86,9 @@ ALTER TABLE t1 DROP x, ALGORITHM=INPLACE;
UPDATE t1 SET x = 'bar';
DROP TABLE t1;
-#
-# MDEV-18799 Long unique does not work after failed alter table
-#
+--echo #
+--echo # MDEV-18799 Long unique does not work after failed alter table
+--echo #
create table t1(a blob unique , b blob);
insert into t1 values(1,1),(2,1);
--error ER_DUP_ENTRY
@@ -97,28 +98,28 @@ alter table t1 add unique(b);
insert into t1 values(1,1);
DROP TABLE t1;
-#
-# MDEV-18792 ASAN unknown-crash in _mi_pack_key upon UPDATE after failed ALTER on a table with long BLOB key
-#
+--echo #
+--echo # MDEV-18792 ASAN unknown-crash in _mi_pack_key upon UPDATE after failed ALTER on a table with long BLOB key
+--echo #
CREATE TABLE t1 (a TEXT, b INT, UNIQUE(a)) ENGINE=MyISAM;
--error ER_CANT_DROP_FIELD_OR_KEY
ALTER TABLE t1 DROP x;
UPDATE t1 SET b = 0 WHERE a = 'foo';
DROP TABLE t1;
-#
-# MDEV-18793 Assertion `0' failed in row_sel_convert_mysql_key_to_innobase, ASAN unknown-crash in
-# row_mysql_store_col_in_innobase_format, warning " InnoDB: Using a partial-field key prefix in search"
-#
+--echo #
+--echo # MDEV-18793 Assertion `0' failed in row_sel_convert_mysql_key_to_innobase, ASAN unknown-crash in
+--echo # row_mysql_store_col_in_innobase_format, warning " InnoDB: Using a partial-field key prefix in search"
+--echo #
CREATE TABLE t1 (a TEXT, b INT, UNIQUE(a)) ENGINE=InnoDB;
--error ER_CANT_DROP_FIELD_OR_KEY
ALTER TABLE t1 DROP x;
UPDATE t1 SET b = 0 WHERE a = 'foo';
DROP TABLE t1;
-#
-# MDEV-18795 InnoDB: Failing assertion: field->prefix_len > 0 upon DML on table with BLOB index
-#
+--echo #
+--echo # MDEV-18795 InnoDB: Failing assertion: field->prefix_len > 0 upon DML on table with BLOB index
+--echo #
CREATE TEMPORARY TABLE t1 (f BLOB, UNIQUE(f)) ENGINE=InnoDB ROW_FORMAT=COMPACT;
--error ER_INDEX_COLUMN_TOO_LONG
ALTER TABLE t1 ADD KEY (f);
@@ -126,36 +127,36 @@ TRUNCATE TABLE t1;
SELECT * FROM t1 WHERE f LIKE 'foo';
DROP TABLE t1;
-#
-# MDEV-18798 InnoDB: No matching column for `DB_ROW_HASH_1`and server crash in
-# ha_innobase::commit_inplace_alter_table upon ALTER on table with UNIQUE key
-#
+--echo #
+--echo # MDEV-18798 InnoDB: No matching column for `DB_ROW_HASH_1`and server crash in
+--echo # ha_innobase::commit_inplace_alter_table upon ALTER on table with UNIQUE key
+--echo #
CREATE TABLE t1 (a INT, UNIQUE ind USING HASH (a)) ENGINE=InnoDB;
ALTER TABLE t1 CHANGE COLUMN IF EXISTS b a INT;
DROP TABLE t1;
-#
-# MDEV-18801 InnoDB: Failing assertion: field->col->mtype == type or ASAN heap-buffer-overflow
-# in row_sel_convert_mysql_key_to_innobase upon SELECT on table with long index
-#
+--echo #
+--echo # MDEV-18801 InnoDB: Failing assertion: field->col->mtype == type or ASAN heap-buffer-overflow
+--echo # in row_sel_convert_mysql_key_to_innobase upon SELECT on table with long index
+--echo #
CREATE TABLE t1 (f VARCHAR(4096), UNIQUE(f)) ENGINE=InnoDB;
--error ER_CANT_DROP_FIELD_OR_KEY
ALTER TABLE t1 DROP x;
SELECT * FROM t1 WHERE f LIKE 'foo';
DROP TABLE t1;
-#
-# MDEV-18800 Server crash in instant_alter_column_possible or
-# Assertion `!pk->has_virtual()' failed in instant_alter_column_possible upon adding key
-#
+--echo #
+--echo # MDEV-18800 Server crash in instant_alter_column_possible or
+--echo # Assertion `!pk->has_virtual()' failed in instant_alter_column_possible upon adding key
+--echo #
CREATE TABLE t1 (pk INT, PRIMARY KEY USING HASH (pk)) ENGINE=InnoDB;
--query_vertical show keys from t1;
ALTER TABLE t1 ADD INDEX (pk);
DROP TABLE t1;
-#
-# MDEV-18922 Alter on long unique varchar column makes result null
-#
+--echo #
+--echo # MDEV-18922 Alter on long unique varchar column makes result null
+--echo #
CREATE TABLE t1 (b int, a varchar(4000));
INSERT INTO t1 VALUES (1, 2),(2,3),(3,4);
ALTER TABLE t1 ADD UNIQUE INDEX (a);
@@ -163,10 +164,10 @@ SELECT * FROM t1;
SELECT a FROM t1;
drop table t1;
-#
-# MDEV-18809 Server crash in fields_in_hash_keyinfo or Assertion `key_info->key_part->field->flags
-# & (1<< 30)' failed in setup_keyinfo_hash
-#
+--echo #
+--echo # MDEV-18809 Server crash in fields_in_hash_keyinfo or Assertion `key_info->key_part->field->flags
+--echo # & (1<< 30)' failed in setup_keyinfo_hash
+--echo #
CREATE TABLE t1 (f VARCHAR(4096), UNIQUE(f)) ENGINE=InnoDB;
--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON
ALTER TABLE t1 DROP KEY f, ADD INDEX idx1(f), ALGORITHM=INSTANT;
@@ -181,9 +182,9 @@ insert into t1 values(1,1);
alter table t1 add column c int;
drop table t1;
-#
-# MDEV-18889 Long unique on virtual fields crashes server
-#
+--echo #
+--echo # MDEV-18889 Long unique on virtual fields crashes server
+--echo #
create table t1(a blob , b blob as (a) unique);
insert into t1 values(1, default);
--error ER_DUP_ENTRY
@@ -199,9 +200,9 @@ insert into t1(a,b) values(2,3);
insert into t1(a,b) values(3,2);
drop table t1;
-#
-# MDEV-18888 Server crashes in Item_field::register_field_in_read_map upon MODIFY COLUMN
-#
+--echo #
+--echo # MDEV-18888 Server crashes in Item_field::register_field_in_read_map upon MODIFY COLUMN
+--echo #
CREATE TABLE t1 (
a CHAR(128),
b CHAR(128) AS (a),
@@ -218,9 +219,9 @@ CREATE TABLE t1 (
) ENGINE=InnoDB;
drop table t1;
-#
-# MDEV-18967 Load data in system version with long unique does not work
-#
+--echo #
+--echo # MDEV-18967 Load data in system version with long unique does not work
+--echo #
CREATE TABLE t1 (data VARCHAR(4), unique(data) using hash) with system versioning;
INSERT INTO t1 VALUES ('A');
SELECT * INTO OUTFILE 'load.data' from t1;
@@ -231,9 +232,9 @@ DROP TABLE t1;
--let $datadir= `select @@datadir`
--remove_file $datadir/test/load.data
-#
-# MDEV-18901 Wrong results after ADD UNIQUE INDEX(blob_column)
-#
+--echo #
+--echo # MDEV-18901 Wrong results after ADD UNIQUE INDEX(blob_column)
+--echo #
CREATE TABLE t1 (data VARCHAR(7961)) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('f'), ('o'), ('o');
@@ -245,16 +246,16 @@ ALTER TABLE t1 ADD SYSTEM VERSIONING ;
SELECT * FROM t1;
REPLACE INTO t1 VALUES ('f'), ('o'), ('o');
SELECT * FROM t1;
-# This should be equivalent to the REPLACE above
+--echo # This should be equivalent to the REPLACE above
LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
--let $datadir= `select @@datadir`
--remove_file $datadir/test/load.data
-#
-# MDEV-18953 Hash index on partial char field not working
-#
+--echo #
+--echo # MDEV-18953 Hash index on partial char field not working
+--echo #
create table t1 (
c char(10) character set utf8mb4,
unique key a using hash (c(1))
@@ -267,17 +268,17 @@ insert into t1 values ('бб');
insert into t1 values ('ббб');
drop table t1;
-#
-# MDEV-18904 Assertion `m_part_spec.start_part >= m_part_spec.end_part' failed in ha_partition::index_read_idx_map
-#
+--echo #
+--echo # MDEV-18904 Assertion `m_part_spec.start_part >= m_part_spec.end_part' failed in ha_partition::index_read_idx_map
+--echo #
CREATE TABLE t1 (a INT, UNIQUE USING HASH (a)) PARTITION BY HASH (a) PARTITIONS 2;
INSERT INTO t1 VALUES (2);
REPLACE INTO t1 VALUES (2);
DROP TABLE t1;
-#
-# MDEV-18820 Assertion `lock_table_has(trx, index->table, LOCK_IX)' failed in lock_rec_insert_check_and_lock upon INSERT into table with blob key'
-#
+--echo #
+--echo # MDEV-18820 Assertion `lock_table_has(trx, index->table, LOCK_IX)' failed in lock_rec_insert_check_and_lock upon INSERT into table with blob key'
+--echo #
set innodb_lock_wait_timeout= 10;
@@ -317,15 +318,15 @@ INSERT IGNORE INTO t1 VALUES (4, 1)/*4*/;
--connection default
DROP TABLE t1, t2;
-#
-# MDEV-18791 Wrong error upon creating Aria table with long index on BLOB
-#
+--echo #
+--echo # MDEV-18791 Wrong error upon creating Aria table with long index on BLOB
+--echo #
--error ER_TOO_LONG_KEY
CREATE TABLE t1 (a TEXT, UNIQUE(a)) ENGINE=Aria;
-#
-# MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes
-#
+--echo #
+--echo # MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes
+--echo #
create table t1(a int, unique(a) using hash);
--let $count=150
--let insert_stmt= insert into t1 values(200)
@@ -340,9 +341,9 @@ while ($count)
--enable_query_log
drop table t1;
-#
-# MDEV-21804 Assertion `marked_for_read()' failed upon INSERT into table with long unique blob under binlog_row_image=NOBLOB
-#
+--echo #
+--echo # MDEV-21804 Assertion `marked_for_read()' failed upon INSERT into table with long unique blob under binlog_row_image=NOBLOB
+--echo #
--source include/have_binlog_format_row.inc
SET binlog_row_image= NOBLOB;
@@ -352,20 +353,17 @@ INSERT INTO t1 VALUES (1,'foo');
create table t2(id int primary key, a blob, b varchar(20) as (LEFT(a,2)));
INSERT INTO t2 VALUES (1, 'foo', default);
-# Cleanup
DROP TABLE t1, t2;
SET binlog_row_image= FULL;
-#
-# MDEV-22719 Long unique keys are not created when individual key_part->length < max_key_length but SUM(key_parts->length) > max_key_length
-#
+--echo #
+--echo # MDEV-22719 Long unique keys are not created when individual key_part->length < max_key_length but SUM(key_parts->length) > max_key_length
+--echo #
CREATE TABLE t1 (a int, b VARCHAR(1000), UNIQUE (a,b)) ENGINE=MyISAM;
show index from t1;
CREATE TABLE t2 (a varchar(900), b VARCHAR(900), UNIQUE (a,b)) ENGINE=MyISAM;
show index from t2;
-
-# Cleanup
DROP TABLE t1,t2;
--echo #
@@ -397,9 +395,9 @@ update t1 set v2 = 1, v3 = -128;
update t1,t2 set v1 = v2 , v5 = 0;
drop table t1, t2;
-#
-# MDEV-23264 Unique blobs allow duplicate values upon UPDATE
-#
+--echo #
+--echo # MDEV-23264 Unique blobs allow duplicate values upon UPDATE
+--echo #
CREATE TABLE t1 (f TEXT UNIQUE);
INSERT INTO t1 VALUES (NULL),(NULL);
@@ -436,6 +434,16 @@ alter table t1 reorganize partition p1 into (
drop table t1;
--echo #
+--echo # MDEV-29199 Unique hash key is ignored upon INSERT ... SELECT into non-empty MyISAM table
+--echo #
+create table t1 (a int, b text, unique(b)) engine=MyISAM;
+insert into t1 values (0,'aa');
+--error ER_DUP_ENTRY
+insert into t1 (a,b) select 1,'xxx' from seq_1_to_5;
+select * from t1;
+drop table t1;
+
+--echo #
--echo # End of 10.4 tests
--echo #
diff --git a/mysql-test/main/rowid_filter.result b/mysql-test/main/rowid_filter.result
index 209dfc44d7b..9860b2e9ad3 100644
--- a/mysql-test/main/rowid_filter.result
+++ b/mysql-test/main/rowid_filter.result
@@ -338,7 +338,7 @@ WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-01-31' AND
o_totalprice between 200000 and 230000;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 98 Using index condition
-1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 Using where
+1 SIMPLE orders eq_ref|filter PRIMARY,i_o_totalprice PRIMARY|i_o_totalprice 4|9 dbt3_s001.lineitem.l_orderkey 1 (5%) Using where; Using rowid filter
set statement optimizer_switch='rowid_filter=on' for EXPLAIN FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-01-31' AND
@@ -371,6 +371,14 @@ EXPLAIN
"key_length": "4",
"used_key_parts": ["o_orderkey"],
"ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_o_totalprice",
+ "used_key_parts": ["o_totalprice"]
+ },
+ "rows": 69,
+ "selectivity_pct": 4.6
+ },
"rows": 1,
"filtered": 4.599999905,
"attached_condition": "orders.o_totalprice between 200000 and 230000"
@@ -383,7 +391,7 @@ WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-01-31' AND
o_totalprice between 200000 and 230000;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 98 98.00 100.00 100.00 Using index condition
-1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 1.00 4.60 11.22 Using where
+1 SIMPLE orders eq_ref|filter PRIMARY,i_o_totalprice PRIMARY|i_o_totalprice 4|9 dbt3_s001.lineitem.l_orderkey 1 (5%) 0.11 (10%) 4.60 100.00 Using where; Using rowid filter
set statement optimizer_switch='rowid_filter=on' for ANALYZE FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-01-31' AND
@@ -423,13 +431,26 @@ ANALYZE
"key_length": "4",
"used_key_parts": ["o_orderkey"],
"ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_o_totalprice",
+ "used_key_parts": ["o_totalprice"]
+ },
+ "rows": 69,
+ "selectivity_pct": 4.6,
+ "r_rows": 71,
+ "r_lookups": 96,
+ "r_selectivity_pct": 10.41666667,
+ "r_buffer_size": "REPLACED",
+ "r_filling_time_ms": "REPLACED"
+ },
"r_loops": 98,
"rows": 1,
- "r_rows": 1,
+ "r_rows": 0.112244898,
"r_table_time_ms": "REPLACED",
"r_other_time_ms": "REPLACED",
"filtered": 4.599999905,
- "r_filtered": 11.2244898,
+ "r_filtered": 100,
"attached_condition": "orders.o_totalprice between 200000 and 230000"
}
}
@@ -575,7 +596,7 @@ l_quantity > 45 AND
o_totalprice between 180000 and 230000;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE lineitem range|filter PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity i_l_shipdate|i_l_quantity 4|9 NULL 509 (12%) Using index condition; Using where; Using rowid filter
-1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 Using where
+1 SIMPLE orders eq_ref|filter PRIMARY,i_o_totalprice PRIMARY|i_o_totalprice 4|9 dbt3_s001.lineitem.l_orderkey 1 (9%) Using where; Using rowid filter
set statement optimizer_switch='rowid_filter=on' for EXPLAIN FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, l_quantity, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
@@ -619,6 +640,14 @@ EXPLAIN
"key_length": "4",
"used_key_parts": ["o_orderkey"],
"ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_o_totalprice",
+ "used_key_parts": ["o_totalprice"]
+ },
+ "rows": 139,
+ "selectivity_pct": 9.266666667
+ },
"rows": 1,
"filtered": 9.266666412,
"attached_condition": "orders.o_totalprice between 180000 and 230000"
@@ -632,7 +661,7 @@ l_quantity > 45 AND
o_totalprice between 180000 and 230000;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE lineitem range|filter PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity i_l_shipdate|i_l_quantity 4|9 NULL 509 (12%) 60.00 (11%) 11.69 100.00 Using index condition; Using where; Using rowid filter
-1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 1.00 9.27 26.67 Using where
+1 SIMPLE orders eq_ref|filter PRIMARY,i_o_totalprice PRIMARY|i_o_totalprice 4|9 dbt3_s001.lineitem.l_orderkey 1 (9%) 0.27 (25%) 9.27 100.00 Using where; Using rowid filter
set statement optimizer_switch='rowid_filter=on' for ANALYZE FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, l_quantity, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
@@ -688,13 +717,26 @@ ANALYZE
"key_length": "4",
"used_key_parts": ["o_orderkey"],
"ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_o_totalprice",
+ "used_key_parts": ["o_totalprice"]
+ },
+ "rows": 139,
+ "selectivity_pct": 9.266666667,
+ "r_rows": 144,
+ "r_lookups": 59,
+ "r_selectivity_pct": 25.42372881,
+ "r_buffer_size": "REPLACED",
+ "r_filling_time_ms": "REPLACED"
+ },
"r_loops": 60,
"rows": 1,
- "r_rows": 1,
+ "r_rows": 0.266666667,
"r_table_time_ms": "REPLACED",
"r_other_time_ms": "REPLACED",
"filtered": 9.266666412,
- "r_filtered": 26.66666667,
+ "r_filtered": 100,
"attached_condition": "orders.o_totalprice between 180000 and 230000"
}
}
diff --git a/mysql-test/main/select.result b/mysql-test/main/select.result
index 726e20889fe..35520d1bd23 100644
--- a/mysql-test/main/select.result
+++ b/mysql-test/main/select.result
@@ -3616,7 +3616,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 4 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND
@@ -3624,7 +3624,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee') ;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 4 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3632,7 +3632,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 2 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3640,7 +3640,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 2 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
DROP TABLE t1,t2,t3;
CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int);
CREATE TABLE t2 ( f11 int PRIMARY KEY );
@@ -5639,4 +5639,60 @@ EXECUTE stmt;
COUNT(DISTINCT a)
3
DROP TABLE t1;
+#
+# MDEV-29294: Assertion `functype() == ((Item_cond *) new_item)->functype()'
+# failed in Item_cond::remove_eq_conds on SELECT
+#
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+# Test for nested OR conditions:
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+a
+1
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using temporary
+3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1249 Select 2 was reduced during optimization
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 1 and (1 or <expr_cache><`test`.`t1`.`a`>((/* select#3 */ select 3 from DUAL where `test`.`t1`.`a` = `test`.`t1`.`a`)) = 3)
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v1;
+a
+1
+# Test for nested AND conditions:
+SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+a
+1
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v2 AS SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v2;
+a
+1
+DROP TABLE t1;
+DROP VIEW v1, v2;
End of 10.0 tests
diff --git a/mysql-test/main/select.test b/mysql-test/main/select.test
index d17d8d626b8..f7b379861e8 100644
--- a/mysql-test/main/select.test
+++ b/mysql-test/main/select.test
@@ -4742,4 +4742,44 @@ EXECUTE stmt;
--enable_warnings
DROP TABLE t1;
+--echo #
+--echo # MDEV-29294: Assertion `functype() == ((Item_cond *) new_item)->functype()'
+--echo # failed in Item_cond::remove_eq_conds on SELECT
+--echo #
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+
+--echo # Test for nested OR conditions:
+SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+EXECUTE stmt;
+
+CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v1;
+
+--echo # Test for nested AND conditions:
+SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+EXECUTE stmt;
+
+CREATE VIEW v2 AS SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v2;
+
+DROP TABLE t1;
+DROP VIEW v1, v2;
+
--echo End of 10.0 tests
diff --git a/mysql-test/main/select_jcl6.result b/mysql-test/main/select_jcl6.result
index 9a610852c80..790e94488cf 100644
--- a/mysql-test/main/select_jcl6.result
+++ b/mysql-test/main/select_jcl6.result
@@ -3627,7 +3627,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 4 Using index condition; Using where; Rowid-ordered scan
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND
@@ -3635,7 +3635,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee') ;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 4 Using index condition; Using where; Rowid-ordered scan
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3643,7 +3643,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 2 Using index condition; Using where; Rowid-ordered scan
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3651,7 +3651,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 2 Using index condition; Using where; Rowid-ordered scan
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
DROP TABLE t1,t2,t3;
CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int);
CREATE TABLE t2 ( f11 int PRIMARY KEY );
@@ -5650,6 +5650,62 @@ EXECUTE stmt;
COUNT(DISTINCT a)
3
DROP TABLE t1;
+#
+# MDEV-29294: Assertion `functype() == ((Item_cond *) new_item)->functype()'
+# failed in Item_cond::remove_eq_conds on SELECT
+#
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+# Test for nested OR conditions:
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+a
+1
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using temporary
+3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1249 Select 2 was reduced during optimization
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 1 and (1 or <expr_cache><`test`.`t1`.`a`>((/* select#3 */ select 3 from DUAL where `test`.`t1`.`a` = `test`.`t1`.`a`)) = 3)
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v1;
+a
+1
+# Test for nested AND conditions:
+SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+a
+1
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v2 AS SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v2;
+a
+1
+DROP TABLE t1;
+DROP VIEW v1, v2;
End of 10.0 tests
set join_cache_level=default;
set @@optimizer_switch=@save_optimizer_switch_jcl6;
diff --git a/mysql-test/main/select_pkeycache.result b/mysql-test/main/select_pkeycache.result
index 726e20889fe..35520d1bd23 100644
--- a/mysql-test/main/select_pkeycache.result
+++ b/mysql-test/main/select_pkeycache.result
@@ -3616,7 +3616,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 4 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND
@@ -3624,7 +3624,7 @@ t3.a=t2.a AND t3.c IN ('bb','ee') ;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 4 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3632,7 +3632,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si si 5 NULL 2 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT t3.a FROM t1,t2,t3
WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND
@@ -3640,7 +3640,7 @@ t3.c IN ('bb','ee');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 range si,ai si 5 NULL 2 Using index condition; Using where
-1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where
+1 SIMPLE t3 eq_ref|filter PRIMARY,ci PRIMARY|ci 4|5 test.t2.a 1 (30%) Using where; Using rowid filter
DROP TABLE t1,t2,t3;
CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int);
CREATE TABLE t2 ( f11 int PRIMARY KEY );
@@ -5639,4 +5639,60 @@ EXECUTE stmt;
COUNT(DISTINCT a)
3
DROP TABLE t1;
+#
+# MDEV-29294: Assertion `functype() == ((Item_cond *) new_item)->functype()'
+# failed in Item_cond::remove_eq_conds on SELECT
+#
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+# Test for nested OR conditions:
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+a
+1
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using temporary
+3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #1
+Note 1249 Select 2 was reduced during optimization
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 1 and (1 or <expr_cache><`test`.`t1`.`a`>((/* select#3 */ select 3 from DUAL where `test`.`t1`.`a` = `test`.`t1`.`a`)) = 3)
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 AND
+ (3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = 1 AND
+(3 = 0 OR (SELECT a = 1 OR (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v1;
+a
+1
+# Test for nested AND conditions:
+SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+a
+1
+PREPARE stmt FROM 'SELECT * FROM t1 WHERE a = 1 OR
+ (3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3))';
+EXECUTE stmt;
+a
+1
+EXECUTE stmt;
+a
+1
+CREATE VIEW v2 AS SELECT * FROM t1 WHERE a = 1 OR
+(3 = 3 AND (SELECT a = 1 AND (SELECT 3 WHERE a = a) = 3));
+SELECT * FROM v2;
+a
+1
+DROP TABLE t1;
+DROP VIEW v1, v2;
End of 10.0 tests
diff --git a/mysql-test/main/subselect2.result b/mysql-test/main/subselect2.result
index 7dc3c2f463c..e6363610301 100644
--- a/mysql-test/main/subselect2.result
+++ b/mysql-test/main/subselect2.result
@@ -132,7 +132,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
-1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
+1 PRIMARY t3 ref|filter PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX FFOLDERID_IDX|CMFLDRPARNT_IDX 34|35 test.t3.PARENTID 1 (29%) Using where; Using rowid filter
drop table t1, t2, t3, t4;
CREATE TABLE t1 (a int(10) , PRIMARY KEY (a)) Engine=InnoDB;
INSERT INTO t1 VALUES (1),(2);
diff --git a/mysql-test/main/type_timestamp.result b/mysql-test/main/type_timestamp.result
index 6ca529cf0c4..f23c66e10a5 100644
--- a/mysql-test/main/type_timestamp.result
+++ b/mysql-test/main/type_timestamp.result
@@ -1320,6 +1320,28 @@ CASE WHEN a THEN DEFAULT(a) END
DROP TABLE t1;
SET timestamp=DEFAULT;
#
+# MDEV-27653 long uniques don't work with unicode collations
+#
+CREATE TABLE t1 (a timestamp, UNIQUE KEY(a) USING HASH);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES ('2001-01-01 10:20:30');
+SET time_zone='+01:00';
+INSERT INTO t1 SELECT MAX(a) FROM t1;
+ERROR 23000: Duplicate entry '2001-01-01 11:20:30' for key 'a'
+SELECT * FROM t1;
+a
+2001-01-01 11:20:30
+DROP TABLE t1;
+CREATE TABLE t1 (a timestamp, UNIQUE KEY(a) USING HASH);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES ('2001-01-01 10:20:30');
+SET time_zone='+01:00';
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+test.t1 check status OK
+DROP TABLE t1;
+SET time_zone=DEFAULT;
+#
# End of 10.4 tests
#
#
diff --git a/mysql-test/main/type_timestamp.test b/mysql-test/main/type_timestamp.test
index 661ec56a04f..a0f1f2c3d78 100644
--- a/mysql-test/main/type_timestamp.test
+++ b/mysql-test/main/type_timestamp.test
@@ -878,6 +878,27 @@ DROP TABLE t1;
SET timestamp=DEFAULT;
--echo #
+--echo # MDEV-27653 long uniques don't work with unicode collations
+--echo #
+
+CREATE TABLE t1 (a timestamp, UNIQUE KEY(a) USING HASH);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES ('2001-01-01 10:20:30');
+SET time_zone='+01:00';
+--error ER_DUP_ENTRY
+INSERT INTO t1 SELECT MAX(a) FROM t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a timestamp, UNIQUE KEY(a) USING HASH);
+SET time_zone='+00:00';
+INSERT INTO t1 VALUES ('2001-01-01 10:20:30');
+SET time_zone='+01:00';
+CHECK TABLE t1;
+DROP TABLE t1;
+SET time_zone=DEFAULT;
+
+--echo #
--echo # End of 10.4 tests
--echo #
diff --git a/mysql-test/main/win.result b/mysql-test/main/win.result
index 9a87c507c64..96b13010516 100644
--- a/mysql-test/main/win.result
+++ b/mysql-test/main/win.result
@@ -4273,11 +4273,13 @@ GROUP BY
LEFT((SYSDATE()), 'foo')
WITH ROLLUP;
SUM(b) OVER (PARTITION BY a) ROW_NUMBER() OVER (PARTITION BY b)
-NULL 1
-NULL 1
+0 1
+0 2
Warnings:
Warning 1292 Truncated incorrect INTEGER value: 'foo'
Warning 1292 Truncated incorrect INTEGER value: 'foo'
+Warning 1292 Truncated incorrect DOUBLE value: 'bar'
+Warning 1292 Truncated incorrect DOUBLE value: 'bar'
drop table t1;
#
#
@@ -4336,6 +4338,46 @@ pk a bit_or
DROP TABLE t2;
DROP TABLE t1;
#
+# MDEV-15178: Filesort::make_sortorder: Assertion `pos->field != __null |
+#
+CREATE TABLE t1 (i1 int, a int);
+INSERT INTO t1 VALUES (1, 1), (2, 2),(3, 3);
+CREATE TABLE t2 (i2 int);
+INSERT INTO t2 VALUES (1),(2),(5),(1),(7),(4),(3);
+SELECT
+a,
+RANK() OVER (ORDER BY SUM(DISTINCT i1))
+FROM
+t1, t2 WHERE t2.i2 = t1.i1
+GROUP BY
+a;
+a RANK() OVER (ORDER BY SUM(DISTINCT i1))
+1 1
+2 2
+3 3
+DROP TABLE t1, t2;
+#
+# MDEV-17014: Crash server using ROW_NUMBER() OVER (PARTITION ..)
+#
+CREATE TABLE t1 (UID BIGINT);
+CREATE TABLE t2 (UID BIGINT);
+CREATE TABLE t3 (UID BIGINT);
+insert into t1 VALUES (1),(2);
+insert into t2 VALUES (1),(2);
+insert into t3 VALUES (1),(2);
+SELECT
+ROW_NUMBER() OVER (PARTITION BY GROUP_CONCAT(TT1.UID))
+FROM t1 TT1,
+t2 TT2,
+t3 TT3
+WHERE TT3.UID = TT1.UID AND TT2.UID = TT3.UID
+GROUP BY TT1.UID
+;
+ROW_NUMBER() OVER (PARTITION BY GROUP_CONCAT(TT1.UID))
+1
+1
+DROP TABLE t1, t2, t3;
+#
# End of 10.3 tests
#
#
diff --git a/mysql-test/main/win.test b/mysql-test/main/win.test
index 5a216123369..472f3529e98 100644
--- a/mysql-test/main/win.test
+++ b/mysql-test/main/win.test
@@ -2816,6 +2816,46 @@ DROP TABLE t2;
DROP TABLE t1;
+--echo #
+--echo # MDEV-15178: Filesort::make_sortorder: Assertion `pos->field != __null |
+--echo #
+
+CREATE TABLE t1 (i1 int, a int);
+INSERT INTO t1 VALUES (1, 1), (2, 2),(3, 3);
+
+CREATE TABLE t2 (i2 int);
+INSERT INTO t2 VALUES (1),(2),(5),(1),(7),(4),(3);
+
+SELECT
+ a,
+ RANK() OVER (ORDER BY SUM(DISTINCT i1))
+FROM
+ t1, t2 WHERE t2.i2 = t1.i1
+GROUP BY
+ a;
+
+DROP TABLE t1, t2;
+
+--echo #
+--echo # MDEV-17014: Crash server using ROW_NUMBER() OVER (PARTITION ..)
+--echo #
+CREATE TABLE t1 (UID BIGINT);
+CREATE TABLE t2 (UID BIGINT);
+CREATE TABLE t3 (UID BIGINT);
+
+insert into t1 VALUES (1),(2);
+insert into t2 VALUES (1),(2);
+insert into t3 VALUES (1),(2);
+SELECT
+ROW_NUMBER() OVER (PARTITION BY GROUP_CONCAT(TT1.UID))
+FROM t1 TT1,
+ t2 TT2,
+ t3 TT3
+WHERE TT3.UID = TT1.UID AND TT2.UID = TT3.UID
+GROUP BY TT1.UID
+;
+
+DROP TABLE t1, t2, t3;
--echo #
--echo # End of 10.3 tests
diff --git a/mysql-test/main/win_orderby.result b/mysql-test/main/win_orderby.result
index bf4a40a4db3..1a9860c1c76 100644
--- a/mysql-test/main/win_orderby.result
+++ b/mysql-test/main/win_orderby.result
@@ -24,3 +24,66 @@ pk count(a) over (order by pk rows between 2 preceding and 2 following)
28 5
27 5
drop table t0,t1;
+#
+# MDEV-30052: Crash with a query containing nested WINDOW clauses
+#
+CREATE TABLE t1 (c INT);
+insert into t1 values (1),(2);
+UPDATE t1 SET c=1
+WHERE c=2
+ORDER BY
+(1 IN ((
+SELECT *
+FROM (SELECT * FROM t1) AS v1
+GROUP BY c
+WINDOW v2 AS (ORDER BY
+(SELECT *
+FROM t1
+GROUP BY c
+WINDOW v3 AS (PARTITION BY c)
+)
+)
+))
+);
+drop table t1;
+#
+# MDEV-29359: Server crashed with heap-use-after-free in
+# Field::is_null(long long) const (Just testcase)
+#
+CREATE TABLE t1 (id int);
+INSERT INTO t1 VALUES (-1),(0),(84);
+SELECT
+id IN (SELECT id
+FROM t1
+WINDOW w AS (ORDER BY (SELECT 1
+FROM t1
+WHERE
+EXISTS ( SELECT id
+FROM t1
+GROUP BY id
+WINDOW w2 AS (ORDER BY id)
+)
+)
+)
+)
+FROM t1;
+id IN (SELECT id
+FROM t1
+WINDOW w AS (ORDER BY (SELECT 1
+FROM t1
+WHERE
+EXISTS ( SELECT id
+FROM t1
+GROUP BY id
+WINDOW w2 AS (ORDER BY id)
+)
+)
+)
+)
+1
+1
+1
+DROP TABLE t1;
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/main/win_orderby.test b/mysql-test/main/win_orderby.test
index 7f02a582ea0..65421fc095c 100644
--- a/mysql-test/main/win_orderby.test
+++ b/mysql-test/main/win_orderby.test
@@ -33,3 +33,58 @@ limit 4;
--disable_view_protocol
drop table t0,t1;
+
+
+--echo #
+--echo # MDEV-30052: Crash with a query containing nested WINDOW clauses
+--echo #
+
+CREATE TABLE t1 (c INT);
+insert into t1 values (1),(2);
+UPDATE t1 SET c=1
+WHERE c=2
+ORDER BY
+ (1 IN ((
+ SELECT *
+ FROM (SELECT * FROM t1) AS v1
+ GROUP BY c
+ WINDOW v2 AS (ORDER BY
+ (SELECT *
+ FROM t1
+ GROUP BY c
+ WINDOW v3 AS (PARTITION BY c)
+ )
+ )
+ ))
+ );
+drop table t1;
+
+--echo #
+--echo # MDEV-29359: Server crashed with heap-use-after-free in
+--echo # Field::is_null(long long) const (Just testcase)
+--echo #
+
+CREATE TABLE t1 (id int);
+INSERT INTO t1 VALUES (-1),(0),(84);
+
+SELECT
+ id IN (SELECT id
+ FROM t1
+ WINDOW w AS (ORDER BY (SELECT 1
+ FROM t1
+ WHERE
+ EXISTS ( SELECT id
+ FROM t1
+ GROUP BY id
+ WINDOW w2 AS (ORDER BY id)
+ )
+ )
+ )
+ )
+FROM t1;
+
+DROP TABLE t1;
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD
new file mode 100644
index 00000000000..5f1ad998c00
--- /dev/null
+++ b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYD
Binary files differ
diff --git a/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI
new file mode 100644
index 00000000000..7a748abbae5
--- /dev/null
+++ b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.MYI
Binary files differ
diff --git a/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm
new file mode 100644
index 00000000000..5a4cec72324
--- /dev/null
+++ b/mysql-test/std_data/mysql_upgrade/mdev27653_100422_myisam_text.frm
Binary files differ
diff --git a/mysql-test/suite/binlog/r/binlog_recovery_after_checksum_change.result b/mysql-test/suite/binlog/r/binlog_recovery_after_checksum_change.result
new file mode 100644
index 00000000000..85b0b84c684
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_recovery_after_checksum_change.result
@@ -0,0 +1,8 @@
+connection default;
+set @@global.binlog_checksum=none;
+set @@session.debug_dbug='d,crash_before_write_second_checkpoint_event';
+set @@global.binlog_checksum=crc32;
+ERROR HY000: Lost connection to MySQL server during query
+connection default;
+NOT FOUND /Replication event checksum verification failed/ in mysqld.1.err
+End of the tests
diff --git a/mysql-test/suite/binlog/t/innodb_rc_insert_before_delete.test b/mysql-test/suite/binlog/t/innodb_rc_insert_before_delete.test
new file mode 100644
index 00000000000..891a7b8ba2f
--- /dev/null
+++ b/mysql-test/suite/binlog/t/innodb_rc_insert_before_delete.test
@@ -0,0 +1,89 @@
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/have_binlog_format_mixed.inc
+--source include/count_sessions.inc
+
+# MDEV-30010 merely adds is a Read-Committed version MDEV-30225 test
+# solely to prove the RC isolation yields ROW binlog format as it is
+# supposed to:
+# https://mariadb.com/kb/en/unsafe-statements-for-statement-based-replication/#isolation-levels.
+# The original MDEV-30225 test is adapted to the RC to create
+# a similar safisticated scenario which does not lead to any deadlock though.
+
+--connect (pause_purge,localhost,root)
+START TRANSACTION WITH CONSISTENT SNAPSHOT;
+
+--connection default
+CREATE TABLE t (pk int PRIMARY KEY, sk INT UNIQUE) ENGINE=InnoDB;
+INSERT INTO t VALUES (10, 100);
+
+--connect (con1,localhost,root)
+BEGIN; # trx 0
+SELECT * FROM t WHERE sk = 100 FOR UPDATE;
+
+--connect (con2,localhost,root)
+SET DEBUG_SYNC="lock_wait_suspend_thread_enter SIGNAL insert_wait_started";
+# trx 1 is locked on try to read the record in secondary index during duplicates
+# check. It's the first in waiting queue, that's why it will be woken up firstly
+# when trx 0 commits.
+--send INSERT INTO t VALUES (5, 100) # trx 1
+
+--connect (con3,localhost,root)
+SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
+SET DEBUG_SYNC="now WAIT_FOR insert_wait_started";
+SET DEBUG_SYNC="lock_wait_suspend_thread_enter SIGNAL delete_started_waiting";
+# trx 2 can delete (5, 100) on master, but not on slave, as on slave trx 1
+# can insert (5, 100) after trx 2 positioned it's cursor. Trx 2 lock is placed
+# in waiting queue after trx 1 lock, but its persistent cursor position was
+# stored on (100, 10) record in secondary index before suspending. After trx 1
+# is committed, trx 2 will restore persistent cursor position on (100, 10). As
+# (100, 5) secondary index record was inserted before (100, 10) in logical
+# order, and (100, 10) record is delete-marked, trx 2 just continues scanning.
+#
+# Note. There can be several records with the same key in unique secondary
+# index, but only one of them must be non-delete-marked. That's why when we do
+# point query, cursor position is set in the first record in logical order, and
+# then records are iterated until either non-delete-marked record is found or
+# all records with the same unique fields are iterated.
+
+# to prepare showing interesting binlog events
+--let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+BEGIN;
+--send UPDATE t SET sk = 200 WHERE sk = 100; # trx 2
+
+--connection con1
+SET DEBUG_SYNC="now WAIT_FOR delete_started_waiting";
+DELETE FROM t WHERE sk=100; # trx 0
+COMMIT;
+--disconnect con1
+
+--connection con2
+--reap
+--disconnect con2
+
+--connection con3
+--error 0
+--reap
+if (`SELECT ROW_COUNT() > 0`)
+{
+ --echo unexpected effective UPDATE
+ --die
+}
+--echo must be logged in ROW format as the only event of trx 2 (con3)
+INSERT INTO t VALUES (11, 101);
+COMMIT;
+--source include/show_binlog_events.inc
+--disconnect con3
+
+--connection default
+# If the bug is not fixed, we will see the row inserted by trx 1 here. This can
+# cause duplicate key error on slave, when some other trx tries in insert row
+# with the same secondary key, as was inserted by trx 1, and not deleted by trx
+# 2.
+SELECT * FROM t;
+
+--disconnect pause_purge
+SET DEBUG_SYNC="RESET";
+DROP TABLE t;
+--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/suite/encryption/r/tempfiles_encrypted.result b/mysql-test/suite/encryption/r/tempfiles_encrypted.result
index 60df9ca5260..5b9dab62c01 100644
--- a/mysql-test/suite/encryption/r/tempfiles_encrypted.result
+++ b/mysql-test/suite/encryption/r/tempfiles_encrypted.result
@@ -4279,11 +4279,13 @@ GROUP BY
LEFT((SYSDATE()), 'foo')
WITH ROLLUP;
SUM(b) OVER (PARTITION BY a) ROW_NUMBER() OVER (PARTITION BY b)
-NULL 1
-NULL 1
+0 1
+0 2
Warnings:
Warning 1292 Truncated incorrect INTEGER value: 'foo'
Warning 1292 Truncated incorrect INTEGER value: 'foo'
+Warning 1292 Truncated incorrect DOUBLE value: 'bar'
+Warning 1292 Truncated incorrect DOUBLE value: 'bar'
drop table t1;
#
#
@@ -4342,6 +4344,46 @@ pk a bit_or
DROP TABLE t2;
DROP TABLE t1;
#
+# MDEV-15178: Filesort::make_sortorder: Assertion `pos->field != __null |
+#
+CREATE TABLE t1 (i1 int, a int);
+INSERT INTO t1 VALUES (1, 1), (2, 2),(3, 3);
+CREATE TABLE t2 (i2 int);
+INSERT INTO t2 VALUES (1),(2),(5),(1),(7),(4),(3);
+SELECT
+a,
+RANK() OVER (ORDER BY SUM(DISTINCT i1))
+FROM
+t1, t2 WHERE t2.i2 = t1.i1
+GROUP BY
+a;
+a RANK() OVER (ORDER BY SUM(DISTINCT i1))
+1 1
+2 2
+3 3
+DROP TABLE t1, t2;
+#
+# MDEV-17014: Crash server using ROW_NUMBER() OVER (PARTITION ..)
+#
+CREATE TABLE t1 (UID BIGINT);
+CREATE TABLE t2 (UID BIGINT);
+CREATE TABLE t3 (UID BIGINT);
+insert into t1 VALUES (1),(2);
+insert into t2 VALUES (1),(2);
+insert into t3 VALUES (1),(2);
+SELECT
+ROW_NUMBER() OVER (PARTITION BY GROUP_CONCAT(TT1.UID))
+FROM t1 TT1,
+t2 TT2,
+t3 TT3
+WHERE TT3.UID = TT1.UID AND TT2.UID = TT3.UID
+GROUP BY TT1.UID
+;
+ROW_NUMBER() OVER (PARTITION BY GROUP_CONCAT(TT1.UID))
+1
+1
+DROP TABLE t1, t2, t3;
+#
# End of 10.3 tests
#
#
diff --git a/mysql-test/suite/galera/t/galera_gcache_recover_manytrx.test b/mysql-test/suite/galera/t/galera_gcache_recover_manytrx.test
index 8f0f0ed65ea..214d5166781 100644
--- a/mysql-test/suite/galera/t/galera_gcache_recover_manytrx.test
+++ b/mysql-test/suite/galera/t/galera_gcache_recover_manytrx.test
@@ -205,8 +205,3 @@ CALL mtr.add_suppression("conflict state 7 after post commit");
# Warning happens when the cluster is started for the first time
CALL mtr.add_suppression("Skipped GCache ring buffer recovery");
-
---connection node_2
-call mtr.add_suppression("Error in Log_event::read_log_event():.*");
-CALL mtr.add_suppression("Skipped GCache ring buffer recovery");
-
diff --git a/mysql-test/suite/mariabackup/incremental_drop_db.result b/mysql-test/suite/mariabackup/incremental_drop_db.result
new file mode 100644
index 00000000000..3a6c89f8de0
--- /dev/null
+++ b/mysql-test/suite/mariabackup/incremental_drop_db.result
@@ -0,0 +1,30 @@
+call mtr.add_suppression("InnoDB: New log files created");
+#
+# Start of 10.3 tests
+#
+#
+# MDEV-23335 MariaBackup Incremental Does Not Reflect Dropped/Created Databases
+#
+CREATE DATABASE db1;
+CREATE DATABASE db2;
+CREATE TABLE db1.t1 (a INT) ENGINE=MyISAM;
+CREATE TABLE db1.t2 (a INT) ENGINE=InnoDB;
+# Create base backup
+DROP DATABASE db1;
+# Create incremental backup
+# Remove incremental_dir/db2/db.opt file to make incremental_dir/db2/ empty
+# Prepare base backup, apply incremental one
+# shutdown server
+# remove datadir
+# xtrabackup move back
+# restart
+# Expect no 'db1' in the output, because it was really dropped.
+# Expect 'db2' in the ouput, because it was not dropped!
+# (its incremental directory was emptied only)
+SHOW DATABASES LIKE 'db%';
+Database (db%)
+db2
+DROP DATABASE db2;
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/suite/mariabackup/incremental_drop_db.test b/mysql-test/suite/mariabackup/incremental_drop_db.test
new file mode 100644
index 00000000000..de270435e9d
--- /dev/null
+++ b/mysql-test/suite/mariabackup/incremental_drop_db.test
@@ -0,0 +1,68 @@
+--source include/have_innodb.inc
+call mtr.add_suppression("InnoDB: New log files created");
+
+--echo #
+--echo # Start of 10.3 tests
+--echo #
+
+--echo #
+--echo # MDEV-23335 MariaBackup Incremental Does Not Reflect Dropped/Created Databases
+--echo #
+
+--let $datadir=`SELECT @@datadir`
+--let $basedir=$MYSQLTEST_VARDIR/tmp/backup
+--let $incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1
+
+# Create two databases:
+# - db1 is dropped normally below
+# - db2 is used to cover a corner case: its db.opt file is removed
+
+# Incremental backup contains:
+# - no directory for db1
+# - an empty directory for db2 (after we remove db2/db.opt)
+
+
+CREATE DATABASE db1;
+CREATE DATABASE db2;
+
+# Add some tables to db1
+CREATE TABLE db1.t1 (a INT) ENGINE=MyISAM;
+CREATE TABLE db1.t2 (a INT) ENGINE=InnoDB;
+
+--echo # Create base backup
+--disable_result_log
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir
+--enable_result_log
+
+DROP DATABASE db1;
+
+--echo # Create incremental backup
+--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir
+
+--echo # Remove incremental_dir/db2/db.opt file to make incremental_dir/db2/ empty
+--remove_file $incremental_dir/db2/db.opt
+
+
+--echo # Prepare base backup, apply incremental one
+--disable_result_log
+--exec $XTRABACKUP --prepare --target-dir=$basedir
+--exec $XTRABACKUP --prepare --target-dir=$basedir --incremental-dir=$incremental_dir
+--enable_result_log
+
+--let $targetdir=$basedir
+--source include/restart_and_restore.inc
+--enable_result_log
+
+--echo # Expect no 'db1' in the output, because it was really dropped.
+--echo # Expect 'db2' in the ouput, because it was not dropped!
+--echo # (its incremental directory was emptied only)
+
+SHOW DATABASES LIKE 'db%';
+DROP DATABASE db2;
+
+--rmdir $basedir
+--rmdir $incremental_dir
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result
index 9768040aa94..fc9d98f5d0a 100644
--- a/mysql-test/suite/plugins/r/server_audit.result
+++ b/mysql-test/suite/plugins/r/server_audit.result
@@ -521,3 +521,10 @@ TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'show status like \'server_audit_
TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'show variables like \'server_audit%\'',0
TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,plugin,
TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'uninstall plugin server_audit',0
+#
+# MDEV-27631 Assertion `global_status_var.global_memory_used == 0' failed in mysqld_exit
+#
+install plugin server_audit soname 'server_audit';
+uninstall plugin server_audit;
+Warnings:
+Warning 1620 Plugin is busy and will be uninstalled on shutdown
diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test
index d8f8b9d0165..5048ccc2446 100644
--- a/mysql-test/suite/plugins/t/server_audit.test
+++ b/mysql-test/suite/plugins/t/server_audit.test
@@ -235,3 +235,8 @@ uninstall plugin server_audit;
cat_file $MYSQLD_DATADIR/server_audit.log;
remove_file $MYSQLD_DATADIR/server_audit.log;
+--echo #
+--echo # MDEV-27631 Assertion `global_status_var.global_memory_used == 0' failed in mysqld_exit
+--echo #
+install plugin server_audit soname 'server_audit';
+uninstall plugin server_audit;
diff --git a/mysql-test/suite/rpl/include/create_or_drop_sync_func.inc b/mysql-test/suite/rpl/include/create_or_drop_sync_func.inc
new file mode 100644
index 00000000000..74cde5de1db
--- /dev/null
+++ b/mysql-test/suite/rpl/include/create_or_drop_sync_func.inc
@@ -0,0 +1,75 @@
+# Creates or drops a stored function as a part of debug-sync based
+# synchronization mechanism between replication servers.
+#
+# Parameters:
+# $create_or_drop= [create]
+# $server_master = [master]
+# $server_slave = [slave]
+if (!$create_or_drop)
+{
+ --let $create_or_drop=create
+}
+
+if (`select strcmp('$create_or_drop', 'create') = 0`)
+{
+ if (!$server_master)
+ {
+ --let $server_master=master
+ }
+ if (!$server_slave)
+ {
+ --let $server_slave=slave
+ }
+
+ --connection $server_master
+ # Use a stored function to inject a debug_sync into the appropriate THD.
+ # The function does nothing on the master, and on the slave it injects the
+ # desired debug_sync action(s).
+ SET sql_log_bin=0;
+ --delimiter ||
+ CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
+ RETURNS INT DETERMINISTIC
+ BEGIN
+ RETURN x;
+ END
+ ||
+ --delimiter ;
+ SET sql_log_bin=1;
+
+ --connection $server_slave
+
+ SET sql_log_bin=0;
+ --delimiter ||
+ CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
+ RETURNS INT DETERMINISTIC
+ BEGIN
+ IF d1 != '' THEN
+ SET debug_sync = d1;
+ END IF;
+ IF d2 != '' THEN
+ SET debug_sync = d2;
+ END IF;
+ RETURN x;
+ END
+ ||
+ --delimiter ;
+SET sql_log_bin=1;
+}
+
+if (`select strcmp('$create_or_drop', 'drop') = 0`)
+{
+ if (!$server_slave)
+ {
+ --let $server_slave=slave=
+ }
+ if (!$server_master)
+ {
+ --let $server_master=master
+ }
+ --connection $server_slave
+ SET DEBUG_SYNC='RESET';
+
+ --connection $server_master
+ SET DEBUG_SYNC='RESET';
+ DROP FUNCTION foo;
+}
diff --git a/mysql-test/suite/rpl/r/rpl_delayed_parallel_slave_sbm.result b/mysql-test/suite/rpl/r/rpl_delayed_parallel_slave_sbm.result
new file mode 100644
index 00000000000..f783b1e0783
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_delayed_parallel_slave_sbm.result
@@ -0,0 +1,60 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+include/stop_slave.inc
+change master to master_delay=3, master_use_gtid=Slave_Pos;
+set @@GLOBAL.slave_parallel_threads=2;
+include/start_slave.inc
+connection master;
+create table t1 (a int);
+include/sync_slave_sql_with_master.inc
+#
+# Pt 1) Ensure SBM is updated immediately upon arrival of the next event
+# Lock t1 on slave so the first received transaction does not complete/commit
+connection slave;
+LOCK TABLES t1 WRITE;
+connection master;
+# Sleep 2 to allow a buffer between events for SBM check
+insert into t1 values (0);
+include/save_master_gtid.inc
+connection slave;
+# Waiting for transaction to arrive on slave and begin SQL Delay..
+# Validating SBM is updated on event arrival..
+# ..done
+connection slave;
+UNLOCK TABLES;
+include/sync_with_master_gtid.inc
+#
+# Pt 2) If the SQL thread has not entered an idle state, ensure
+# following events do not update SBM
+# Stop slave IO thread so it receives both events together on restart
+connection slave;
+include/stop_slave_io.inc
+connection master;
+# Sleep 2 to allow a buffer between events for SBM check
+insert into t1 values (1);
+# Sleep 3 to create gap between events
+insert into t1 values (2);
+connection slave;
+LOCK TABLES t1 WRITE;
+START SLAVE IO_THREAD;
+# Wait for first transaction to complete SQL delay and begin execution..
+# Validate SBM calculation doesn't use the second transaction because SQL thread shouldn't have gone idle..
+# ..and that SBM wasn't calculated using prior committed transactions
+# ..done
+connection slave;
+UNLOCK TABLES;
+#
+# Cleanup
+# Reset master_delay
+include/stop_slave.inc
+CHANGE MASTER TO master_delay=0;
+set @@GLOBAL.slave_parallel_threads=4;
+include/start_slave.inc
+connection master;
+DROP TABLE t1;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+include/rpl_end.inc
+# End of rpl_delayed_parallel_slave_sbm.test
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_analyze.result b/mysql-test/suite/rpl/r/rpl_parallel_analyze.result
new file mode 100644
index 00000000000..c0a2abbc1ee
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_parallel_analyze.result
@@ -0,0 +1,76 @@
+include/master-slave.inc
+[connection master]
+# Initialize
+connection slave;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+# Setup data
+connection master;
+CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE ta (a int);
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+connection master;
+SET sql_log_bin=0;
+CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
+RETURNS INT DETERMINISTIC
+BEGIN
+RETURN x;
+END
+||
+SET sql_log_bin=1;
+connection slave;
+SET sql_log_bin=0;
+CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
+RETURNS INT DETERMINISTIC
+BEGIN
+IF d1 != '' THEN
+SET debug_sync = d1;
+END IF;
+IF d2 != '' THEN
+SET debug_sync = d2;
+END IF;
+RETURN x;
+END
+||
+SET sql_log_bin=1;
+include/stop_slave.inc
+SET @old_parallel_threads =@@GLOBAL.slave_parallel_threads;
+SET @old_parallel_mode =@@GLOBAL.slave_parallel_mode;
+SET @old_gtid_strict_mode =@@GLOBAL.gtid_strict_mode;
+SET GLOBAL slave_parallel_threads=10;
+SET GLOBAL slave_parallel_mode=conservative;
+SET GLOBAL gtid_strict_mode=ON;
+include/start_slave.inc
+connection master;
+SET @old_format= @@SESSION.binlog_format;
+SET binlog_format=statement;
+INSERT INTO t1 VALUES (foo(1, 'rpl_parallel_after_mark_start_commit WAIT_FOR sig_go', ''));
+ANALYZE TABLE ta;
+Table Op Msg_type Msg_text
+test.ta analyze status Engine-independent statistics collected
+test.ta analyze status Table is already up to date
+include/save_master_gtid.inc
+connection slave;
+SELECT info FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit";
+info
+ANALYZE TABLE ta
+set @@debug_sync="now signal sig_go";
+include/sync_with_master_gtid.inc
+# Cleanup
+connection master;
+DROP TABLE t1,ta;
+connection slave;
+SET DEBUG_SYNC='RESET';
+connection master;
+SET DEBUG_SYNC='RESET';
+DROP FUNCTION foo;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+include/stop_slave.inc
+SET @@GLOBAL.slave_parallel_threads=@old_parallel_threads;
+SET @@GLOBAL.slave_parallel_mode =@old_parallel_mode;
+SET @@GLOBAL.gtid_strict_mode =@old_gtid_strict_mode;
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm-slave.opt b/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm-slave.opt
new file mode 100644
index 00000000000..9eea6a54b68
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm-slave.opt
@@ -0,0 +1 @@
+--slave-parallel-threads=4
diff --git a/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm.test b/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm.test
new file mode 100644
index 00000000000..4bcb7ad9e3f
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_delayed_parallel_slave_sbm.test
@@ -0,0 +1,133 @@
+#
+# This test ensures that after a delayed parallel slave has idled, i.e.
+# executed everything in its relay log, the next event group that the SQL
+# thread reads from the relay log will immediately be used in the
+# Seconds_Behind_Master. In particular, it ensures that the calculation for
+# Seconds_Behind_Master is based on the timestamp of the new transaction,
+# rather than the last committed transaction.
+#
+# References:
+# MDEV-29639: Seconds_Behind_Master is incorrect for Delayed, Parallel
+# Replicas
+#
+
+--source include/master-slave.inc
+
+--connection slave
+--source include/stop_slave.inc
+--let $master_delay= 3
+--eval change master to master_delay=$master_delay, master_use_gtid=Slave_Pos
+--let $old_slave_threads= `SELECT @@GLOBAL.slave_parallel_threads`
+set @@GLOBAL.slave_parallel_threads=2;
+--source include/start_slave.inc
+
+--connection master
+create table t1 (a int);
+--source include/sync_slave_sql_with_master.inc
+
+--echo #
+--echo # Pt 1) Ensure SBM is updated immediately upon arrival of the next event
+
+--echo # Lock t1 on slave so the first received transaction does not complete/commit
+--connection slave
+LOCK TABLES t1 WRITE;
+
+--connection master
+--echo # Sleep 2 to allow a buffer between events for SBM check
+sleep 2;
+
+--let $ts_trx_before_ins= `SELECT UNIX_TIMESTAMP()`
+--let insert_ctr= 0
+--eval insert into t1 values ($insert_ctr)
+--inc $insert_ctr
+--source include/save_master_gtid.inc
+
+--connection slave
+
+--echo # Waiting for transaction to arrive on slave and begin SQL Delay..
+--let $wait_condition= SELECT count(*) FROM information_schema.processlist WHERE state LIKE 'Waiting until MASTER_DELAY seconds after master executed event';
+--source include/wait_condition.inc
+
+--echo # Validating SBM is updated on event arrival..
+--let $sbm_trx1_arrive= query_get_value(SHOW SLAVE STATUS, Seconds_Behind_Master, 1)
+--let $seconds_since_idling= `SELECT UNIX_TIMESTAMP() - $ts_trx_before_ins`
+if (`SELECT $sbm_trx1_arrive > ($seconds_since_idling + 1)`)
+{
+ --echo # SBM was $sbm_trx1_arrive yet shouldn't have been larger than $seconds_since_idling + 1 (for possible negative clock_diff_with_master)
+ --die Seconds_Behind_Master should reset after idling
+}
+--echo # ..done
+
+--connection slave
+UNLOCK TABLES;
+--source include/sync_with_master_gtid.inc
+
+--echo #
+--echo # Pt 2) If the SQL thread has not entered an idle state, ensure
+--echo # following events do not update SBM
+
+--echo # Stop slave IO thread so it receives both events together on restart
+--connection slave
+--source include/stop_slave_io.inc
+
+--connection master
+
+--echo # Sleep 2 to allow a buffer between events for SBM check
+sleep 2;
+--let $ts_trxpt2_before_ins= `SELECT UNIX_TIMESTAMP()`
+--eval insert into t1 values ($insert_ctr)
+--inc $insert_ctr
+--echo # Sleep 3 to create gap between events
+sleep 3;
+--eval insert into t1 values ($insert_ctr)
+--inc $insert_ctr
+--let $ts_trx_after_ins= `SELECT UNIX_TIMESTAMP()`
+
+--connection slave
+LOCK TABLES t1 WRITE;
+
+START SLAVE IO_THREAD;
+
+--echo # Wait for first transaction to complete SQL delay and begin execution..
+--let $wait_condition= SELECT count(*) FROM information_schema.processlist WHERE state LIKE 'Waiting for table metadata lock%' AND command LIKE 'Slave_Worker';
+--source include/wait_condition.inc
+
+--echo # Validate SBM calculation doesn't use the second transaction because SQL thread shouldn't have gone idle..
+--let $sbm_after_trx_no_idle= query_get_value(SHOW SLAVE STATUS, Seconds_Behind_Master, 1)
+--let $timestamp_trxpt2_arrive= `SELECT UNIX_TIMESTAMP()`
+if (`SELECT $sbm_after_trx_no_idle < $timestamp_trxpt2_arrive - $ts_trx_after_ins`)
+{
+ --let $cmpv= `SELECT $timestamp_trxpt2_arrive - $ts_trx_after_ins`
+ --echo # SBM $sbm_after_trx_no_idle was more recent than time since last transaction ($cmpv seconds)
+ --die Seconds_Behind_Master should not have used second transaction timestamp
+}
+--let $seconds_since_idling= `SELECT ($timestamp_trxpt2_arrive - $ts_trxpt2_before_ins)`
+--echo # ..and that SBM wasn't calculated using prior committed transactions
+if (`SELECT $sbm_after_trx_no_idle > ($seconds_since_idling + 1)`)
+{
+ --echo # SBM was $sbm_after_trx_no_idle yet shouldn't have been larger than $seconds_since_idling + 1 (for possible negative clock_diff_with_master)
+ --die Seconds_Behind_Master calculation should not have used prior committed transaction
+}
+--echo # ..done
+
+--connection slave
+UNLOCK TABLES;
+
+--echo #
+--echo # Cleanup
+
+--echo # Reset master_delay
+--source include/stop_slave.inc
+--eval CHANGE MASTER TO master_delay=0
+--eval set @@GLOBAL.slave_parallel_threads=$old_slave_threads
+--source include/start_slave.inc
+
+--connection master
+DROP TABLE t1;
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+
+--source include/rpl_end.inc
+--echo # End of rpl_delayed_parallel_slave_sbm.test
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_analyze.test b/mysql-test/suite/rpl/t/rpl_parallel_analyze.test
new file mode 100644
index 00000000000..921c6f02904
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_parallel_analyze.test
@@ -0,0 +1,84 @@
+# The test file is created to prove fixes to
+# MDEV-30323 Some DDLs like ANALYZE can complete on parallel slave out of order
+# Debug-sync tests aiming at parallel replication of ADMIN commands
+# are welcome here.
+
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+--source include/master-slave.inc
+
+--echo # Initialize
+--connection slave
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+
+--echo # Setup data
+--connection master
+CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE ta (a int);
+--let $pre_load_gtid=`SELECT @@last_gtid`
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+
+--source suite/rpl/include/create_or_drop_sync_func.inc
+
+# configure MDEV-30323 slave
+--source include/stop_slave.inc
+SET @old_parallel_threads =@@GLOBAL.slave_parallel_threads;
+SET @old_parallel_mode =@@GLOBAL.slave_parallel_mode;
+SET @old_gtid_strict_mode =@@GLOBAL.gtid_strict_mode;
+SET GLOBAL slave_parallel_threads=10;
+SET GLOBAL slave_parallel_mode=conservative;
+SET GLOBAL gtid_strict_mode=ON;
+--source include/start_slave.inc
+
+# MDEV-30323 setup needs two group of events the first of which is a DML
+# and ANALYZE is the 2nd.
+# The latter is made to race in slave execution over the DML thanks
+# to a DML latency simulation.
+# In the fixed case the race-over should not be a problem: ultimately
+# ANALYZE must wait for its turn to update slave@@global.gtid_binlog_pos.
+# Otherwise the reported OOO error must be issued.
+
+--connection master
+SET @old_format= @@SESSION.binlog_format;
+SET binlog_format=statement;
+INSERT INTO t1 VALUES (foo(1, 'rpl_parallel_after_mark_start_commit WAIT_FOR sig_go', ''));
+
+ANALYZE TABLE ta;
+--source include/save_master_gtid.inc
+
+--connection slave
+--let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit"
+--source include/wait_condition.inc
+
+SELECT info FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit";
+if (`select strcmp(@@global.gtid_binlog_pos, '$pre_load_gtid') <> 0 or strcmp(@@global.gtid_slave_pos, '$pre_load_gtid') <> 0`)
+{
+ --let $bs=`SELECT @@global.gtid_binlog_pos`
+ --let $es=`SELECT @@global.gtid_slave_pos`
+ --echo Mismatch between expected $pre_load_gtid state and the actual binlog state " @@global.gtid_binlog_pos = $bs or/and slave execution state @@global.gtid_slave_pos = $es.
+ --die
+}
+
+set @@debug_sync="now signal sig_go";
+--source include/sync_with_master_gtid.inc
+
+--echo # Cleanup
+--connection master
+DROP TABLE t1,ta;
+--let $create_or_drop=drop
+--source suite/rpl/include/create_or_drop_sync_func.inc
+
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+--source include/stop_slave.inc
+SET @@GLOBAL.slave_parallel_threads=@old_parallel_threads;
+SET @@GLOBAL.slave_parallel_mode =@old_parallel_mode;
+SET @@GLOBAL.gtid_strict_mode =@old_gtid_strict_mode;
+--source include/start_slave.inc
+
+--source include/rpl_end.inc
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 18054252584..8bed77aed8d 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -228,6 +228,15 @@ FOREACH(se aria partition perfschema sql_sequence wsrep)
ENDIF()
ENDFOREACH()
+IF(VISIBILITY_HIDDEN_FLAG AND TARGET partition AND WITH_UBSAN)
+ # the spider plugin needs some partition symbols from inside mysqld
+ # when built with ubsan, in which case we need to remove
+ # -fvisibility=hidden from partition
+ GET_TARGET_PROPERTY(f partition COMPILE_FLAGS)
+ STRING(REPLACE "${VISIBILITY_HIDDEN_FLAG}" "" f ${f})
+ SET_TARGET_PROPERTIES(partition PROPERTIES COMPILE_FLAGS "${f}")
+ENDIF()
+
IF(WIN32)
SET(MYSQLD_SOURCE main.cc message.rc)
ELSE()
diff --git a/sql/discover.h b/sql/discover.h
index 1775f5d6551..750c2944ede 100644
--- a/sql/discover.h
+++ b/sql/discover.h
@@ -28,7 +28,7 @@ int writefile(const char *path, const char *db, const char *table,
inline void deletefrm(const char *path)
{
char frm_name[FN_REFLEN];
- strxmov(frm_name, path, reg_ext, NullS);
+ strxnmov(frm_name, sizeof(frm_name)-1, path, reg_ext, NullS);
mysql_file_delete(key_file_frm, frm_name, MYF(0));
}
diff --git a/sql/field.cc b/sql/field.cc
index 8afbb6fc421..a6bbd8c9c16 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1869,17 +1869,11 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
}
-void Field::hash(ulong *nr, ulong *nr2)
+void Field::hash_not_null(Hasher *hasher)
{
- if (is_null())
- {
- *nr^= (*nr << 1) | 1;
- }
- else
- {
- uint len= pack_length();
- sort_charset()->hash_sort(ptr, len, nr, nr2);
- }
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ASSERT(!is_null());
+ hasher->add(sort_charset(), ptr, pack_length());
}
size_t
@@ -8268,17 +8262,12 @@ bool Field_varstring::is_equal(const Column_definition &new_field) const
}
-void Field_varstring::hash(ulong *nr, ulong *nr2)
+void Field_varstring::hash_not_null(Hasher *hasher)
{
- if (is_null())
- {
- *nr^= (*nr << 1) | 1;
- }
- else
- {
- uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- charset()->hash_sort(ptr + length_bytes, len, nr, nr2);
- }
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ASSERT(!is_null());
+ uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ hasher->add(charset(), ptr + length_bytes, len);
}
@@ -8653,6 +8642,17 @@ oom_error:
}
+void Field_blob::hash_not_null(Hasher *hasher)
+{
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ASSERT(!is_null());
+ char *blob;
+ memcpy(&blob, ptr + packlength, sizeof(char*));
+ if (blob)
+ hasher->add(Field_blob::charset(), blob, get_length(ptr));
+}
+
+
double Field_blob::val_real(void)
{
DBUG_ASSERT(marked_for_read());
@@ -9726,20 +9726,27 @@ const DTCollation & Field_bit::dtcollation() const
}
-void Field_bit::hash(ulong *nr, ulong *nr2)
+/*
+ This method always calculates hash over 8 bytes.
+ This is different from how the HEAP engine calculate hash:
+ HEAP takes into account the actual octet size, so say for BIT(18)
+ it calculates hash over three bytes only:
+ - the incomplete byte with bits 16..17
+ - the two full bytes with bits 0..15
+ See hp_rec_hashnr(), hp_hashnr() for details.
+
+ The HEAP way is more efficient, especially for short lengths.
+ Let's consider fixing Field_bit eventually to do it in the HEAP way,
+ with proper measures to upgrade partitioned tables easy.
+*/
+void Field_bit::hash_not_null(Hasher *hasher)
{
- if (is_null())
- {
- *nr^= (*nr << 1) | 1;
- }
- else
- {
- CHARSET_INFO *cs= &my_charset_bin;
- longlong value= Field_bit::val_int();
- uchar tmp[8];
- mi_int8store(tmp,value);
- cs->hash_sort(tmp, 8, nr, nr2);
- }
+ DBUG_ASSERT(marked_for_read());
+ DBUG_ASSERT(!is_null());
+ longlong value= Field_bit::val_int();
+ uchar tmp[8];
+ mi_int8store(tmp,value);
+ hasher->add(&my_charset_bin, tmp, 8);
}
diff --git a/sql/field.h b/sql/field.h
index 43bcfe5590a..fafe0557ddc 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1827,7 +1827,14 @@ public:
key_map get_possible_keys();
/* Hash value */
- virtual void hash(ulong *nr, ulong *nr2);
+ void hash(Hasher *hasher)
+ {
+ if (is_null())
+ hasher->add_null();
+ else
+ hash_not_null(hasher);
+ }
+ virtual void hash_not_null(Hasher *hasher);
/**
Get the upper limit of the MySQL integral and floating-point type.
@@ -4196,7 +4203,7 @@ public:
uchar *new_ptr, uint32 length,
uchar *new_null_ptr, uint new_null_bit) override;
bool is_equal(const Column_definition &new_field) const override;
- void hash(ulong *nr, ulong *nr2) override;
+ void hash_not_null(Hasher *hasher) override;
uint length_size() const override { return length_bytes; }
void print_key_value(String *out, uint32 length) override;
Binlog_type_info binlog_type_info() const override;
@@ -4456,6 +4463,7 @@ public:
bool make_empty_rec_store_default_value(THD *thd, Item *item) override;
int store(const char *to, size_t length, CHARSET_INFO *charset) override;
using Field_str::store;
+ void hash_not_null(Hasher *hasher) override;
double val_real() override;
longlong val_int() override;
String *val_str(String *, String *) override;
@@ -5032,7 +5040,7 @@ public:
if (bit_ptr)
bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
}
- void hash(ulong *nr, ulong *nr2) override;
+ void hash_not_null(Hasher *hasher) override;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 7298dcd15c6..4df4a4c7c01 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -9980,8 +9980,7 @@ uint8 ha_partition::table_cache_type()
uint32 ha_partition::calculate_key_hash_value(Field **field_array)
{
- ulong nr1= 1;
- ulong nr2= 4;
+ Hasher hasher;
bool use_51_hash;
use_51_hash= MY_TEST((*field_array)->table->part_info->key_algorithm ==
partition_info::KEY_ALGORITHM_51);
@@ -10008,12 +10007,12 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
{
if (field->is_null())
{
- nr1^= (nr1 << 1) | 1;
+ hasher.add_null();
continue;
}
/* Force this to my_hash_sort_bin, which was used in 5.1! */
uint len= field->pack_length();
- my_charset_bin.hash_sort(field->ptr, len, &nr1, &nr2);
+ hasher.add(&my_charset_bin, field->ptr, len);
/* Done with this field, continue with next one. */
continue;
}
@@ -10031,12 +10030,12 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
{
if (field->is_null())
{
- nr1^= (nr1 << 1) | 1;
+ hasher.add_null();
continue;
}
/* Force this to my_hash_sort_bin, which was used in 5.1! */
uint len= field->pack_length();
- my_charset_latin1.hash_sort(field->ptr, len, &nr1, &nr2);
+ hasher.add(&my_charset_latin1, field->ptr, len);
continue;
}
/* New types in mysql-5.6. */
@@ -10063,9 +10062,9 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
}
/* fall through, use collation based hashing. */
}
- field->hash(&nr1, &nr2);
+ field->hash(&hasher);
} while (*(++field_array));
- return (uint32) nr1;
+ return (uint32) hasher.finalize();
}
diff --git a/sql/handler.cc b/sql/handler.cc
index d969c8c6ab8..7c42b3bbb6f 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -4356,6 +4356,35 @@ int handler::check_collation_compatibility()
}
+int handler::check_long_hash_compatibility() const
+{
+ if (!table->s->old_long_hash_function())
+ return 0;
+ KEY *key= table->key_info;
+ KEY *key_end= key + table->s->keys;
+ for ( ; key < key_end; key++)
+ {
+ if (key->algorithm == HA_KEY_ALG_LONG_HASH)
+ {
+ /*
+ The old (pre-MDEV-27653) hash function was wrong.
+ So the long hash unique constraint can have some
+ duplicate records. REPAIR TABLE can't fix this,
+ it will fail on a duplicate key error.
+ Only "ALTER IGNORE TABLE .. FORCE" can fix this.
+ So we need to return HA_ADMIN_NEEDS_ALTER here,
+ (not HA_ADMIN_NEEDS_UPGRADE which is used elsewhere),
+ to properly send the error message text corresponding
+ to ER_TABLE_NEEDS_REBUILD (rather than to ER_TABLE_NEEDS_UPGRADE)
+ to the user.
+ */
+ return HA_ADMIN_NEEDS_ALTER;
+ }
+ }
+ return 0;
+}
+
+
int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
{
int error;
@@ -4393,6 +4422,9 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
if (unlikely((error= check_collation_compatibility())))
return error;
+
+ if (unlikely((error= check_long_hash_compatibility())))
+ return error;
return check_for_upgrade(check_opt);
}
diff --git a/sql/handler.h b/sql/handler.h
index 3191c408e56..60cc026359b 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -3416,6 +3416,7 @@ public:
}
int check_collation_compatibility();
+ int check_long_hash_compatibility() const;
int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
/** to be actually called to get 'check()' functionality*/
int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
diff --git a/sql/item.cc b/sql/item.cc
index ac5082dbdb2..19c0b341982 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -10755,7 +10755,7 @@ table_map Item_direct_view_ref::used_tables() const
table_map used= (*ref)->used_tables();
return (used ?
used :
- ((null_ref_table != NO_NULL_TABLE) ?
+ (null_ref_table != NO_NULL_TABLE && !null_ref_table->const_table ?
null_ref_table->map :
(table_map)0 ));
}
diff --git a/sql/item.h b/sql/item.h
index 01898709131..bf111c649ae 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1295,6 +1295,12 @@ public:
*/
inline ulonglong val_uint() { return (ulonglong) val_int(); }
+ virtual bool hash_not_null(Hasher *hasher)
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+
/*
Return string representation of this item object.
@@ -3506,6 +3512,13 @@ public:
{
return Sql_mode_dependency(0, field->value_depends_on_sql_mode());
}
+ bool hash_not_null(Hasher *hasher)
+ {
+ if (field->is_null())
+ return true;
+ field->hash_not_null(hasher);
+ return false;
+ }
longlong val_int_endpoint(bool left_endp, bool *incl_endp) override;
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
bool get_date_result(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 63cf9c70e88..40181c3384c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -4870,38 +4870,18 @@ Item_cond::fix_fields(THD *thd, Item **ref)
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
return TRUE; // Fatal error flag is set!
- /*
- The following optimization reduces the depth of an AND-OR tree.
- E.g. a WHERE clause like
- F1 AND (F2 AND (F2 AND F4))
- is parsed into a tree with the same nested structure as defined
- by braces. This optimization will transform such tree into
- AND (F1, F2, F3, F4).
- Trees of OR items are flattened as well:
- ((F1 OR F2) OR (F3 OR F4)) => OR (F1, F2, F3, F4)
- Items for removed AND/OR levels will dangle until the death of the
- entire statement.
- The optimization is currently prepared statements and stored procedures
- friendly as it doesn't allocate any memory and its effects are durable
- (i.e. do not depend on PS/SP arguments).
- */
- while ((item=li++))
+
+ while (li++)
{
- while (item->type() == Item::COND_ITEM &&
- ((Item_cond*) item)->functype() == functype() &&
- !((Item_cond*) item)->list.is_empty())
- { // Identical function
- li.replace(((Item_cond*) item)->list);
- ((Item_cond*) item)->list.empty();
- item= *li.ref(); // new current item
- }
+ merge_sub_condition(li);
+ item= *li.ref();
if (abort_on_null)
item->top_level_item();
/*
replace degraded condition:
was: <field>
- become: <field> = 1
+ become: <field> != 0
*/
Item::Type type= item->type();
if (type == Item::FIELD_ITEM || type == Item::REF_ITEM)
@@ -4917,7 +4897,9 @@ Item_cond::fix_fields(THD *thd, Item **ref)
if (item->fix_fields_if_needed_for_bool(thd, li.ref()))
return TRUE; /* purecov: inspected */
- item= *li.ref(); // item can be substituted in fix_fields
+ merge_sub_condition(li);
+ item= *li.ref(); // may be substituted in fix_fields/merge_item_if_possible
+
used_tables_cache|= item->used_tables();
if (item->const_item() && !item->with_param &&
!item->is_expensive() && !cond_has_datetime_is_null(item))
@@ -4969,6 +4951,55 @@ Item_cond::fix_fields(THD *thd, Item **ref)
return FALSE;
}
+/**
+ @brief
+ Merge a lower-level condition pointed by the iterator into this Item_cond
+ if possible
+
+ @param li list iterator pointing to condition that must be
+ examined and merged if possible.
+
+ @details
+ If an item pointed by the iterator is an instance of Item_cond with the
+ same functype() as this Item_cond (i.e. both are Item_cond_and or both are
+ Item_cond_or) then the arguments of that lower-level item can be merged
+ into the list of arguments of this upper-level Item_cond.
+
+ This optimization reduces the depth of an AND-OR tree.
+ E.g. a WHERE clause like
+ F1 AND (F2 AND (F2 AND F4))
+ is parsed into a tree with the same nested structure as defined
+ by braces. This optimization will transform such tree into
+ AND (F1, F2, F3, F4).
+ Trees of OR items are flattened as well:
+ ((F1 OR F2) OR (F3 OR F4)) => OR (F1, F2, F3, F4)
+ Items for removed AND/OR levels will dangle until the death of the
+ entire statement.
+
+ The optimization is currently prepared statements and stored procedures
+ friendly as it doesn't allocate any memory and its effects are durable
+ (i.e. do not depend on PS/SP arguments).
+*/
+void Item_cond::merge_sub_condition(List_iterator<Item>& li)
+{
+ Item *item= *li.ref();
+
+ /*
+ The check for list.is_empty() is to catch empty Item_cond_and() items.
+ We may encounter Item_cond_and with an empty list, because optimizer code
+ strips multiple equalities, combines items, then adds multiple equalities
+ back
+ */
+ while (item->type() == Item::COND_ITEM &&
+ ((Item_cond*) item)->functype() == functype() &&
+ !((Item_cond*) item)->list.is_empty())
+ {
+ li.replace(((Item_cond*) item)->list);
+ ((Item_cond*) item)->list.empty();
+ item= *li.ref();
+ }
+}
+
bool
Item_cond::eval_not_null_tables(void *opt_arg)
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 25553f8d565..439e5d5a708 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -3049,6 +3049,9 @@ public:
Item *build_clone(THD *thd);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
+
+private:
+ void merge_sub_condition(List_iterator<Item>& li);
};
template <template<class> class LI, class T> class Item_equal_iterator;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 81a53a88dc6..dc9afbd5c93 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1768,7 +1768,7 @@ static void calc_hash_for_unique(ulong &nr1, ulong &nr2, String *str)
cs->hash_sort((uchar *)str->ptr(), str->length(), &nr1, &nr2);
}
-longlong Item_func_hash::val_int()
+longlong Item_func_hash_mariadb_100403::val_int()
{
DBUG_EXECUTE_IF("same_long_unique_hash", return 9;);
unsigned_flag= true;
@@ -1789,6 +1789,24 @@ longlong Item_func_hash::val_int()
}
+longlong Item_func_hash::val_int()
+{
+ DBUG_EXECUTE_IF("same_long_unique_hash", return 9;);
+ unsigned_flag= true;
+ Hasher hasher;
+ for(uint i= 0;i<arg_count;i++)
+ {
+ if (args[i]->hash_not_null(&hasher))
+ {
+ null_value= 1;
+ return 0;
+ }
+ }
+ null_value= 0;
+ return (longlong) hasher.finalize();
+}
+
+
bool Item_func_hash::fix_length_and_dec()
{
decimals= 0;
diff --git a/sql/item_func.h b/sql/item_func.h
index de515100146..523f672eba6 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1213,6 +1213,18 @@ public:
const char *func_name() const { return "<hash>"; }
};
+class Item_func_hash_mariadb_100403: public Item_func_hash
+{
+public:
+ Item_func_hash_mariadb_100403(THD *thd, List<Item> &item)
+ :Item_func_hash(thd, item)
+ {}
+ longlong val_int();
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_hash_mariadb_100403>(thd, this); }
+ const char *func_name() const { return "<hash_mariadb_100403>"; }
+};
+
class Item_longlong_func: public Item_int_func
{
public:
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index cffeeb7256e..000d198eb0c 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1612,6 +1612,18 @@ bool Item_func_ucase::fix_length_and_dec()
}
+bool Item_func_left::hash_not_null(Hasher *hasher)
+{
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> buf;
+ String *str= val_str(&buf);
+ DBUG_ASSERT((str == NULL) == null_value);
+ if (!str)
+ return true;
+ hasher->add(collation.collation, str->ptr(), str->length());
+ return false;
+}
+
+
String *Item_func_left::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 0c4967a5247..4366d65eba0 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -459,6 +459,7 @@ class Item_func_left :public Item_str_func
String tmp_value;
public:
Item_func_left(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
+ bool hash_not_null(Hasher *hasher);
String *val_str(String *);
bool fix_length_and_dec();
const char *func_name() const { return "left"; }
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 96604e5cf1d..02b9431b83e 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -367,7 +367,14 @@ public:
int8 aggr_level; /* nesting level of the aggregating subquery */
int8 max_arg_level; /* max level of unbound column references */
int8 max_sum_func_level;/* max level of aggregation for embedded functions */
- bool quick_group; /* If incremental update of fields */
+
+ /*
+ true (the default value) means this aggregate function can be computed
+ with TemporaryTableWithPartialSums algorithm (see end_update()).
+ false means this aggregate function needs OrderedGroupBy algorithm (see
+ end_write_group()).
+ */
+ bool quick_group;
/*
This list is used by the check for mixing non aggregated fields and
sum functions in the ONLY_FULL_GROUP_BY_MODE. We save all outer fields
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 35e24a521e4..8879d7daf5d 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -263,7 +263,7 @@ static char *get_plugindir()
{
static char plugin_dir[2*MAX_PATH];
get_basedir(plugin_dir, sizeof(plugin_dir), mysqld_path);
- strcat(plugin_dir, "/" STR(INSTALL_PLUGINDIR));
+ safe_strcat(plugin_dir, sizeof(plugin_dir), "/" STR(INSTALL_PLUGINDIR));
if (access(plugin_dir, 0) == 0)
return plugin_dir;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b65cd6d81bb..ebefaf00282 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4852,12 +4852,11 @@ static int init_server_components()
else // full wsrep initialization
{
// add basedir/bin to PATH to resolve wsrep script names
- char* const tmp_path= (char*)my_alloca(strlen(mysql_home) +
- strlen("/bin") + 1);
+ size_t tmp_path_size= strlen(mysql_home) + 5; /* including "/bin" */
+ char* const tmp_path= (char*)my_alloca(tmp_path_size);
if (tmp_path)
{
- strcpy(tmp_path, mysql_home);
- strcat(tmp_path, "/bin");
+ snprintf(tmp_path, tmp_path_size, "%s/bin", mysql_home);
wsrep_prepend_PATH(tmp_path);
}
else
@@ -5668,8 +5667,9 @@ int mysqld_main(int argc, char **argv)
char real_server_version[2 * SERVER_VERSION_LENGTH + 10];
set_server_version(real_server_version, sizeof(real_server_version));
- strcat(real_server_version, "' as '");
- strcat(real_server_version, server_version);
+ safe_strcat(real_server_version, sizeof(real_server_version), "' as '");
+ safe_strcat(real_server_version, sizeof(real_server_version),
+ server_version);
sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname,
real_server_version,
@@ -7916,7 +7916,8 @@ static int mysql_init_variables(void)
}
else
my_path(prg_dev, my_progname, "mysql/bin");
- strcat(prg_dev,"/../"); // Remove 'bin' to get base dir
+ // Remove 'bin' to get base dir
+ safe_strcat(prg_dev, sizeof(prg_dev), "/../");
cleanup_dirname(mysql_home,prg_dev);
}
#else
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 8409a9cd89c..6332e02453d 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -56,8 +56,7 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev,
rgi->event_relay_log_pos= qev->event_relay_log_pos;
rgi->future_event_relay_log_pos= qev->future_event_relay_log_pos;
strcpy(rgi->future_event_master_log_name, qev->future_event_master_log_name);
- if (!(ev->is_artificial_event() || ev->is_relay_log_event() ||
- (ev->when == 0)))
+ if (event_can_update_last_master_timestamp(ev))
rgi->last_master_timestamp= ev->when + (time_t)ev->exec_time;
err= apply_event_and_update_pos_for_parallel(ev, thd, rgi);
diff --git a/sql/slave.cc b/sql/slave.cc
index 715fa8cd69e..ae273da0456 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -4192,10 +4192,10 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
the user might be surprised to see a claim that the slave is up to date
long before those queued events are actually executed.
*/
- if (!rli->mi->using_parallel() &&
- !(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0)))
+ if ((!rli->mi->using_parallel()) && event_can_update_last_master_timestamp(ev))
{
rli->last_master_timestamp= ev->when + (time_t) ev->exec_time;
+ rli->sql_thread_caught_up= false;
DBUG_ASSERT(rli->last_master_timestamp >= 0);
}
@@ -4247,6 +4247,17 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
if (rli->mi->using_parallel())
{
+ if (unlikely((rli->last_master_timestamp == 0 ||
+ rli->sql_thread_caught_up) &&
+ event_can_update_last_master_timestamp(ev)))
+ {
+ if (rli->last_master_timestamp < ev->when)
+ {
+ rli->last_master_timestamp= ev->when;
+ rli->sql_thread_caught_up= false;
+ }
+ }
+
int res= rli->parallel.do_event(serial_rgi, ev, event_size);
/*
In parallel replication, we need to update the relay log position
@@ -4267,7 +4278,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
This is the case for pre-10.0 events without GTID, and for handling
slave_skip_counter.
*/
- if (!(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0)))
+ if (event_can_update_last_master_timestamp(ev))
{
/*
Ignore FD's timestamp as it does not reflect the slave execution
@@ -4275,7 +4286,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
data modification event execution last long all this time
Seconds_Behind_Master is zero.
*/
- if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT &&
+ rli->last_master_timestamp < ev->when)
rli->last_master_timestamp= ev->when + (time_t) ev->exec_time;
DBUG_ASSERT(rli->last_master_timestamp >= 0);
@@ -7615,7 +7627,6 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
if (hot_log)
mysql_mutex_unlock(log_lock);
- rli->sql_thread_caught_up= false;
DBUG_RETURN(ev);
}
if (opt_reckless_slave) // For mysql-test
@@ -7779,7 +7790,6 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
rli->relay_log.wait_for_update_relay_log(rli->sql_driver_thd);
// re-acquire data lock since we released it earlier
mysql_mutex_lock(&rli->data_lock);
- rli->sql_thread_caught_up= false;
continue;
}
/*
@@ -7970,12 +7980,19 @@ event(errno: %d cur_log->error: %d)",
{
sql_print_information("Error reading relay log event: %s",
"slave SQL thread was killed");
- DBUG_RETURN(0);
+ goto end;
}
err:
if (errmsg)
sql_print_error("Error reading relay log event: %s", errmsg);
+
+end:
+ /*
+ Set that we are not caught up so if there is a hang/problem on restart,
+ Seconds_Behind_Master will still grow.
+ */
+ rli->sql_thread_caught_up= false;
DBUG_RETURN(0);
}
#ifdef WITH_WSREP
diff --git a/sql/slave.h b/sql/slave.h
index 5ca6054a178..e2bd5cec1b9 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -49,6 +49,7 @@
#include "rpl_filter.h"
#include "rpl_tblmap.h"
#include "rpl_gtid.h"
+#include "log_event.h"
#define SLAVE_NET_TIMEOUT 60
@@ -293,6 +294,17 @@ extern char *report_host, *report_password;
extern I_List<THD> threads;
+/*
+ Check that a binlog event (read from the relay log) is valid to update
+ last_master_timestamp. That is, a valid event is one with a consistent
+ timestamp which originated from a primary server.
+*/
+static inline bool event_can_update_last_master_timestamp(Log_event *ev)
+{
+ return ev && !(ev->is_artificial_event() || ev->is_relay_log_event() ||
+ (ev->when == 0));
+}
+
#else
#define close_active_mi() /* no-op */
#endif /* HAVE_REPLICATION */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 060dcc059b5..2967339d404 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -37,7 +37,8 @@ const LEX_CSTRING msg_status= {STRING_WITH_LEN("status")};
/* Prepare, run and cleanup for mysql_recreate_table() */
-static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
+static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list,
+ Recreate_info *recreate_info)
{
bool result_code;
DBUG_ENTER("admin_recreate_table");
@@ -58,7 +59,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
DEBUG_SYNC(thd, "ha_admin_try_alter");
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
result_code= (thd->open_temporary_tables(table_list) ||
- mysql_recreate_table(thd, table_list, false));
+ mysql_recreate_table(thd, table_list, recreate_info, false));
reenable_binlog(thd);
/*
mysql_recreate_table() can push OK or ERROR.
@@ -528,6 +529,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
bool open_error;
bool collect_eis= FALSE;
bool open_for_modify= org_open_for_modify;
+ Recreate_info recreate_info;
DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str));
DEBUG_SYNC(thd, "admin_command_kill_before_modify");
@@ -787,7 +789,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
{
/* We use extra_open_options to be able to open crashed tables */
thd->open_options|= extra_open_options;
- result_code= admin_recreate_table(thd, table);
+ result_code= admin_recreate_table(thd, table, &recreate_info) ?
+ HA_ADMIN_FAILED : HA_ADMIN_OK;
thd->open_options&= ~extra_open_options;
goto send_result;
}
@@ -968,12 +971,31 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
repair was not implemented and we need to upgrade the table
to a new version so we recreate the table with ALTER TABLE
*/
- result_code= admin_recreate_table(thd, table);
+ result_code= admin_recreate_table(thd, table, &recreate_info);
}
send_result:
lex->cleanup_after_one_table_open();
thd->clear_error(); // these errors shouldn't get client
+
+ if (recreate_info.records_duplicate())
+ {
+ protocol->prepare_for_resend();
+ protocol->store(&table_name, system_charset_info);
+ protocol->store((char*) operator_name, system_charset_info);
+ protocol->store(warning_level_names[Sql_condition::WARN_LEVEL_WARN].str,
+ warning_level_names[Sql_condition::WARN_LEVEL_WARN].length,
+ system_charset_info);
+ char buf[80];
+ size_t length= my_snprintf(buf, sizeof(buf),
+ "Number of rows changed from %u to %u",
+ (uint) recreate_info.records_processed(),
+ (uint) recreate_info.records_copied());
+ protocol->store(buf, length, system_charset_info);
+ if (protocol->write())
+ goto err;
+ }
+
{
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
@@ -1083,7 +1105,7 @@ send_result_message:
*save_next_global= table->next_global;
table->next_local= table->next_global= 0;
- result_code= admin_recreate_table(thd, table);
+ result_code= admin_recreate_table(thd, table, &recreate_info);
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
@@ -1278,6 +1300,8 @@ send_result_message:
goto err;
DEBUG_SYNC(thd, "admin_command_kill_after_modify");
}
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_analyze_table_sleep", my_sleep(500000););
if (is_table_modified && is_cmd_replicated &&
(!opt_readonly || thd->slave_thread) && !thd->lex->no_write_to_binlog)
{
@@ -1287,10 +1311,8 @@ send_result_message:
if (res)
goto err;
}
-
my_eof(thd);
- thd->resume_subsequent_commits(suspended_wfc);
- DBUG_EXECUTE_IF("inject_analyze_table_sleep", my_sleep(500000););
+
DBUG_RETURN(FALSE);
err:
@@ -1438,6 +1460,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
LEX *m_lex= thd->lex;
TABLE_LIST *first_table= m_lex->first_select_lex()->table_list.first;
bool res= TRUE;
+ Recreate_info recreate_info;
DBUG_ENTER("Sql_cmd_optimize_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
@@ -1446,7 +1469,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
- mysql_recreate_table(thd, first_table, true) :
+ mysql_recreate_table(thd, first_table, &recreate_info, true) :
mysql_admin_table(thd, first_table, &m_lex->check_opt,
"optimize", TL_WRITE, 1, 0, 0, 0,
&handler::ha_optimize, 0, true);
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index dee5ea2fe4b..a4b0c24cc6b 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -550,9 +550,11 @@ bool Sql_cmd_alter_table::execute(THD *thd)
thd->work_part_info= 0;
#endif
+ Recreate_info recreate_info;
result= mysql_alter_table(thd, &select_lex->db, &lex->name,
&create_info,
first_table,
+ &recreate_info,
&alter_info,
select_lex->order_list.elements,
select_lex->order_list.first,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d29f5832434..66c9e3da58d 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -8276,6 +8276,20 @@ bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
return 0;
}
+
+void THD::my_ok_with_recreate_info(const Recreate_info &info,
+ ulong warn_count)
+{
+ char buf[80];
+ my_snprintf(buf, sizeof(buf),
+ ER_THD(this, ER_INSERT_INFO),
+ (ulong) info.records_processed(),
+ (ulong) info.records_duplicate(),
+ warn_count);
+ my_ok(this, info.records_processed(), 0L, buf);
+}
+
+
THD_list_iterator *THD_list_iterator::iterator()
{
return &server_threads;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1dced52c133..ef4fc58f4e2 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -243,6 +243,29 @@ public:
};
+class Recreate_info
+{
+ ha_rows m_records_copied;
+ ha_rows m_records_duplicate;
+public:
+ Recreate_info()
+ :m_records_copied(0),
+ m_records_duplicate(0)
+ { }
+ Recreate_info(ha_rows records_copied,
+ ha_rows records_duplicate)
+ :m_records_copied(records_copied),
+ m_records_duplicate(records_duplicate)
+ { }
+ ha_rows records_copied() const { return m_records_copied; }
+ ha_rows records_duplicate() const { return m_records_duplicate; }
+ ha_rows records_processed() const
+ {
+ return m_records_copied + m_records_duplicate;
+ }
+};
+
+
#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
extern ulong tc_heuristic_recover;
@@ -4102,6 +4125,8 @@ public:
inline bool vio_ok() const { return TRUE; }
inline bool is_connected() { return TRUE; }
#endif
+
+ void my_ok_with_recreate_info(const Recreate_info &info, ulong warn_count);
/**
Mark the current error as fatal. Warning: this does not
set any error, it sets a property of the error, so must be
@@ -5963,6 +5988,12 @@ public:
uint sum_func_count;
uint hidden_field_count;
uint group_parts,group_length,group_null_parts;
+
+ /*
+ If we're doing a GROUP BY operation, shows which one is used:
+ true TemporaryTableWithPartialSums algorithm (see end_update()).
+ false OrderedGroupBy algorithm (see end_write_group()).
+ */
uint quick_group;
/**
Enabled when we have atleast one outer_sum_func. Needed when used
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index 91300e3a326..e22aa1ebd6f 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -102,49 +102,6 @@ bool LEX::check_dependencies_in_with_clauses()
/**
@brief
- Resolve references to CTE in specification of hanging CTE
-
- @details
- A CTE to which there are no references in the query is called hanging CTE.
- Although such CTE is not used for execution its specification must be
- subject to context analysis. All errors concerning references to
- non-existing tables or fields occurred in the specification must be
- reported as well as all other errors caught at the prepare stage.
- The specification of a hanging CTE might contain references to other
- CTE outside of the specification and within it if the specification
- contains a with clause. This function resolves all such references for
- all hanging CTEs encountered in the processed query.
-
- @retval
- false on success
- true on failure
-*/
-
-bool
-LEX::resolve_references_to_cte_in_hanging_cte()
-{
- for (With_clause *with_clause= with_clauses_list;
- with_clause; with_clause= with_clause->next_with_clause)
- {
- for (With_element *with_elem= with_clause->with_list.first;
- with_elem; with_elem= with_elem->next)
- {
- if (!with_elem->is_referenced())
- {
- TABLE_LIST *first_tbl=
- with_elem->spec->first_select()->table_list.first;
- TABLE_LIST **with_elem_end_pos= with_elem->head->tables_pos.end_pos;
- if (first_tbl && resolve_references_to_cte(first_tbl, with_elem_end_pos))
- return true;
- }
- }
- }
- return false;
-}
-
-
-/**
- @brief
Resolve table references to CTE from a sub-chain of table references
@param tables Points to the beginning of the sub-chain
@@ -289,8 +246,6 @@ LEX::check_cte_dependencies_and_resolve_references()
return false;
if (resolve_references_to_cte(query_tables, query_tables_last))
return true;
- if (resolve_references_to_cte_in_hanging_cte())
- return true;
return false;
}
@@ -489,47 +444,33 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
st_unit_ctxt_elem *ctxt)
{
With_element *found= 0;
+ st_select_lex_unit *top_unit= 0;
for (st_unit_ctxt_elem *unit_ctxt_elem= ctxt;
unit_ctxt_elem;
unit_ctxt_elem= unit_ctxt_elem->prev)
{
st_select_lex_unit *unit= unit_ctxt_elem->unit;
With_clause *with_clause= unit->with_clause;
- /*
- First look for the table definition in the with clause attached to 'unit'
- if there is any such clause.
- */
if (with_clause)
{
- found= with_clause->find_table_def(tbl, NULL);
+ /*
+ If the reference to tbl that has to be resolved belongs to
+ the FROM clause of a descendant of top_unit->with_element
+ and this with element belongs to with_clause then this
+ element must be used as the barrier for the search in the
+ the list of CTEs from with_clause unless the clause contains
+ RECURSIVE.
+ */
+ With_element *barrier= 0;
+ if (top_unit && !with_clause->with_recursive &&
+ top_unit->with_element &&
+ top_unit->with_element->get_owner() == with_clause)
+ barrier= top_unit->with_element;
+ found= with_clause->find_table_def(tbl, barrier);
if (found)
break;
}
- /*
- If 'unit' is the unit that defines a with element then reset 'unit'
- to the unit whose attached with clause contains this with element.
- */
- With_element *with_elem= unit->with_element;
- if (with_elem)
- {
- if (!(unit_ctxt_elem= unit_ctxt_elem->prev))
- break;
- unit= unit_ctxt_elem->unit;
- }
- with_clause= unit->with_clause;
- /*
- Now look for the table definition in this with clause. If the with clause
- contains RECURSIVE the search is performed through all CTE definitions in
- clause, otherwise up to the definition of 'with_elem' unless it is NULL.
- */
- if (with_clause)
- {
- found= with_clause->find_table_def(tbl,
- with_clause->with_recursive ?
- NULL : with_elem);
- if (found)
- break;
- }
+ top_unit= unit;
}
return found;
}
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index e0fbd79803b..6a1f67d3258 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -326,8 +326,6 @@ public:
friend
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
TABLE_LIST **tables_last);
- friend
- bool LEX::resolve_references_to_cte_in_hanging_cte();
};
const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8;
@@ -441,9 +439,6 @@ public:
friend
bool LEX::check_dependencies_in_with_clauses();
-
- friend
- bool LEX::resolve_references_to_cte_in_hanging_cte();
};
inline
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index d9796fb4380..132934773f9 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3962,7 +3962,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
lex->current_select->join->select_options|= OPTION_BUFFER_RESULT;
}
else if (!(lex->current_select->options & OPTION_BUFFER_RESULT) &&
- thd->locked_tables_mode <= LTM_LOCK_TABLES)
+ thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ !table->s->long_unique_table)
{
/*
We must not yet prepare the result table if it is the same as one of the
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 413cd0f18e7..fad9aabdc90 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1308,8 +1308,6 @@ void LEX::start(THD *thd_arg)
stmt_var_list.empty();
proc_list.elements=0;
- save_group_list.empty();
- save_order_list.empty();
win_ref= NULL;
win_frame= NULL;
frame_top_bound= NULL;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 731ddbaefac..374c923a6b9 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1116,6 +1116,7 @@ public:
group_list_ptrs, and re-establish the original list before each execution.
*/
SQL_I_List<ORDER> group_list;
+ SQL_I_List<ORDER> save_group_list;
Group_list_ptrs *group_list_ptrs;
List<Item> item_list; /* list of fields & expressions */
@@ -1181,6 +1182,7 @@ public:
const char *type; /* type of select for EXPLAIN */
SQL_I_List<ORDER> order_list; /* ORDER clause */
+ SQL_I_List<ORDER> save_order_list;
SQL_I_List<ORDER> gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
bool is_set_query_expr_tail;
@@ -3537,8 +3539,6 @@ public:
}
- SQL_I_List<ORDER> save_group_list;
- SQL_I_List<ORDER> save_order_list;
LEX_CSTRING *win_ref;
Window_frame *win_frame;
Window_frame_bound *frame_top_bound;
@@ -4778,12 +4778,11 @@ public:
const LEX_CSTRING *constraint_name,
Table_ident *ref_table_name,
DDL_options ddl_options);
+
bool check_dependencies_in_with_clauses();
- bool resolve_references_to_cte_in_hanging_cte();
bool check_cte_dependencies_and_resolve_references();
bool resolve_references_to_cte(TABLE_LIST *tables,
TABLE_LIST **tables_last);
-
};
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 9d159071ff3..a9ab5415d5a 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -54,7 +54,7 @@ public:
{
elements= tmp.elements;
first= tmp.first;
- next= tmp.next;
+ next= elements ? tmp.next : &first;;
return *this;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b56e5742845..75381c9a894 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4270,8 +4270,10 @@ mysql_execute_command(THD *thd)
WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL);
+ Recreate_info recreate_info;
res= mysql_alter_table(thd, &first_table->db, &first_table->table_name,
- &create_info, first_table, &alter_info,
+ &create_info, first_table,
+ &recreate_info, &alter_info,
0, (ORDER*) 0, 0, lex->if_exists());
break;
}
@@ -8894,8 +8896,8 @@ TABLE_LIST *st_select_lex::convert_right_join()
void st_select_lex::prepare_add_window_spec(THD *thd)
{
LEX *lex= thd->lex;
- lex->save_group_list= group_list;
- lex->save_order_list= order_list;
+ save_group_list= group_list;
+ save_order_list= order_list;
lex->win_ref= NULL;
lex->win_frame= NULL;
lex->frame_top_bound= NULL;
@@ -8922,8 +8924,8 @@ bool st_select_lex::add_window_def(THD *thd,
win_part_list_ptr,
win_order_list_ptr,
win_frame);
- group_list= thd->lex->save_group_list;
- order_list= thd->lex->save_order_list;
+ group_list= save_group_list;
+ order_list= save_order_list;
if (parsing_place != SELECT_LIST)
{
fields_in_window_functions+= win_part_list_ptr->elements +
@@ -8949,8 +8951,8 @@ bool st_select_lex::add_window_spec(THD *thd,
win_part_list_ptr,
win_order_list_ptr,
win_frame);
- group_list= thd->lex->save_group_list;
- order_list= thd->lex->save_order_list;
+ group_list= save_group_list;
+ order_list= save_order_list;
if (parsing_place != SELECT_LIST)
{
fields_in_window_functions+= win_part_list_ptr->elements +
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 8c2dbc91ec2..c50a59a62cb 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -342,7 +342,7 @@ static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *,
struct st_plugin_int **);
static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(struct system_variables *vars);
-static void plugin_vars_free_values(sys_var *vars);
+static void plugin_vars_free_values(st_mysql_sys_var **vars);
static void restore_ptr_backup(uint n, st_ptr_backup *backup);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void);
@@ -1290,7 +1290,7 @@ static void plugin_del(struct st_plugin_int *plugin)
DBUG_ENTER("plugin_del");
mysql_mutex_assert_owner(&LOCK_plugin);
/* Free allocated strings before deleting the plugin. */
- plugin_vars_free_values(plugin->system_vars);
+ plugin_vars_free_values(plugin->plugin->system_vars);
restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
if (plugin->plugin_dl)
{
@@ -2941,6 +2941,7 @@ sys_var *find_sys_var(THD *thd, const char *str, size_t length,
/*
called by register_var, construct_options and test_plugin_options.
Returns the 'bookmark' for the named variable.
+ returns null for non thd-local variables.
LOCK_system_variables_hash should be at least read locked
*/
static st_bookmark *find_bookmark(const char *plugin, const char *name,
@@ -2997,7 +2998,6 @@ static size_t var_storage_size(int flags)
/*
returns a bookmark for thd-local variables, creating if neccessary.
- returns null for non thd-local variables.
Requires that a write lock is obtained on LOCK_system_variables_hash
*/
static st_bookmark *register_var(const char *plugin, const char *name,
@@ -3351,27 +3351,35 @@ void plugin_thdvar_cleanup(THD *thd)
variables are no longer accessible and the value space is lost. Note
that only string values with PLUGIN_VAR_MEMALLOC are allocated and
must be freed.
-
- @param[in] vars Chain of system variables of a plugin
*/
-static void plugin_vars_free_values(sys_var *vars)
+static void plugin_vars_free_values(st_mysql_sys_var **vars)
{
DBUG_ENTER("plugin_vars_free_values");
- for (sys_var *var= vars; var; var= var->next)
+ if (!vars)
+ DBUG_VOID_RETURN;
+
+ while(st_mysql_sys_var *var= *vars++)
{
- sys_var_pluginvar *piv= var->cast_pluginvar();
- if (piv &&
- ((piv->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
- (piv->plugin_var->flags & PLUGIN_VAR_MEMALLOC))
+ if ((var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ var->flags & PLUGIN_VAR_MEMALLOC)
{
- /* Free the string from global_system_variables. */
- char **valptr= (char**) piv->real_value_ptr(NULL, OPT_GLOBAL);
+ char **val;
+ if (var->flags & PLUGIN_VAR_THDLOCAL)
+ {
+ st_bookmark *v= find_bookmark(0, var->name, var->flags);
+ if (!v)
+ continue;
+ val= (char**)(global_system_variables.dynamic_variables_ptr + v->offset);
+ }
+ else
+ val= *(char***) (var + 1);
+
DBUG_PRINT("plugin", ("freeing value for: '%s' addr: %p",
- var->name.str, valptr));
- my_free(*valptr);
- *valptr= NULL;
+ var->name, val));
+ my_free(*val);
+ *val= NULL;
}
}
DBUG_VOID_RETURN;
@@ -4031,7 +4039,7 @@ static my_option *construct_help_options(MEM_ROOT *mem_root,
bzero(opts, sizeof(my_option) * count);
/**
- some plugin variables (those that don't have PLUGIN_VAR_NOSYSVAR flag)
+ some plugin variables
have their names prefixed with the plugin name. Restore the names here
to get the correct (not double-prefixed) help text.
We won't need @@sysvars anymore and don't care about their proper names.
@@ -4143,9 +4151,6 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
char *varname;
sys_var *v;
- if (o->flags & PLUGIN_VAR_NOSYSVAR)
- continue;
-
tmp_backup[tmp->nbackups++].save(&o->name);
if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
{
@@ -4161,6 +4166,12 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
my_casedn_str(&my_charset_latin1, varname);
convert_dash_to_underscore(varname, len-1);
}
+ if (o->flags & PLUGIN_VAR_NOSYSVAR)
+ {
+ o->name= varname;
+ continue;
+ }
+
const char *s= o->flags & PLUGIN_VAR_DEPRECATED ? "" : NULL;
v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o, s);
v->test_load= (var ? &var->loaded : &static_unload);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a721899a8be..2bcd38bf502 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -3539,15 +3539,26 @@ bool JOIN::make_aggr_tables_info()
/*
If we have different sort & group then we must sort the data by group
- and copy it to another tmp table
+ and copy it to another tmp table.
+
This code is also used if we are using distinct something
we haven't been able to store in the temporary table yet
like SEC_TO_TIME(SUM(...)).
+
+ 3. Also, this is used when
+ - the query has Window functions,
+ - the GROUP BY operation is done with OrderedGroupBy algorithm.
+ In this case, the first temptable will contain pre-GROUP-BY data. Force
+ the creation of the second temporary table. Post-GROUP-BY dataset will be
+ written there, and then Window Function processing code will be able to
+ process it.
*/
if ((group_list &&
(!test_if_subpart(group_list, order) || select_distinct)) ||
- (select_distinct && tmp_table_param.using_outer_summary_function))
- { /* Must copy to another table */
+ (select_distinct && tmp_table_param.using_outer_summary_function) ||
+ (group_list && !tmp_table_param.quick_group && // (3)
+ select_lex->have_window_funcs())) // (3)
+ { /* Must copy to another table */
DBUG_PRINT("info",("Creating group table"));
calc_group_buffer(this, group_list);
@@ -7992,7 +8003,27 @@ best_access_path(JOIN *join,
(table->file->index_flags(start_key->key,0,1) &
HA_DO_RANGE_FILTER_PUSHDOWN))
{
- double rows= record_count * records;
+ double rows;
+ if (type == JT_EQ_REF)
+ {
+ /*
+ Treat EQ_REF access in a special way:
+ 1. We have no cost for index-only read. Assume its cost is 50% of
+ the cost of the full read.
+
+ 2. A regular ref access will do #record_count lookups, but eq_ref
+ has "lookup cache" which reduces the number of lookups made.
+ The estimation code uses prev_record_reads() call to estimate:
+
+ tmp = prev_record_reads(join_positions, idx, found_ref);
+
+ Set the effective number of rows from "tmp" here.
+ */
+ keyread_tmp= tmp/ 2;
+ rows= tmp;
+ }
+ else
+ rows= record_count * records;
/*
If we use filter F with selectivity s the the cost of fetching data
@@ -8035,10 +8066,6 @@ best_access_path(JOIN *join,
we cannot use filters as the cost calculation below would cause
tmp to become negative. The future resultion is to not limit
cost with worst_seek.
-
- We cannot use filter with JT_EQ_REF as in this case 'tmp' is
- number of rows from prev_record_read() and keyread_tmp is 0. These
- numbers are not usable with rowid filter code.
*/
double access_cost_factor= MY_MIN((rows - keyread_tmp) / rows, 1.0);
if (!(records < s->worst_seeks &&
@@ -8046,7 +8073,7 @@ best_access_path(JOIN *join,
trace_access_idx.add("rowid_filter_skipped", "worst/max seeks clipping");
else if (access_cost_factor <= 0.0)
trace_access_idx.add("rowid_filter_skipped", "cost_factor <= 0");
- else if (type != JT_EQ_REF)
+ else
{
filter=
table->best_range_rowid_filter_for_partial_join(start_key->key,
@@ -22380,11 +22407,17 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
/*
@brief
- Perform a GROUP BY operation over a stream of rows ordered by their group.
- The result is sent into join->result.
+ Perform OrderedGroupBy operation and write the output into join->result.
@detail
- Also applies HAVING, etc.
+ The input stream is ordered by the GROUP BY expression, so groups come
+ one after another. We only need to accumulate the aggregate value, when
+ a GROUP BY group ends, check the HAVING and send the group.
+
+ Note that the output comes in the GROUP BY order, which is required by
+ the MySQL's GROUP BY semantics. No further sorting is needed.
+
+ @seealso end_write_group() also implements SortAndGroup
*/
enum_nested_loop_state
@@ -22574,13 +22607,26 @@ end:
/*
@brief
- Perform a GROUP BY operation over rows coming in arbitrary order.
-
- This is done by looking up the group in a temp.table and updating group
- values.
+ Perform GROUP BY operation over rows coming in arbitrary order: use
+ TemporaryTableWithPartialSums algorithm.
+
+ @detail
+ The TemporaryTableWithPartialSums algorithm is:
+
+ CREATE TEMPORARY TABLE tmp (
+ group_by_columns PRIMARY KEY,
+ partial_sum
+ );
+
+ for each row R in join output {
+ INSERT INTO tmp (R.group_by_columns, R.sum_value)
+ ON DUPLICATE KEY UPDATE partial_sum=partial_sum + R.sum_value;
+ }
@detail
Also applies HAVING, etc.
+
+ @seealso end_unique_update()
*/
static enum_nested_loop_state
@@ -22730,13 +22776,15 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
/*
@brief
- Perform a GROUP BY operation over a stream of rows ordered by their group.
- Write the result into a temporary table.
+ Perform OrderedGroupBy operation and write the output into the temporary
+ table (join_tab->table).
@detail
- Also applies HAVING, etc.
+ The input stream is ordered by the GROUP BY expression, so groups come
+ one after another. We only need to accumulate the aggregate value, when
+ a GROUP BY group ends, check the HAVING and write the group.
- The rows are written into temptable so e.g. filesort can read them.
+ @seealso end_send_group() also implements OrderedGroupBy
*/
enum_nested_loop_state
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 807b4115fec..fb36fd90a0d 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1434,12 +1434,30 @@ public:
(set in make_join_statistics())
*/
bool impossible_where;
- List<Item> all_fields; ///< to store all fields that used in query
+
+ /*
+ All fields used in the query processing.
+
+ Initially this is a list of fields from the query's SQL text.
+
+ Then, ORDER/GROUP BY and Window Function code add columns that need to
+ be saved to be available in the post-group-by context. These extra columns
+ are added to the front, because this->all_fields points to the suffix of
+ this list.
+ */
+ List<Item> all_fields;
///Above list changed to use temporary table
List<Item> tmp_all_fields1, tmp_all_fields2, tmp_all_fields3;
///Part, shared with list above, emulate following list
List<Item> tmp_fields_list1, tmp_fields_list2, tmp_fields_list3;
- List<Item> &fields_list; ///< hold field list passed to mysql_select
+
+ /*
+ The original field list as it was passed to mysql_select(). This refers
+ to select_lex->item_list.
+ CAUTION: this list is a suffix of this->all_fields list, that is, it shares
+ elements with that list!
+ */
+ List<Item> &fields_list;
List<Item> procedure_fields_list;
int error;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 24ce892fb12..15be1c66f2a 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -10018,6 +10018,7 @@ 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,
+ Recreate_info *recreate_info,
Alter_info *alter_info,
uint order_num, ORDER *order, bool ignore,
bool if_exists)
@@ -11302,11 +11303,10 @@ end_inplace:
end_temporary:
thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
- 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_buff);
+ *recreate_info= Recreate_info(copied, deleted);
+ thd->my_ok_with_recreate_info(*recreate_info,
+ (ulong) thd->get_stmt_da()->
+ current_statement_warn_count());
DEBUG_SYNC(thd, "alter_table_inplace_trans_commit");
DBUG_RETURN(false);
@@ -11816,7 +11816,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
Like mysql_alter_table().
*/
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
+ Recreate_info *recreate_info, bool table_copy)
{
HA_CREATE_INFO create_info;
Alter_info alter_info;
@@ -11842,8 +11843,11 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
Alter_info::ALTER_TABLE_ALGORITHM_COPY);
bool res= mysql_alter_table(thd, &null_clex_str, &null_clex_str, &create_info,
- table_list, &alter_info, 0,
- (ORDER *) 0, 0, 0);
+ table_list, recreate_info, &alter_info, 0,
+ (ORDER *) 0,
+ // Ignore duplicate records on REPAIR
+ thd->lex->sql_command == SQLCOM_REPAIR,
+ 0);
table_list->next_global= next_table;
DBUG_RETURN(res);
}
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 53741d934cc..f5c783f6f3d 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -224,6 +224,7 @@ 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,
+ class Recreate_info *recreate_info,
Alter_info *alter_info,
uint order_num, ORDER *order, bool ignore,
bool if_exists);
@@ -231,7 +232,8 @@ bool mysql_compare_tables(TABLE *table,
Alter_info *alter_info,
HA_CREATE_INFO *create_info,
bool *metadata_equal);
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy);
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
+ class Recreate_info *recreate_info, bool table_copy);
bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
TABLE_LIST *src_table,
Table_specification_st *create_info);
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 8a3a3776b52..5d2f08cb1e3 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -124,6 +124,32 @@ enum scalar_comparison_op
};
+class Hasher
+{
+ ulong m_nr1;
+ ulong m_nr2;
+public:
+ Hasher(): m_nr1(1), m_nr2(4)
+ { }
+ void add_null()
+ {
+ m_nr1^= (m_nr1 << 1) | 1;
+ }
+ void add(CHARSET_INFO *cs, const uchar *str, size_t length)
+ {
+ cs->coll->hash_sort(cs, str, length, &m_nr1, &m_nr2);
+ }
+ void add(CHARSET_INFO *cs, const char *str, size_t length)
+ {
+ add(cs, (const uchar *) str, length);
+ }
+ uint32 finalize() const
+ {
+ return (uint32) m_nr1;
+ }
+};
+
+
enum partition_value_print_mode_t
{
PARTITION_VALUE_PRINT_MODE_SHOW= 0,
diff --git a/sql/table.cc b/sql/table.cc
index 7334f0143e6..98a9665ba27 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1097,6 +1097,18 @@ static void mysql57_calculate_null_position(TABLE_SHARE *share,
}
}
+
+Item_func_hash *TABLE_SHARE::make_long_hash_func(THD *thd,
+ MEM_ROOT *mem_root,
+ List<Item> *field_list)
+ const
+{
+ if (old_long_hash_function())
+ return new (mem_root) Item_func_hash_mariadb_100403(thd, *field_list);
+ return new (mem_root) Item_func_hash(thd, *field_list);
+}
+
+
/** Parse TABLE_SHARE::vcol_defs
unpack_vcol_info_from_frm
@@ -1308,7 +1320,10 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
list_item= new (mem_root) Item_field(thd, keypart->field);
field_list->push_back(list_item, mem_root);
}
- Item_func_hash *hash_item= new(mem_root)Item_func_hash(thd, *field_list);
+
+ Item_func_hash *hash_item= table->s->make_long_hash_func(thd, mem_root,
+ field_list);
+
Virtual_column_info *v= new (mem_root) Virtual_column_info();
field->vcol_info= v;
field->vcol_info->expr= hash_item;
diff --git a/sql/table.h b/sql/table.h
index d704f3ce05e..0b4faba1b0b 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -55,6 +55,7 @@ class Item; /* Needed by ORDER */
typedef Item (*Item_ptr);
class Item_subselect;
class Item_field;
+class Item_func_hash;
class GRANT_TABLE;
class st_select_lex_unit;
class st_select_lex;
@@ -1165,6 +1166,21 @@ struct TABLE_SHARE
void free_frm_image(const uchar *frm);
void set_overlapped_keys();
+
+ bool old_long_hash_function() const
+ {
+ return mysql_version < 100428 ||
+ (mysql_version >= 100500 && mysql_version < 100519) ||
+ (mysql_version >= 100600 && mysql_version < 100612) ||
+ (mysql_version >= 100700 && mysql_version < 100708) ||
+ (mysql_version >= 100800 && mysql_version < 100807) ||
+ (mysql_version >= 100900 && mysql_version < 100905) ||
+ (mysql_version >= 101000 && mysql_version < 101003) ||
+ (mysql_version >= 101100 && mysql_version < 101102);
+ }
+ Item_func_hash *make_long_hash_func(THD *thd,
+ MEM_ROOT *mem_root,
+ List<Item> *field_list) const;
};
/* not NULL, but cannot be dereferenced */
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 219bf9fc9c7..db4a2a2e7b9 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -995,13 +995,19 @@ void wsrep_init_startup (bool sst_first)
With mysqldump SST (!sst_first) wait until the server reaches
joiner state and procedd to accepting connections.
*/
+ int err= 0;
if (sst_first)
{
- server_state.wait_until_state(Wsrep_server_state::s_initializing);
+ err= server_state.wait_until_state(Wsrep_server_state::s_initializing);
}
else
{
- server_state.wait_until_state(Wsrep_server_state::s_joiner);
+ err= server_state.wait_until_state(Wsrep_server_state::s_joiner);
+ }
+ if (err)
+ {
+ WSREP_ERROR("Wsrep startup was interrupted");
+ unireg_abort(1);
}
}
@@ -1107,7 +1113,11 @@ void wsrep_stop_replication(THD *thd)
{
WSREP_DEBUG("Disconnect provider");
Wsrep_server_state::instance().disconnect();
- Wsrep_server_state::instance().wait_until_state(Wsrep_server_state::s_disconnected);
+ if (Wsrep_server_state::instance().wait_until_state(
+ Wsrep_server_state::s_disconnected))
+ {
+ WSREP_WARN("Wsrep interrupted while waiting for disconnected state");
+ }
}
/* my connection, should not terminate with wsrep_close_client_connection(),
@@ -1129,7 +1139,11 @@ void wsrep_shutdown_replication()
{
WSREP_DEBUG("Disconnect provider");
Wsrep_server_state::instance().disconnect();
- Wsrep_server_state::instance().wait_until_state(Wsrep_server_state::s_disconnected);
+ if (Wsrep_server_state::instance().wait_until_state(
+ Wsrep_server_state::s_disconnected))
+ {
+ WSREP_WARN("Wsrep interrupted while waiting for disconnected state");
+ }
}
wsrep_close_client_connections(TRUE);
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index 09211a96e1e..d4035fcf363 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -336,9 +336,14 @@ static bool wsrep_sst_complete (THD* thd,
if ((state == Wsrep_server_state::s_joiner ||
state == Wsrep_server_state::s_initialized))
{
- Wsrep_server_state::instance().sst_received(client_service,
- rcode);
- WSREP_INFO("SST succeeded for position %s", start_pos_buf);
+ if (Wsrep_server_state::instance().sst_received(client_service, rcode))
+ {
+ failed= true;
+ }
+ else
+ {
+ WSREP_INFO("SST succeeded for position %s", start_pos_buf);
+ }
}
else
{
diff --git a/storage/connect/array.cpp b/storage/connect/array.cpp
index a5c2e065742..1eeb4ac05ca 100644
--- a/storage/connect/array.cpp
+++ b/storage/connect/array.cpp
@@ -975,13 +975,13 @@ PSZ ARRAY::MakeArrayList(PGLOBAL g)
xtrc(1, "Arraylist: len=%d\n", len);
p = (char *)PlugSubAlloc(g, NULL, len);
- strcpy(p, "(");
+ safe_strcpy(p, len, "(");
for (i = 0; i < Nval;) {
Value->SetValue_pvblk(Vblp, i);
Value->Prints(g, tp, z);
- strcat(p, tp);
- strcat(p, (++i == Nval) ? ")" : ",");
+ safe_strcat(p, len, tp);
+ safe_strcat(p, len, (++i == Nval) ? ")" : ",");
} // enfor i
xtrc(1, "Arraylist: newlen=%d\n", strlen(p));
diff --git a/storage/connect/bson.cpp b/storage/connect/bson.cpp
index 1eef3c063d6..6bea4eb5d85 100644
--- a/storage/connect/bson.cpp
+++ b/storage/connect/bson.cpp
@@ -10,6 +10,7 @@
/* Include relevant sections of the MariaDB header file. */
/***********************************************************************/
#include <my_global.h>
+#include <m_string.h>
/***********************************************************************/
/* Include application header files: */
@@ -598,7 +599,7 @@ PSZ BDOC::Serialize(PGLOBAL g, PBVAL bvp, char* fn, int pretty)
try {
if (!bvp) {
- strcpy(g->Message, "Null json tree");
+ safe_strcpy(g->Message, sizeof(g->Message), "Null json tree");
throw 1;
} else if (!fn) {
// Serialize to a string
@@ -606,9 +607,8 @@ PSZ BDOC::Serialize(PGLOBAL g, PBVAL bvp, char* fn, int pretty)
b = pretty == 1;
} else {
if (!(fs = fopen(fn, "wb"))) {
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
- "w", (int)errno, fn);
- strcat(strcat(g->Message, ": "), strerror(errno));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR) ": %s",
+ "w", (int)errno, fn, strerror(errno));
throw 2;
} else if (pretty >= 2) {
// Serialize to a pretty file
diff --git a/storage/connect/bsonudf.cpp b/storage/connect/bsonudf.cpp
index b67fb0232d5..439f5a16c70 100644
--- a/storage/connect/bsonudf.cpp
+++ b/storage/connect/bsonudf.cpp
@@ -4910,7 +4910,7 @@ char *bbin_make_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endfor i
if ((bsp = BbinAlloc(bnx.G, initid->max_length, arp))) {
- strcat(bsp->Msg, " array");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
// Keep result of constant function
g->Xchk = (initid->const_item) ? bsp : NULL;
@@ -5108,8 +5108,9 @@ char *bbin_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
PUSH_WARNING("Result truncated to json_grp_size values");
if (arp)
- if ((bsp = BbinAlloc(g, initid->max_length, arp)))
- strcat(bsp->Msg, " array");
+ if ((bsp = BbinAlloc(g, initid->max_length, arp))) {
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
+ }
if (!bsp) {
*res_length = 0;
@@ -5155,8 +5156,9 @@ char *bbin_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
PUSH_WARNING("Result truncated to json_grp_size values");
if (bop)
- if ((bsp = BbinAlloc(g, initid->max_length, bop)))
- strcat(bsp->Msg, " object");
+ if ((bsp = BbinAlloc(g, initid->max_length, bop))) {
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
+ }
if (!bsp) {
*res_length = 0;
@@ -5200,7 +5202,7 @@ char *bbin_make_object(UDF_INIT *initid, UDF_ARGS *args, char *result,
bnx.SetKeyValue(objp, bnx.MakeValue(args, i), bnx.MakeKey(args, i));
if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
// Keep result of constant function
g->Xchk = (initid->const_item) ? bsp : NULL;
@@ -5255,7 +5257,7 @@ char *bbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
bnx.SetKeyValue(objp, jvp, bnx.MakeKey(args, i));
if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
// Keep result of constant function
g->Xchk = (initid->const_item) ? bsp : NULL;
@@ -5314,7 +5316,7 @@ char *bbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
bnx.SetKeyValue(objp, bnx.MakeValue(args, i + 1), MakePSZ(g, args, i));
if ((bsp = BbinAlloc(bnx.G, initid->max_length, objp))) {
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
// Keep result of constant function
g->Xchk = (initid->const_item) ? bsp : NULL;
@@ -6077,7 +6079,7 @@ char *bbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
// pretty = pty;
if ((bsp = BbinAlloc(bnx.G, len, jsp))) {
- strcat(bsp->Msg, " file");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " file");
bsp->Filename = fn;
bsp->Pretty = pretty;
} else {
diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp
index 98efd635bf3..4649e36a69e 100644
--- a/storage/connect/filamdbf.cpp
+++ b/storage/connect/filamdbf.cpp
@@ -442,7 +442,7 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, PTOS topt, bool info)
hp->Headlen, hp->Filedate[0], hp->Filedate[1],
hp->Filedate[2]);
- strcat(g->Message, buf);
+ safe_strcat(g->Message, sizeof(g->Message), buf);
} // endif info
#endif // 0
diff --git a/storage/connect/filamfix.cpp b/storage/connect/filamfix.cpp
index 117a4e8dda6..3298a83394c 100644
--- a/storage/connect/filamfix.cpp
+++ b/storage/connect/filamfix.cpp
@@ -36,6 +36,8 @@
#include <fcntl.h>
#endif // !_WIN32
+#include <m_string.h>
+
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
@@ -881,7 +883,6 @@ bool BGXFAM::OpenTableFile(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)filename, sizeof(filename), NULL);
- strcat(g->Message, filename);
} else
rc = 0;
@@ -1002,7 +1003,7 @@ int BGXFAM::Cardinality(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)filename, sizeof(filename), NULL);
- strcat(g->Message, filename);
+ safe_strcat(g->Message, sizeof(g->Message), filename);
return -1;
} else
return 0; // File does not exist
@@ -1382,7 +1383,8 @@ bool BGXFAM::OpenTempFile(PGLOBAL g)
/*********************************************************************/
tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
PlugSetPath(tempname, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(tempname, tempname), ".t");
+ PlugRemoveType(tempname, tempname);
+ safe_strcat(tempname, _MAX_PATH, ".t");
remove(tempname); // Be sure it does not exist yet
#if defined(_WIN32)
@@ -1391,11 +1393,12 @@ bool BGXFAM::OpenTempFile(PGLOBAL g)
if (Tfile == INVALID_HANDLE_VALUE) {
DWORD rc = GetLastError();
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, MODE_INSERT,
+ tempname);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)tempname, _MAX_PATH, NULL);
- strcat(g->Message, tempname);
+ safe_strcat(g->Message, sizeof(g->Message), tempname);
return true;
} // endif Tfile
#else // UNIX
@@ -1403,8 +1406,8 @@ bool BGXFAM::OpenTempFile(PGLOBAL g)
if (Tfile == INVALID_HANDLE_VALUE) {
int rc = errno;
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
- strcat(g->Message, strerror(errno));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR)" %s", rc,
+ MODE_INSERT, tempname, strerror(errno));
return true;
} //endif Tfile
#endif // UNIX
diff --git a/storage/connect/filamgz.cpp b/storage/connect/filamgz.cpp
index d6c48cb25a9..0eaf3571a80 100644
--- a/storage/connect/filamgz.cpp
+++ b/storage/connect/filamgz.cpp
@@ -33,6 +33,8 @@
#include <fcntl.h>
#endif // !_WIN32
+#include <m_string.h>
+
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
@@ -128,12 +130,13 @@ int GZFAM::GetFileLength(PGLOBAL g)
/***********************************************************************/
bool GZFAM::OpenTableFile(PGLOBAL g)
{
- char opmode[4], filename[_MAX_PATH];
- MODE mode = Tdbp->GetMode();
+ const char *opmode;
+ char filename[_MAX_PATH];
+ MODE mode = Tdbp->GetMode();
switch (mode) {
case MODE_READ:
- strcpy(opmode, "r");
+ opmode = "rb";
break;
case MODE_UPDATE:
/*****************************************************************/
@@ -147,7 +150,7 @@ bool GZFAM::OpenTableFile(PGLOBAL g)
DelRows = Cardinality(g);
// This will erase the entire file
- strcpy(opmode, "w");
+ opmode = "wb";
// Block = 0; // For ZBKFAM
// Last = Nrec; // For ZBKFAM
Tdbp->ResetSize();
@@ -158,7 +161,7 @@ bool GZFAM::OpenTableFile(PGLOBAL g)
break;
case MODE_INSERT:
- strcpy(opmode, "a+");
+ opmode = "a+b";
break;
default:
snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
@@ -170,13 +173,11 @@ bool GZFAM::OpenTableFile(PGLOBAL g)
/* Use specific zlib functions. */
/* Treat files as binary. */
/*********************************************************************/
- strcat(opmode, "b");
Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode);
if (Zfile == NULL) {
- snprintf(g->Message, sizeof(g->Message), MSG(GZOPEN_ERROR),
- opmode, (int)errno, filename);
- strcat(strcat(g->Message, ": "), strerror(errno));
+ snprintf(g->Message, sizeof(g->Message), MSG(GZOPEN_ERROR) ": %s",
+ opmode, (int)errno, filename, strerror(errno));
return (mode == MODE_READ && errno == ENOENT)
? PushWarning(g, Tdbp) : true;
} // endif Zfile
diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp
index 7a5d16accb2..f8168887e89 100644
--- a/storage/connect/filamtxt.cpp
+++ b/storage/connect/filamtxt.cpp
@@ -38,6 +38,8 @@
#include <fcntl.h>
#endif // !_WIN32
+#include <m_string.h>
+
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
@@ -593,7 +595,7 @@ bool DOSFAM::OpenTableFile(PGLOBAL g)
} // endswitch Mode
// For blocked I/O or for moving lines, open the table in binary
- strcat(opmode, (Bin) ? "b" : "t");
+ safe_strcat(opmode, sizeof(opmode), (Bin) ? "b" : "t");
// Now open the file stream
PlugSetPath(filename, To_File, Tdbp->GetPath());
@@ -1081,7 +1083,8 @@ bool DOSFAM::OpenTempFile(PGLOBAL g)
/* Open the temporary file, Spos is at the beginning of file. */
/*********************************************************************/
PlugSetPath(tempname, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(tempname, tempname), ".t");
+ PlugRemoveType(tempname, tempname);
+ safe_strcat(tempname, sizeof(tempname), ".t");
if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
if (trace(1))
@@ -1170,7 +1173,8 @@ int DOSFAM::RenameTempFile(PGLOBAL g)
if (!Abort) {
PlugSetPath(filename, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(filetemp, filename), ".ttt");
+ PlugRemoveType(filetemp, filename);
+ safe_strcat(filetemp, sizeof(filetemp), ".ttt");
remove(filetemp); // May still be there from previous error
if (rename(filename, filetemp)) { // Save file for security
diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp
index 76e9336dedf..184df646cb7 100644
--- a/storage/connect/filamvct.cpp
+++ b/storage/connect/filamvct.cpp
@@ -42,6 +42,8 @@
#include <fcntl.h>
#endif // !_WIN32
+#include <m_string.h>
+
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
@@ -194,7 +196,7 @@ int VCTFAM::GetBlockInfo(PGLOBAL g)
if (Header == 2)
{
PlugRemoveType(filename, filename);
- strncat(filename, ".blk", _MAX_PATH - strlen(filename));
+ safe_strcat(filename, sizeof(filename), ".blk");
}
if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
@@ -251,7 +253,7 @@ bool VCTFAM::SetBlockInfo(PGLOBAL g)
} else { // Header == 2
PlugRemoveType(filename, filename);
- strncat(filename, ".blk", _MAX_PATH - strlen(filename));
+ safe_strcat(filename, sizeof(filename), ".blk");
s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
} // endif Header
@@ -587,7 +589,7 @@ bool VCTFAM::InitInsert(PGLOBAL g)
htrc("Exception %d: %s\n", n, g->Message);
rc = true;
} catch (const char *msg) {
- strncpy(g->Message, msg, sizeof(g->Message));
+ safe_strcpy(g->Message, sizeof(msg), msg);
rc = true;
} // end catch
@@ -891,8 +893,7 @@ bool VCTFAM::OpenTempFile(PGLOBAL g)
/*********************************************************************/
PlugSetPath(tempname, To_File, Tdbp->GetPath());
PlugRemoveType(tempname, tempname);
- strncat(tempname, ".t", _MAX_PATH - strlen(tempname));
-
+ safe_strcat(tempname, sizeof(tempname), ".t");
if (MaxBlk) {
if (MakeEmptyFile(g, tempname))
return true;
@@ -1562,7 +1563,7 @@ bool VCMFAM::InitInsert(PGLOBAL g)
htrc("Exception %d: %s\n", n, g->Message);
rc = true;
} catch (const char *msg) {
- strncpy(g->Message, msg, sizeof(g->Message));
+ safe_strcpy(g->Message, sizeof(g->Message), msg);
rc = true;
} // end catch
@@ -2082,10 +2083,10 @@ bool VECFAM::AllocateBuffer(PGLOBAL g)
// Allocate all that is needed to move lines and make Temp
if (UseTemp) {
Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
- strcpy(Tempat, Colfn);
+ safe_strcpy(Tempat, _MAX_PATH, Colfn);
PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
PlugRemoveType(Tempat, Tempat);
- strncat(Tempat, ".t", _MAX_PATH - strlen(Tempat));
+ safe_strcat(Tempat, _MAX_PATH, ".t");
T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
} // endif UseTemp
@@ -2460,7 +2461,7 @@ int VECFAM::RenameTempFile(PGLOBAL g)
snprintf(filename, _MAX_PATH, Colfn, i+1);
PlugSetPath(filename, filename, Tdbp->GetPath());
PlugRemoveType(filetemp, filename);
- strncat(filetemp, ".ttt", _MAX_PATH - strlen(filetemp));
+ safe_strcat(filetemp, sizeof(filetemp), ".ttt");
remove(filetemp); // May still be there from previous error
if (rename(filename, filetemp)) { // Save file for security
@@ -3221,7 +3222,7 @@ int BGVFAM::GetBlockInfo(PGLOBAL g)
if (Header == 2)
{
PlugRemoveType(filename, filename);
- strncat(filename, ".blk", _MAX_PATH - strlen(filename));
+ safe_strcat(filename, sizeof(filename), ".blk");
}
#if defined(_WIN32)
@@ -3300,7 +3301,7 @@ bool BGVFAM::SetBlockInfo(PGLOBAL g)
} else // Header == 2
{
PlugRemoveType(filename, filename);
- strncat(filename, ".blk", _MAX_PATH - strlen(filename));
+ safe_strcat(filename, sizeof(filename), ".blk");
}
if (h == INVALID_HANDLE_VALUE) {
@@ -3398,7 +3399,7 @@ bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)filename, sizeof(filename), NULL);
- strncat(g->Message, filename, sizeof(g->Message) - strlen(g->Message));
+ safe_strcat(g->Message, sizeof(g->Message), filename);
if (h != INVALID_HANDLE_VALUE)
CloseHandle(h);
@@ -3534,7 +3535,7 @@ bool BGVFAM::OpenTableFile(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)filename, sizeof(filename), NULL);
- strncat(g->Message, filename, sizeof(g->Message) - strlen(g->Message));
+ safe_strcat(g->Message, sizeof(g->Message), filename);
} // endif Hfile
if (trace(1))
@@ -3622,8 +3623,8 @@ bool BGVFAM::OpenTableFile(PGLOBAL g)
if (Hfile == INVALID_HANDLE_VALUE) {
rc = errno;
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, mode, filename);
- strncat(g->Message, strerror(errno), sizeof(g->Message) - strlen(g->Message));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR)"%s", rc, mode,
+ filename, strerror(errno));
} // endif Hfile
if (trace(1))
@@ -3967,7 +3968,7 @@ bool BGVFAM::OpenTempFile(PGLOBAL g)
tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
PlugSetPath(tempname, To_File, Tdbp->GetPath());
PlugRemoveType(tempname, tempname);
- strncat(tempname, ".t", _MAX_PATH - strlen(tempname));
+ safe_strcat(tempname, _MAX_PATH, ".t");
if (!MaxBlk)
remove(tempname); // Be sure it does not exist yet
@@ -3986,7 +3987,7 @@ bool BGVFAM::OpenTempFile(PGLOBAL g)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)tempname, _MAX_PATH, NULL);
- strncat(g->Message, tempname, sizeof(g->Message) - strlen(g->Message));
+ safe_strcat(g->Message, sizeof(g->Message), tempname);
return true;
} // endif Tfile
#else // UNIX
@@ -3996,8 +3997,8 @@ bool BGVFAM::OpenTempFile(PGLOBAL g)
if (Tfile == INVALID_HANDLE_VALUE) {
int rc = errno;
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
- strncat(g->Message, strerror(errno), sizeof(g->Message) - strlen(g->Message));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR) "%s", rc, MODE_INSERT,
+ tempname, strerror(errno));
return true;
} //endif Tfile
#endif // UNIX
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index f0ee947bc23..5a1e40cccb2 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -29,6 +29,7 @@
#include <fcntl.h>
#endif // !_WIN32
#include <time.h>
+#include <m_string.h>
/***********************************************************************/
/* Include application header files: */
@@ -181,7 +182,8 @@ static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, PCSZ pat, char *buf)
while (true) {
if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
- strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName);
+ snprintf(filename, sizeof(filename), "%s%s%s",
+ drive, direc, FileData.cFileName);
if (ZipFile(g, zutp, filename, FileData.cFileName, buf)) {
FindClose(hSearch);
@@ -217,7 +219,7 @@ static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, PCSZ pat, char *buf)
struct dirent *entry;
_splitpath(filename, NULL, direc, pattern, ftype);
- strcat(pattern, ftype);
+ safe_strcat(pattern, sizeof(pattern), ftype);
// Start searching files in the target directory.
if (!(dir = opendir(direc))) {
@@ -226,7 +228,7 @@ static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, PCSZ pat, char *buf)
} // endif dir
while ((entry = readdir(dir))) {
- strcat(strcpy(fn, direc), entry->d_name);
+ snprintf(fn, sizeof(fn), "%s%s", direc, entry->d_name);
if (lstat(fn, &fileinfo) < 0) {
snprintf(g->Message, sizeof(g->Message), "%s: %s", fn, strerror(errno));
@@ -240,7 +242,7 @@ static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, PCSZ pat, char *buf)
if (fnmatch(pattern, entry->d_name, 0))
continue; // Not a match
- strcat(strcpy(filename, direc), entry->d_name);
+ snprintf(filename, sizeof(filename), "%s%s", direc, entry->d_name);
if (ZipFile(g, zutp, filename, entry->d_name, buf)) {
closedir(dir);
diff --git a/storage/connect/javaconn.cpp b/storage/connect/javaconn.cpp
index bf104402628..efde2e86330 100644
--- a/storage/connect/javaconn.cpp
+++ b/storage/connect/javaconn.cpp
@@ -33,6 +33,8 @@
#define NODW
#endif // !_WIN32
+#include <m_string.h>
+
/***********************************************************************/
/* Required objects includes. */
/***********************************************************************/
@@ -231,15 +233,16 @@ bool JAVAConn::GetJVM(PGLOBAL g)
#if defined(_WIN32)
for (ntry = 0; !LibJvm && ntry < 3; ntry++) {
if (!ntry && JvmPath) {
- strcat(strcpy(soname, JvmPath), "\\jvm.dll");
+ snprintf(soname, sizeof(soname), "%s\\jvm.dll", JvmPath);
+
ntry = 3; // No other try
} else if (ntry < 2 && getenv("JAVA_HOME")) {
- strcpy(soname, getenv("JAVA_HOME"));
+ safe_strcpy(soname, sizeof(soname), getenv("JAVA_HOME"));
if (ntry == 1)
- strcat(soname, "\\jre");
+ safe_strcat(soname, sizeof(soname), "\\jre");
- strcat(soname, "\\bin\\client\\jvm.dll");
+ safe_strcat(soname, sizeof(soname), "\\bin\\client\\jvm.dll");
} else {
// Try to find it through the registry
char version[16];
@@ -247,11 +250,12 @@ bool JAVAConn::GetJVM(PGLOBAL g)
LONG rc;
DWORD BufferSize = 16;
- strcpy(soname, "jvm.dll"); // In case it fails
+ safe_strcpy(soname, sizeof(soname), "jvm.dll"); // In case it fails
if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "CurrentVersion",
RRF_RT_ANY, NULL, (PVOID)&version, &BufferSize)) == ERROR_SUCCESS) {
- strcat(strcat(javaKey, "\\"), version);
+ safe_strcat(javaKey, sizeof(javaKey), "\\");
+ safe_strcat(javaKey, sizeof(javaKey), version);
BufferSize = sizeof(soname);
if ((rc = RegGetValue(HKEY_LOCAL_MACHINE, javaKey, "RuntimeLib",
@@ -272,11 +276,11 @@ bool JAVAConn::GetJVM(PGLOBAL g)
char buf[256];
DWORD rc = GetLastError();
- snprintf(g->Message, sizeof(g->Message), MSG(DLL_LOAD_ERROR), rc, soname);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
(LPTSTR)buf, sizeof(buf), NULL);
- strcat(strcat(g->Message, ": "), buf);
+ snprintf(g->Message, sizeof(g->Message), MSG(DLL_LOAD_ERROR)": %s", rc,
+ soname, buf);
} else if (!(CreateJavaVM = (CRTJVM)GetProcAddress((HINSTANCE)LibJvm,
"JNI_CreateJavaVM"))) {
snprintf(g->Message, sizeof(g->Message), MSG(PROCADD_ERROR), GetLastError(), "JNI_CreateJavaVM");
@@ -301,13 +305,14 @@ bool JAVAConn::GetJVM(PGLOBAL g)
for (ntry = 0; !LibJvm && ntry < 2; ntry++) {
if (!ntry && JvmPath) {
- strcat(strcpy(soname, JvmPath), "/libjvm.so");
+ snprintf(soname, sizeof(soname), "%s/libjvm.so", JvmPath);
ntry = 2;
} else if (!ntry && getenv("JAVA_HOME")) {
// TODO: Replace i386 by a better guess
- strcat(strcpy(soname, getenv("JAVA_HOME")), "/jre/lib/i386/client/libjvm.so");
+ snprintf(soname, sizeof(soname), "%s/jre/lib/i386/client/libjvm.so",
+ getenv("JAVA_HOME"));
} else { // Will need LD_LIBRARY_PATH to be set
- strcpy(soname, "libjvm.so");
+ safe_strcpy(soname, sizeof(soname), "libjvm.so");
ntry = 2;
} // endelse
diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp
index 4078a03830a..a46d6001434 100644
--- a/storage/connect/json.cpp
+++ b/storage/connect/json.cpp
@@ -10,6 +10,7 @@
/* Include relevant sections of the MariaDB header file. */
/***********************************************************************/
#include <my_global.h>
+#include <m_string.h>
/***********************************************************************/
/* Include application header files: */
@@ -270,7 +271,7 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, char* fn, int pretty) {
jdp->dfp = GetDefaultPrec();
if (!jsp) {
- strcpy(g->Message, "Null json tree");
+ safe_strcpy(g->Message, sizeof(g->Message), "Null json tree");
throw 1;
} else if (!fn) {
// Serialize to a string
@@ -278,9 +279,8 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, char* fn, int pretty) {
b = pretty == 1;
} else {
if (!(fs = fopen(fn, "wb"))) {
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
- "w", (int)errno, fn);
- strcat(strcat(g->Message, ": "), strerror(errno));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR) ": %s",
+ "w", (int)errno, fn, strerror(errno));
throw 2;
} else if (pretty >= 2) {
// Serialize to a pretty file
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp
index 0baaa70b496..f514e9ee806 100644
--- a/storage/connect/jsonudf.cpp
+++ b/storage/connect/jsonudf.cpp
@@ -4755,7 +4755,7 @@ char *jbin_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
if ((arp = (PJAR)JsonNew(g, TYPE_JAR)) &&
(bsp = JbinAlloc(g, args, initid->max_length, arp))) {
- strcat(bsp->Msg, " array");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
for (uint i = 0; i < args->arg_count; i++)
arp->AddArrayValue(g, MakeValue(g, args, i));
@@ -4832,7 +4832,7 @@ char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
arp->InitArray(gb);
if ((bsp = JbinAlloc(g, args, initid->max_length, top))) {
- strcat(bsp->Msg, " array");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
bsp->Jsp = arp;
} // endif bsp
@@ -5053,7 +5053,7 @@ char *jbin_object(UDF_INIT *initid, UDF_ARGS *args, char *result,
if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
} else
bsp = NULL;
@@ -5109,7 +5109,7 @@ char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
objp->SetKeyValue(g, jvp, MakeKey(g, args, i));
if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
} else
bsp = NULL;
@@ -5168,7 +5168,7 @@ char *jbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
objp->SetKeyValue(g, MakeValue(g, args, i + 1), MakePSZ(g, args, i));
if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
- strcat(bsp->Msg, " object");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " object");
} else
bsp = NULL;
@@ -5390,7 +5390,7 @@ char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endif CheckMemory
if ((bsp = JbinAlloc(g, args, initid->max_length, jarp)))
- strcat(bsp->Msg, " array");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " array");
// Keep result of constant function
g->Xchk = (initid->const_item) ? bsp : NULL;
@@ -5465,7 +5465,7 @@ char *jbin_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
jsp = (jvp->GetJsp()) ? jvp->GetJsp() : JvalNew(g, TYPE_JVAL, jvp->GetValue(g));
if ((bsp = JbinAlloc(g, args, initid->max_length, jsp)))
- strcat(bsp->Msg, " item");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " item");
else
*error = 1;
@@ -5825,7 +5825,7 @@ char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
pretty = pty;
if ((bsp = JbinAlloc(g, args, len, jsp))) {
- strcat(bsp->Msg, " file");
+ safe_strcat(bsp->Msg, sizeof(bsp->Msg), " file");
bsp->Filename = fn;
bsp->Pretty = pretty;
} else {
@@ -6161,9 +6161,8 @@ char* JUP::UnprettyJsonFile(PGLOBAL g, char *fn, char *outfn, int lrecl) {
/* Parse the json file and allocate its tree structure. */
/*********************************************************************************/
if (!(fs = fopen(outfn, "wb"))) {
- snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
- "w", (int)errno, outfn);
- strcat(strcat(g->Message, ": "), strerror(errno));
+ snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR)": %s",
+ "w", (int)errno, outfn, strerror(errno));
CloseMemMap(mm.memory, len);
return NULL;
} // endif fs
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 579ca07616e..ba493f5bbb9 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -405,18 +405,20 @@ PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
port = mysqld_port;
if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) {
- query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 10);
+ size_t query_sz = strlen(srcdef) + 10;
+ query = (char *)PlugSubAlloc(g, NULL, query_sz);
if ((p= strstr(srcdef, "%s")))
{
/* Replace %s with 1=1 */
- sprintf(query, "%.*s1=1%s", (int) (p - srcdef), srcdef, p + 2); // dummy where clause
+ snprintf(query, query_sz, "%.*s1=1%s",
+ (int) (p - srcdef), srcdef, p + 2); // dummy where clause
}
- else
- strcpy(query, srcdef);
+ else
+ safe_strcpy(query, query_sz, srcdef);
if (!strnicmp(srcdef, "select ", 7))
- strcat(query, " LIMIT 0");
+ safe_strcat(query, query_sz, " LIMIT 0");
} else
query = (char *)srcdef;
diff --git a/storage/mroonga/vendor/groonga/lib/alloc.c b/storage/mroonga/vendor/groonga/lib/alloc.c
index 94e31901cec..adb35e04c45 100644
--- a/storage/mroonga/vendor/groonga/lib/alloc.c
+++ b/storage/mroonga/vendor/groonga/lib/alloc.c
@@ -828,7 +828,7 @@ grn_free_default(grn_ctx *ctx, void *ptr,
if (ptr) {
GRN_ADD_ALLOC_COUNT(-1);
} else {
- GRN_LOG(ctx, GRN_LOG_ALERT, "free fail (%s:%d) <%d>",
+ GRN_LOG(ctx, GRN_LOG_ALERT, "free fail (nullptr) (%s:%d) <%d>",
file, line, alloc_count);
}
}
diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_26541.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_26541.result
new file mode 100644
index 00000000000..b2edaff6918
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_26541.result
@@ -0,0 +1,20 @@
+#
+# MDEV-26541 Undefined symbol: _ZTI12ha_partition when attempting to use ha_spider.so in UBSAN builds
+#
+INSTALL PLUGIN spider SONAME 'ha_spider.so';
+DROP FUNCTION spider_flush_table_mon_cache;
+DROP FUNCTION spider_copy_tables;
+DROP FUNCTION spider_ping_table;
+DROP FUNCTION spider_bg_direct_sql;
+DROP FUNCTION spider_direct_sql;
+UNINSTALL PLUGIN spider_alloc_mem;
+UNINSTALL PLUGIN spider;
+DROP TABLE IF EXISTS mysql.spider_xa;
+DROP TABLE IF EXISTS mysql.spider_xa_member;
+DROP TABLE IF EXISTS mysql.spider_xa_failed_log;
+DROP TABLE IF EXISTS mysql.spider_tables;
+DROP TABLE IF EXISTS mysql.spider_link_mon_servers;
+DROP TABLE IF EXISTS mysql.spider_link_failed_log;
+DROP TABLE IF EXISTS mysql.spider_table_position_for_recovery;
+DROP TABLE IF EXISTS mysql.spider_table_sts;
+DROP TABLE IF EXISTS mysql.spider_table_crd;
diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_30370.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_30370.result
new file mode 100644
index 00000000000..df0f6949280
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_30370.result
@@ -0,0 +1,3 @@
+#
+# MDEV-30370 mariadbd hangs when running with --wsrep-recover and --plugin-load-add=ha_spider.so
+#
diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_26541.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_26541.test
new file mode 100644
index 00000000000..ffd99390748
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_26541.test
@@ -0,0 +1,40 @@
+--echo #
+--echo # MDEV-26541 Undefined symbol: _ZTI12ha_partition when attempting to use ha_spider.so in UBSAN builds
+--echo #
+
+if (`select not(count(*)) from information_schema.system_variables where variable_name='have_sanitizer' and global_value="UBSAN"`)
+{
+--skip test needs to be run with UBSAN
+}
+
+# init spider
+
+INSTALL PLUGIN spider SONAME 'ha_spider.so';
+
+let $PLUGIN_NAME= spider_flush_table_mon_cache;
+let $PLUGIN_EXIST=
+ `SELECT COUNT(*) FROM mysql.func WHERE name = '$PLUGIN_NAME'`;
+while (!$PLUGIN_EXIST)
+{
+ let $PLUGIN_EXIST=
+ `SELECT COUNT(*) FROM mysql.func WHERE name = '$PLUGIN_NAME'`;
+}
+
+# deinit spider
+
+DROP FUNCTION spider_flush_table_mon_cache;
+DROP FUNCTION spider_copy_tables;
+DROP FUNCTION spider_ping_table;
+DROP FUNCTION spider_bg_direct_sql;
+DROP FUNCTION spider_direct_sql;
+UNINSTALL PLUGIN spider_alloc_mem;
+UNINSTALL PLUGIN spider;
+DROP TABLE IF EXISTS mysql.spider_xa;
+DROP TABLE IF EXISTS mysql.spider_xa_member;
+DROP TABLE IF EXISTS mysql.spider_xa_failed_log;
+DROP TABLE IF EXISTS mysql.spider_tables;
+DROP TABLE IF EXISTS mysql.spider_link_mon_servers;
+DROP TABLE IF EXISTS mysql.spider_link_failed_log;
+DROP TABLE IF EXISTS mysql.spider_table_position_for_recovery;
+DROP TABLE IF EXISTS mysql.spider_table_sts;
+DROP TABLE IF EXISTS mysql.spider_table_crd;
diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test
new file mode 100644
index 00000000000..788ea2323f7
--- /dev/null
+++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_30370.test
@@ -0,0 +1,5 @@
+--echo #
+--echo # MDEV-30370 mariadbd hangs when running with --wsrep-recover and --plugin-load-add=ha_spider.so
+--echo #
+
+--exec $MYSQLD_BOOTSTRAP_CMD --wsrep-recover --plugin-load-add=ha_spider.so
diff --git a/storage/spider/spd_conn.cc b/storage/spider/spd_conn.cc
index d98f688fd39..ef10a56d583 100644
--- a/storage/spider/spd_conn.cc
+++ b/storage/spider/spd_conn.cc
@@ -637,18 +637,25 @@ SPIDER_CONN *spider_create_conn(
conn->tgt_host = tmp_host;
memcpy(conn->tgt_host, share->tgt_hosts[link_idx],
share->tgt_hosts_lengths[link_idx]);
+
conn->tgt_username_length = share->tgt_usernames_lengths[link_idx];
conn->tgt_username = tmp_username;
- memcpy(conn->tgt_username, share->tgt_usernames[link_idx],
- share->tgt_usernames_lengths[link_idx]);
+ if (conn->tgt_username_length)
+ memcpy(conn->tgt_username, share->tgt_usernames[link_idx],
+ share->tgt_usernames_lengths[link_idx]);
+
conn->tgt_password_length = share->tgt_passwords_lengths[link_idx];
conn->tgt_password = tmp_password;
- memcpy(conn->tgt_password, share->tgt_passwords[link_idx],
- share->tgt_passwords_lengths[link_idx]);
+ if (conn->tgt_password_length)
+ memcpy(conn->tgt_password, share->tgt_passwords[link_idx],
+ share->tgt_passwords_lengths[link_idx]);
+
conn->tgt_socket_length = share->tgt_sockets_lengths[link_idx];
conn->tgt_socket = tmp_socket;
- memcpy(conn->tgt_socket, share->tgt_sockets[link_idx],
- share->tgt_sockets_lengths[link_idx]);
+ if (conn->tgt_socket_length)
+ memcpy(conn->tgt_socket, share->tgt_sockets[link_idx],
+ share->tgt_sockets_lengths[link_idx]);
+
conn->tgt_wrapper_length = share->tgt_wrappers_lengths[link_idx];
conn->tgt_wrapper = tmp_wrapper;
memcpy(conn->tgt_wrapper, share->tgt_wrappers[link_idx],
diff --git a/storage/spider/spd_table.cc b/storage/spider/spd_table.cc
index 7b07e84cd1f..349e34f6632 100644
--- a/storage/spider/spd_table.cc
+++ b/storage/spider/spd_table.cc
@@ -10102,6 +10102,7 @@ void spider_free_sts_threads(
) {
bool thread_killed;
DBUG_ENTER("spider_free_sts_threads");
+ spider_thread->init_command = FALSE;
pthread_mutex_lock(&spider_thread->mutex);
thread_killed = spider_thread->killed;
spider_thread->killed = TRUE;
@@ -10258,12 +10259,13 @@ void *spider_table_bg_sts_action(
tmp_disable_binlog(thd);
thd->security_ctx->skip_grants();
thd->client_capabilities |= CLIENT_MULTI_RESULTS;
- if (!(*spd_mysqld_server_started) && !thd->killed)
+ if (!(*spd_mysqld_server_started) && !thd->killed && !thread->killed)
{
pthread_mutex_lock(spd_LOCK_server_started);
thd->mysys_var->current_cond = spd_COND_server_started;
thd->mysys_var->current_mutex = spd_LOCK_server_started;
- if (!(*spd_mysqld_server_started) && !thd->killed)
+ if (!(*spd_mysqld_server_started) && !thd->killed && !thread->killed &&
+ thread->init_command)
{
do
{
@@ -10273,14 +10275,16 @@ void *spider_table_bg_sts_action(
spd_LOCK_server_started, &abstime);
} while (
(error_num == ETIMEDOUT || error_num == ETIME) &&
- !(*spd_mysqld_server_started) && !thd->killed && !thread->killed
+ !(*spd_mysqld_server_started) && !thd->killed && !thread->killed &&
+ thread->init_command
);
}
pthread_mutex_unlock(spd_LOCK_server_started);
thd->mysys_var->current_cond = &thread->cond;
thd->mysys_var->current_mutex = &thread->mutex;
}
- while (spider_init_queries[i].length && !thd->killed && !thread->killed)
+ while (spider_init_queries[i].length && !thd->killed && !thread->killed &&
+ thread->init_command)
{
dispatch_command(COM_QUERY, thd, spider_init_queries[i].str,
(uint) spider_init_queries[i].length, FALSE, FALSE);
diff --git a/storage/spider/spd_trx.cc b/storage/spider/spd_trx.cc
index 316fe23f40e..b00a7262230 100644
--- a/storage/spider/spd_trx.cc
+++ b/storage/spider/spd_trx.cc
@@ -627,103 +627,136 @@ int spider_create_trx_alter_table(
alter_table->tmp_tgt_dsns_lengths = tmp_tgt_dsns_lengths;
alter_table->tmp_static_link_ids_lengths = tmp_static_link_ids_lengths;
+ size_t len;
for(roop_count = 0; roop_count < (int) share->all_link_count; roop_count++)
{
- tmp_server_names[roop_count] = tmp_server_names_char;
- memcpy(tmp_server_names_char,
- share_alter->tmp_server_names[roop_count],
- sizeof(char) * share_alter->tmp_server_names_lengths[roop_count]);
- tmp_server_names_char +=
- share_alter->tmp_server_names_lengths[roop_count] + 1;
-
- tmp_tgt_table_names[roop_count] = tmp_tgt_table_names_char;
- memcpy(tmp_tgt_table_names_char,
- share_alter->tmp_tgt_table_names[roop_count],
- sizeof(char) * share_alter->tmp_tgt_table_names_lengths[roop_count]);
- tmp_tgt_table_names_char +=
- share_alter->tmp_tgt_table_names_lengths[roop_count] + 1;
-
- tmp_tgt_dbs[roop_count] = tmp_tgt_dbs_char;
- memcpy(tmp_tgt_dbs_char, share_alter->tmp_tgt_dbs[roop_count],
- sizeof(char) * share_alter->tmp_tgt_dbs_lengths[roop_count]);
- tmp_tgt_dbs_char +=
- share_alter->tmp_tgt_dbs_lengths[roop_count] + 1;
-
- tmp_tgt_hosts[roop_count] = tmp_tgt_hosts_char;
- memcpy(tmp_tgt_hosts_char, share_alter->tmp_tgt_hosts[roop_count],
- sizeof(char) * share_alter->tmp_tgt_hosts_lengths[roop_count]);
- tmp_tgt_hosts_char +=
- share_alter->tmp_tgt_hosts_lengths[roop_count] + 1;
-
- tmp_tgt_usernames[roop_count] = tmp_tgt_usernames_char;
- memcpy(tmp_tgt_usernames_char, share_alter->tmp_tgt_usernames[roop_count],
- sizeof(char) * share_alter->tmp_tgt_usernames_lengths[roop_count]);
- tmp_tgt_usernames_char +=
- share_alter->tmp_tgt_usernames_lengths[roop_count] + 1;
-
- tmp_tgt_passwords[roop_count] = tmp_tgt_passwords_char;
- memcpy(tmp_tgt_passwords_char, share_alter->tmp_tgt_passwords[roop_count],
- sizeof(char) * share_alter->tmp_tgt_passwords_lengths[roop_count]);
- tmp_tgt_passwords_char +=
- share_alter->tmp_tgt_passwords_lengths[roop_count] + 1;
-
- tmp_tgt_sockets[roop_count] = tmp_tgt_sockets_char;
- memcpy(tmp_tgt_sockets_char, share_alter->tmp_tgt_sockets[roop_count],
- sizeof(char) * share_alter->tmp_tgt_sockets_lengths[roop_count]);
- tmp_tgt_sockets_char +=
- share_alter->tmp_tgt_sockets_lengths[roop_count] + 1;
-
- tmp_tgt_wrappers[roop_count] = tmp_tgt_wrappers_char;
- memcpy(tmp_tgt_wrappers_char, share_alter->tmp_tgt_wrappers[roop_count],
- sizeof(char) * share_alter->tmp_tgt_wrappers_lengths[roop_count]);
- tmp_tgt_wrappers_char +=
- share_alter->tmp_tgt_wrappers_lengths[roop_count] + 1;
-
- tmp_tgt_ssl_cas[roop_count] = tmp_tgt_ssl_cas_char;
- memcpy(tmp_tgt_ssl_cas_char, share_alter->tmp_tgt_ssl_cas[roop_count],
- sizeof(char) * share_alter->tmp_tgt_ssl_cas_lengths[roop_count]);
- tmp_tgt_ssl_cas_char +=
- share_alter->tmp_tgt_ssl_cas_lengths[roop_count] + 1;
-
- tmp_tgt_ssl_capaths[roop_count] = tmp_tgt_ssl_capaths_char;
- memcpy(tmp_tgt_ssl_capaths_char,
- share_alter->tmp_tgt_ssl_capaths[roop_count],
- sizeof(char) * share_alter->tmp_tgt_ssl_capaths_lengths[roop_count]);
- tmp_tgt_ssl_capaths_char +=
- share_alter->tmp_tgt_ssl_capaths_lengths[roop_count] + 1;
-
- tmp_tgt_ssl_certs[roop_count] = tmp_tgt_ssl_certs_char;
- memcpy(tmp_tgt_ssl_certs_char, share_alter->tmp_tgt_ssl_certs[roop_count],
- sizeof(char) * share_alter->tmp_tgt_ssl_certs_lengths[roop_count]);
- tmp_tgt_ssl_certs_char +=
- share_alter->tmp_tgt_ssl_certs_lengths[roop_count] + 1;
-
- tmp_tgt_ssl_ciphers[roop_count] = tmp_tgt_ssl_ciphers_char;
- memcpy(tmp_tgt_ssl_ciphers_char,
- share_alter->tmp_tgt_ssl_ciphers[roop_count],
- sizeof(char) * share_alter->tmp_tgt_ssl_ciphers_lengths[roop_count]);
- tmp_tgt_ssl_ciphers_char +=
- share_alter->tmp_tgt_ssl_ciphers_lengths[roop_count] + 1;
-
- tmp_tgt_ssl_keys[roop_count] = tmp_tgt_ssl_keys_char;
- memcpy(tmp_tgt_ssl_keys_char, share_alter->tmp_tgt_ssl_keys[roop_count],
- sizeof(char) * share_alter->tmp_tgt_ssl_keys_lengths[roop_count]);
- tmp_tgt_ssl_keys_char +=
- share_alter->tmp_tgt_ssl_keys_lengths[roop_count] + 1;
-
- tmp_tgt_default_files[roop_count] = tmp_tgt_default_files_char;
- memcpy(tmp_tgt_default_files_char,
- share_alter->tmp_tgt_default_files[roop_count],
- sizeof(char) * share_alter->tmp_tgt_default_files_lengths[roop_count]);
- tmp_tgt_default_files_char +=
- share_alter->tmp_tgt_default_files_lengths[roop_count] + 1;
-
- tmp_tgt_default_groups[roop_count] = tmp_tgt_default_groups_char;
- memcpy(tmp_tgt_default_groups_char,
- share_alter->tmp_tgt_default_groups[roop_count],
- sizeof(char) * share_alter->tmp_tgt_default_groups_lengths[roop_count]);
- tmp_tgt_default_groups_char +=
- share_alter->tmp_tgt_default_groups_lengths[roop_count] + 1;
+ if ((len=
+ sizeof(char) * share_alter->tmp_server_names_lengths[roop_count]))
+ {
+ tmp_server_names[roop_count]= tmp_server_names_char;
+ memcpy(tmp_server_names_char, share_alter->tmp_server_names[roop_count],
+ len);
+ tmp_server_names_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_table_names_lengths[roop_count]))
+ {
+ tmp_tgt_table_names[roop_count]= tmp_tgt_table_names_char;
+ memcpy(tmp_tgt_table_names_char,
+ share_alter->tmp_tgt_table_names[roop_count], len);
+ tmp_tgt_table_names_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) * share_alter->tmp_tgt_dbs_lengths[roop_count]))
+ {
+ tmp_tgt_dbs[roop_count]= tmp_tgt_dbs_char;
+ memcpy(tmp_tgt_dbs_char, share_alter->tmp_tgt_dbs[roop_count], len);
+ tmp_tgt_dbs_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) * share_alter->tmp_tgt_hosts_lengths[roop_count]))
+ {
+ tmp_tgt_hosts[roop_count]= tmp_tgt_hosts_char;
+ memcpy(tmp_tgt_hosts_char, share_alter->tmp_tgt_hosts[roop_count], len);
+ tmp_tgt_hosts_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_usernames_lengths[roop_count]))
+ {
+ tmp_tgt_usernames[roop_count]= tmp_tgt_usernames_char;
+ memcpy(tmp_tgt_usernames_char,
+ share_alter->tmp_tgt_usernames[roop_count], len);
+ tmp_tgt_usernames_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_passwords_lengths[roop_count]))
+ {
+ tmp_tgt_passwords[roop_count]= tmp_tgt_passwords_char;
+ memcpy(tmp_tgt_passwords_char,
+ share_alter->tmp_tgt_passwords[roop_count], len);
+ tmp_tgt_passwords_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) * share_alter->tmp_tgt_sockets_lengths[roop_count]))
+ {
+ tmp_tgt_sockets[roop_count]= tmp_tgt_sockets_char;
+ memcpy(tmp_tgt_sockets_char, share_alter->tmp_tgt_sockets[roop_count],
+ len);
+ tmp_tgt_sockets_char+= len + 1;
+ }
+
+ if ((len=
+ sizeof(char) * share_alter->tmp_tgt_wrappers_lengths[roop_count]))
+ {
+ tmp_tgt_wrappers[roop_count]= tmp_tgt_wrappers_char;
+ memcpy(tmp_tgt_wrappers_char, share_alter->tmp_tgt_wrappers[roop_count],
+ len);
+ tmp_tgt_wrappers_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) * share_alter->tmp_tgt_ssl_cas_lengths[roop_count]))
+ {
+ tmp_tgt_ssl_cas[roop_count]= tmp_tgt_ssl_cas_char;
+ memcpy(tmp_tgt_ssl_cas_char, share_alter->tmp_tgt_ssl_cas[roop_count],
+ len);
+ tmp_tgt_ssl_cas_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_ssl_capaths_lengths[roop_count]))
+ {
+ tmp_tgt_ssl_capaths[roop_count]= tmp_tgt_ssl_capaths_char;
+ memcpy(tmp_tgt_ssl_capaths_char,
+ share_alter->tmp_tgt_ssl_capaths[roop_count], len);
+ tmp_tgt_ssl_capaths_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_ssl_certs_lengths[roop_count]))
+ {
+ tmp_tgt_ssl_certs[roop_count]= tmp_tgt_ssl_certs_char;
+ memcpy(tmp_tgt_ssl_certs_char,
+ share_alter->tmp_tgt_ssl_certs[roop_count], len);
+ tmp_tgt_ssl_certs_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_ssl_ciphers_lengths[roop_count]))
+ {
+ tmp_tgt_ssl_ciphers[roop_count]= tmp_tgt_ssl_ciphers_char;
+ memcpy(tmp_tgt_ssl_ciphers_char,
+ share_alter->tmp_tgt_ssl_ciphers[roop_count], len);
+ tmp_tgt_ssl_ciphers_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) * share_alter->tmp_tgt_ssl_keys_lengths[roop_count]))
+ {
+ tmp_tgt_ssl_keys[roop_count]= tmp_tgt_ssl_keys_char;
+ memcpy(tmp_tgt_ssl_keys_char, share_alter->tmp_tgt_ssl_keys[roop_count],
+ len);
+ tmp_tgt_ssl_keys_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_default_files_lengths[roop_count]))
+ {
+ tmp_tgt_default_files[roop_count]= tmp_tgt_default_files_char;
+ memcpy(tmp_tgt_default_files_char,
+ share_alter->tmp_tgt_default_files[roop_count], len);
+ tmp_tgt_default_files_char+= len + 1;
+ }
+
+ if ((len= sizeof(char) *
+ share_alter->tmp_tgt_default_groups_lengths[roop_count]))
+ {
+ tmp_tgt_default_groups[roop_count]= tmp_tgt_default_groups_char;
+ memcpy(tmp_tgt_default_groups_char,
+ share_alter->tmp_tgt_default_groups[roop_count], len);
+ tmp_tgt_default_groups_char+= len + 1;
+ }
tmp_tgt_dsns[roop_count] = tmp_tgt_dsns_char;
memcpy(tmp_tgt_dsns_char, share_alter->tmp_tgt_dsns[roop_count],
@@ -731,14 +764,13 @@ int spider_create_trx_alter_table(
tmp_tgt_dsns_char +=
share_alter->tmp_tgt_dsns_lengths[roop_count] + 1;
- if (share_alter->tmp_static_link_ids[roop_count])
+ if ((len= sizeof(char) *
+ share_alter->tmp_static_link_ids_lengths[roop_count]))
{
tmp_static_link_ids[roop_count] = tmp_static_link_ids_char;
memcpy(tmp_static_link_ids_char,
- share_alter->tmp_static_link_ids[roop_count],
- sizeof(char) * share_alter->tmp_static_link_ids_lengths[roop_count]);
- tmp_static_link_ids_char +=
- share_alter->tmp_static_link_ids_lengths[roop_count] + 1;
+ share_alter->tmp_static_link_ids[roop_count], len);
+ tmp_static_link_ids_char += len + 1;
}
}
diff --git a/strings/json_lib.c b/strings/json_lib.c
index cfdaa39d33c..bb60df589c3 100644
--- a/strings/json_lib.c
+++ b/strings/json_lib.c
@@ -841,7 +841,7 @@ static int skip_key(json_engine_t *j)
{
int t_next, c_len;
- if (json_instr_chr_map[j->s.c_next] == S_BKSL &&
+ if (j->s.c_next<128 && json_instr_chr_map[j->s.c_next] == S_BKSL &&
json_handle_esc(&j->s))
return 1;
diff --git a/wsrep-lib b/wsrep-lib
-Subproject f8ff2cfdd4c6424ffd96fc53bcc0f2e1d9ffe13
+Subproject 275a0af8c5b92f0ee33cfe9e23f3db5f59b56e9