revision-id: 9a5a86f293b6fe40ad606de43c04a2d8ba6b60b1 (mariadb-10.2.23-80-g9a5a86f293b)
parent(s): 4e01bc8c963d9513625dd984cd1aca24b8a7b516
author: Sujatha Sivakumar
committer: Sujatha Sivakumar
timestamp: 2019-04-26 11:37:00 +0530
message:
MDEV-17260: Memory leaks in mysqlbinlog
Problem:
========
The mysqlbinlog tool is leaking memory, causing failures in various tests when
compiling and testing with AddressSanitizer or LeakSanitizer like this:
cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source
make -j$(nproc)
cd mysql-test
ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto
Analysis:
=========
Two types of leaks were observed during above execution.
1) Leak in Log_event::read_log_event(char const*, unsigned int, char const**,
Format_description_log_event const*, char)
File: sql/log_event.cc:2150
For all row based replication events the memory which is allocated during
read_log_event is not freed after the event is processed. The event specific
memory has to be retained only when flashback option is enabled with
mysqlbinlog tool. In this case all the events are retained till the end
statement is received and they are processed in reverse order and they are
destroyed. But in the existing code all events are retained irrespective of
flashback mode. Hence the memory leaks are observed.
2) read_remote_annotate_event(unsigned char*, unsigned long, char const**)
File: client/mysqlbinlog.cc:194
In general the Annotate event is not printed immediately because all
subsequent rbr-events can be filtered away. Instead it will be printed
together with the first not filtered away Table map or the last rbr will be
processed. While reading remote annotate events memory is allocated for event
buffer and event's temp_buf is made to point to the allocated buffer as shown
below. The TRUE flag is used for doing proper cleanup using free_temp_buf().
i.e at the time of deletion of annotate event its destructor takes care of
clearing the temp_buf.
/*
Ensure the event->temp_buf is pointing to the allocated buffer.
(TRUE = free temp_buf on the event deletion)
*/
event->register_temp_buf((char*)event_buf, TRUE);
But existing code does the following when it receives a remote annotate_event.
if (remote_opt)
ev->temp_buf= 0;
That is code immediately sets temp_buf=0, because of which free_temp_buf()
call will return empty handed as it has lost the reference to the allocated
temporary buffer. This results in memory leak
Fix:
====
1) If not in flashback mode, destroy the memory for events once they are
processed.
2) Remove the ev->temp_buf=0 code for remote option. Let the proper cleanup to
be done as part of free_temp_buf().
---
client/mysqlbinlog.cc | 9 +++------
mysql-test/suite/binlog/r/flashback.result | 18 ++++++++++++++++++
mysql-test/suite/binlog/t/flashback.test | 30 +++++++++++++++++++++++++++++-
3 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index cfc05cbf794..3dad4fef67b 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -227,8 +227,7 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
if (annotate_event)
{
annotate_event->print(result_file, print_event_info);
- delete annotate_event; // the event should not be printed more than once
- annotate_event= 0;
+ free_annotate_event();
}
}
@@ -1465,7 +1464,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1478,7 +1477,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1539,8 +1538,6 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
}
}
- if (remote_opt)
- ev->temp_buf= 0;
if (destroy_evt) /* destroy it later if not set (ignored table map) */
delete ev;
}
diff --git a/mysql-test/suite/binlog/r/flashback.result b/mysql-test/suite/binlog/r/flashback.result
index c96eaebe838..0d189b735a3 100644
--- a/mysql-test/suite/binlog/r/flashback.result
+++ b/mysql-test/suite/binlog/r/flashback.result
@@ -674,6 +674,24 @@ world.city 563256876
DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
+# < CASE 7 >
+# Test Case for MDEV-17260
+#
+RESET MASTER;
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+# 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+6
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+# 0- Rows must be present
+include/assert.inc [Table t1 should have 0 rows.]
+# 6- Rows must be present upon restoring from flashback
+include/assert.inc [Table t1 should have six rows.]
+DROP TABLE t1;
SET binlog_format=statement;
Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
diff --git a/mysql-test/suite/binlog/t/flashback.test b/mysql-test/suite/binlog/t/flashback.test
index 3fc8c44c60c..9782fa4ec83 100644
--- a/mysql-test/suite/binlog/t/flashback.test
+++ b/mysql-test/suite/binlog/t/flashback.test
@@ -335,8 +335,36 @@ DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
-## Clear
+--echo # < CASE 7 >
+--echo # Test Case for MDEV-17260
+--echo #
+
+RESET MASTER;
+
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+--echo # 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+--echo # 0- Rows must be present
+--let $assert_cond= COUNT(*) = 0 FROM t1
+--let $assert_text= Table t1 should have 0 rows.
+--source include/assert.inc
+
+--exec $MYSQL_BINLOG -vv -B --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002> $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql
+--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql;"
+
+--echo # 6- Rows must be present upon restoring from flashback
+--let $assert_cond= COUNT(*) = 6 FROM t1
+--let $assert_text= Table t1 should have six rows.
+--source include/assert.inc
+
+DROP TABLE t1;
+
+## Clear
SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED
SET GLOBAL binlog_format=statement;
1
0
revision-id: 9a5a86f293b6fe40ad606de43c04a2d8ba6b60b1 (mariadb-10.2.23-80-g9a5a86f293b)
parent(s): 4e01bc8c963d9513625dd984cd1aca24b8a7b516
author: Sujatha Sivakumar
committer: Sujatha Sivakumar
timestamp: 2019-04-26 11:37:00 +0530
message:
MDEV-17260: Memory leaks in mysqlbinlog
Problem:
========
The mysqlbinlog tool is leaking memory, causing failures in various tests when
compiling and testing with AddressSanitizer or LeakSanitizer like this:
cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source
make -j$(nproc)
cd mysql-test
ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto
Analysis:
=========
Two types of leaks were observed during above execution.
1) Leak in Log_event::read_log_event(char const*, unsigned int, char const**,
Format_description_log_event const*, char)
File: sql/log_event.cc:2150
For all row based replication events the memory which is allocated during
read_log_event is not freed after the event is processed. The event specific
memory has to be retained only when flashback option is enabled with
mysqlbinlog tool. In this case all the events are retained till the end
statement is received and they are processed in reverse order and they are
destroyed. But in the existing code all events are retained irrespective of
flashback mode. Hence the memory leaks are observed.
2) read_remote_annotate_event(unsigned char*, unsigned long, char const**)
File: client/mysqlbinlog.cc:194
In general the Annotate event is not printed immediately because all
subsequent rbr-events can be filtered away. Instead it will be printed
together with the first not filtered away Table map or the last rbr will be
processed. While reading remote annotate events memory is allocated for event
buffer and event's temp_buf is made to point to the allocated buffer as shown
below. The TRUE flag is used for doing proper cleanup using free_temp_buf().
i.e at the time of deletion of annotate event its destructor takes care of
clearing the temp_buf.
/*
Ensure the event->temp_buf is pointing to the allocated buffer.
(TRUE = free temp_buf on the event deletion)
*/
event->register_temp_buf((char*)event_buf, TRUE);
But existing code does the following when it receives a remote annotate_event.
if (remote_opt)
ev->temp_buf= 0;
That is code immediately sets temp_buf=0, because of which free_temp_buf()
call will return empty handed as it has lost the reference to the allocated
temporary buffer. This results in memory leak
Fix:
====
1) If not in flashback mode, destroy the memory for events once they are
processed.
2) Remove the ev->temp_buf=0 code for remote option. Let the proper cleanup to
be done as part of free_temp_buf().
---
client/mysqlbinlog.cc | 9 +++------
mysql-test/suite/binlog/r/flashback.result | 18 ++++++++++++++++++
mysql-test/suite/binlog/t/flashback.test | 30 +++++++++++++++++++++++++++++-
3 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index cfc05cbf794..3dad4fef67b 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -227,8 +227,7 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
if (annotate_event)
{
annotate_event->print(result_file, print_event_info);
- delete annotate_event; // the event should not be printed more than once
- annotate_event= 0;
+ free_annotate_event();
}
}
@@ -1465,7 +1464,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1478,7 +1477,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1539,8 +1538,6 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
}
}
- if (remote_opt)
- ev->temp_buf= 0;
if (destroy_evt) /* destroy it later if not set (ignored table map) */
delete ev;
}
diff --git a/mysql-test/suite/binlog/r/flashback.result b/mysql-test/suite/binlog/r/flashback.result
index c96eaebe838..0d189b735a3 100644
--- a/mysql-test/suite/binlog/r/flashback.result
+++ b/mysql-test/suite/binlog/r/flashback.result
@@ -674,6 +674,24 @@ world.city 563256876
DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
+# < CASE 7 >
+# Test Case for MDEV-17260
+#
+RESET MASTER;
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+# 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+6
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+# 0- Rows must be present
+include/assert.inc [Table t1 should have 0 rows.]
+# 6- Rows must be present upon restoring from flashback
+include/assert.inc [Table t1 should have six rows.]
+DROP TABLE t1;
SET binlog_format=statement;
Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
diff --git a/mysql-test/suite/binlog/t/flashback.test b/mysql-test/suite/binlog/t/flashback.test
index 3fc8c44c60c..9782fa4ec83 100644
--- a/mysql-test/suite/binlog/t/flashback.test
+++ b/mysql-test/suite/binlog/t/flashback.test
@@ -335,8 +335,36 @@ DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
-## Clear
+--echo # < CASE 7 >
+--echo # Test Case for MDEV-17260
+--echo #
+
+RESET MASTER;
+
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+--echo # 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+--echo # 0- Rows must be present
+--let $assert_cond= COUNT(*) = 0 FROM t1
+--let $assert_text= Table t1 should have 0 rows.
+--source include/assert.inc
+
+--exec $MYSQL_BINLOG -vv -B --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002> $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql
+--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql;"
+
+--echo # 6- Rows must be present upon restoring from flashback
+--let $assert_cond= COUNT(*) = 6 FROM t1
+--let $assert_text= Table t1 should have six rows.
+--source include/assert.inc
+
+DROP TABLE t1;
+
+## Clear
SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED
SET GLOBAL binlog_format=statement;
1
0
revision-id: 9a5a86f293b6fe40ad606de43c04a2d8ba6b60b1 (mariadb-10.2.23-80-g9a5a86f293b)
parent(s): 4e01bc8c963d9513625dd984cd1aca24b8a7b516
author: Sujatha Sivakumar
committer: Sujatha Sivakumar
timestamp: 2019-04-26 11:37:00 +0530
message:
MDEV-17260: Memory leaks in mysqlbinlog
Problem:
========
The mysqlbinlog tool is leaking memory, causing failures in various tests when
compiling and testing with AddressSanitizer or LeakSanitizer like this:
cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source
make -j$(nproc)
cd mysql-test
ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto
Analysis:
=========
Two types of leaks were observed during above execution.
1) Leak in Log_event::read_log_event(char const*, unsigned int, char const**,
Format_description_log_event const*, char)
File: sql/log_event.cc:2150
For all row based replication events the memory which is allocated during
read_log_event is not freed after the event is processed. The event specific
memory has to be retained only when flashback option is enabled with
mysqlbinlog tool. In this case all the events are retained till the end
statement is received and they are processed in reverse order and they are
destroyed. But in the existing code all events are retained irrespective of
flashback mode. Hence the memory leaks are observed.
2) read_remote_annotate_event(unsigned char*, unsigned long, char const**)
File: client/mysqlbinlog.cc:194
In general the Annotate event is not printed immediately because all
subsequent rbr-events can be filtered away. Instead it will be printed
together with the first not filtered away Table map or the last rbr will be
processed. While reading remote annotate events memory is allocated for event
buffer and event's temp_buf is made to point to the allocated buffer as shown
below. The TRUE flag is used for doing proper cleanup using free_temp_buf().
i.e at the time of deletion of annotate event its destructor takes care of
clearing the temp_buf.
/*
Ensure the event->temp_buf is pointing to the allocated buffer.
(TRUE = free temp_buf on the event deletion)
*/
event->register_temp_buf((char*)event_buf, TRUE);
But existing code does the following when it receives a remote annotate_event.
if (remote_opt)
ev->temp_buf= 0;
That is code immediately sets temp_buf=0, because of which free_temp_buf()
call will return empty handed as it has lost the reference to the allocated
temporary buffer. This results in memory leak
Fix:
====
1) If not in flashback mode, destroy the memory for events once they are
processed.
2) Remove the ev->temp_buf=0 code for remote option. Let the proper cleanup to
be done as part of free_temp_buf().
---
client/mysqlbinlog.cc | 9 +++------
mysql-test/suite/binlog/r/flashback.result | 18 ++++++++++++++++++
mysql-test/suite/binlog/t/flashback.test | 30 +++++++++++++++++++++++++++++-
3 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index cfc05cbf794..3dad4fef67b 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -227,8 +227,7 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
if (annotate_event)
{
annotate_event->print(result_file, print_event_info);
- delete annotate_event; // the event should not be printed more than once
- annotate_event= 0;
+ free_annotate_event();
}
}
@@ -1465,7 +1464,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1478,7 +1477,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1539,8 +1538,6 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
}
}
- if (remote_opt)
- ev->temp_buf= 0;
if (destroy_evt) /* destroy it later if not set (ignored table map) */
delete ev;
}
diff --git a/mysql-test/suite/binlog/r/flashback.result b/mysql-test/suite/binlog/r/flashback.result
index c96eaebe838..0d189b735a3 100644
--- a/mysql-test/suite/binlog/r/flashback.result
+++ b/mysql-test/suite/binlog/r/flashback.result
@@ -674,6 +674,24 @@ world.city 563256876
DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
+# < CASE 7 >
+# Test Case for MDEV-17260
+#
+RESET MASTER;
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+# 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+6
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+# 0- Rows must be present
+include/assert.inc [Table t1 should have 0 rows.]
+# 6- Rows must be present upon restoring from flashback
+include/assert.inc [Table t1 should have six rows.]
+DROP TABLE t1;
SET binlog_format=statement;
Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
diff --git a/mysql-test/suite/binlog/t/flashback.test b/mysql-test/suite/binlog/t/flashback.test
index 3fc8c44c60c..9782fa4ec83 100644
--- a/mysql-test/suite/binlog/t/flashback.test
+++ b/mysql-test/suite/binlog/t/flashback.test
@@ -335,8 +335,36 @@ DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
-## Clear
+--echo # < CASE 7 >
+--echo # Test Case for MDEV-17260
+--echo #
+
+RESET MASTER;
+
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+--echo # 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+--echo # 0- Rows must be present
+--let $assert_cond= COUNT(*) = 0 FROM t1
+--let $assert_text= Table t1 should have 0 rows.
+--source include/assert.inc
+
+--exec $MYSQL_BINLOG -vv -B --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002> $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql
+--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql;"
+
+--echo # 6- Rows must be present upon restoring from flashback
+--let $assert_cond= COUNT(*) = 6 FROM t1
+--let $assert_text= Table t1 should have six rows.
+--source include/assert.inc
+
+DROP TABLE t1;
+
+## Clear
SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED
SET GLOBAL binlog_format=statement;
1
0

[Commits] 0beb0ed: MDEV-17894 Assertion `(thd->lex)->current_select' failed in MYSQLparse(),
by IgorBabaev 26 Apr '19
by IgorBabaev 26 Apr '19
26 Apr '19
revision-id: 0beb0edf54a889c4db29d052cd3ca14877302c0b (mariadb-10.3.12-86-g0beb0ed)
parent(s): 51e48b9f8981986257a1cfbdf75e4fc29a5959c1
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-04-25 20:27:24 -0700
message:
MDEV-17894 Assertion `(thd->lex)->current_select' failed in MYSQLparse(),
query with VALUES()
A table value constructor can be used in all contexts where a select
can be used. In particular an ORDER BY clause or a LIMIT clause or both
of them can be attached to a table value constructor to produce a new
query. Unfortunately execution of such queries was not supported.
This patch fixes the problem.
---
mysql-test/main/table_value_constr.result | 333 ++++++++++++++++++++++++++++++
mysql-test/main/table_value_constr.test | 149 +++++++++++++
sql/item_subselect.cc | 2 +-
sql/item_subselect.h | 2 +-
sql/sql_lex.cc | 16 ++
sql/sql_lex.h | 6 +
sql/sql_tvc.cc | 203 +++++++++++++++---
sql/sql_tvc.h | 5 +
sql/sql_union.cc | 19 +-
sql/sql_yacc.yy | 11 +-
sql/sql_yacc_ora.yy | 12 +-
11 files changed, 715 insertions(+), 43 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index 1d485af..082f2b4 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2189,3 +2189,336 @@ EXECUTE stmt;
1 + 1 2 abc
2 2 abc
DEALLOCATE PREPARE stmt;
+#
+# MDEV-17894: tvc with ORDER BY ... LIMIT
+#
+values (5), (7), (1), (3), (4) limit 2;
+5
+5
+7
+explain extended values (5), (7), (1), (3), (4) limit 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 values (5),(7),(1),(3),(4) limit 2
+values (5), (7), (1), (3), (4) limit 2 offset 1;
+5
+7
+1
+explain extended values (5), (7), (1), (3), (4) limit 2 offset 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 values (5),(7),(1),(3),(4) limit 1,2
+values (5), (7), (1), (3), (4) order by 1 limit 2;
+5
+1
+3
+explain extended values (5), (7), (1), (3), (4) order by 1 limit 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNIT RESULT <unit1> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 values (5),(7),(1),(3),(4) order by 1 limit 2
+values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1;
+5
+3
+4
+explain extended values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNIT RESULT <unit1> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 values (5),(7),(1),(3),(4) order by 1 limit 1,2
+values (5), (7), (1), (3), (4) order by 1;
+5
+1
+3
+4
+5
+7
+explain extended values (5), (7), (1), (3), (4) order by 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNIT RESULT <unit1> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 values (5),(7),(1),(3),(4) order by 1
+select 2 union (values (5), (7), (1), (3), (4) limit 2);
+2
+2
+5
+7
+explain extended select 2 union (values (5), (7), (1), (3), (4) limit 2);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 /* select#1 */ select 2 AS `2` union (values (5),(7),(1),(3),(4) limit 2)
+select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1);
+2
+2
+7
+1
+explain extended select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 /* select#1 */ select 2 AS `2` union (values (5),(7),(1),(3),(4) limit 1,2)
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2);
+2
+2
+1
+3
+explain extended select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION <derived2> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 /* select#1 */ select 2 AS `2` union (/* select#3 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2)
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1);
+2
+2
+3
+4
+explain extended select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION <derived2> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 /* select#1 */ select 2 AS `2` union (/* select#3 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 1,2)
+(values (5), (7), (1), (3), (4) limit 2) union select 2;
+5
+5
+7
+2
+explain extended (values (5), (7), (1), (3), (4) limit 2) union select 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 2) union /* select#2 */ select 2 AS `2`
+(values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2;
+5
+7
+1
+2
+explain extended (values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union /* select#2 */ select 2 AS `2`
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2;
+5
+1
+3
+2
+explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+3 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (/* select#1 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2) union /* select#2 */ select 2 AS `2`
+(values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2;
+5
+3
+4
+2
+explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+3 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (/* select#1 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 1,2) union /* select#2 */ select 2 AS `2`
+select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3);
+3
+3
+3
+4
+explain extended select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 /* select#1 */ select 3 AS `3` union all (values (5),(7),(1),(3),(4) limit 3,2)
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3;
+5
+3
+4
+3
+explain extended (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 3,2) union all /* select#2 */ select 3 AS `3`
+select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2);
+3
+3
+1
+3
+explain extended select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2);
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION <derived2> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 /* select#1 */ select 3 AS `3` union all (/* select#3 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2)
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3;
+5
+1
+3
+3
+explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+3 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (/* select#1 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2) union all /* select#2 */ select 3 AS `3`
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+union
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+5
+7
+1
+3
+explain extended ( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+union
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION <derived2> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union (/* select#3 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2)
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+union all
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+5
+7
+1
+1
+3
+explain extended ( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+union all
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+3 UNION <derived2> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL NULL
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union all (/* select#3 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 2)
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1;
+5
+3
+3
+4
+explain extended (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 (values (5),(7),(1),(3),(4) limit 3,2) union all /* select#2 */ select 3 AS `3` order by 1
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1;
+5
+3
+3
+4
+5
+explain extended (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+3 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 (/* select#1 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 1,3) union all /* select#2 */ select 3 AS `3` order by 1
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3
+order by 1 limit 2 offset 1;
+5
+3
+4
+explain extended (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3
+order by 1 limit 2 offset 1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 100.00 Using filesort
+3 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL Using filesort
+Warnings:
+Note 1003 (/* select#1 */ select `tvc_0`.`5` AS `5` from (values (5),(7),(1),(3),(4)) `tvc_0` order by 1 limit 1,3) union all /* select#2 */ select 3 AS `3` order by 1 limit 1,2
+prepare stmt from "
+select 2 union (values (5), (7), (1), (3), (4) limit 2)
+";
+execute stmt;
+2
+2
+5
+7
+execute stmt;
+2
+2
+5
+7
+deallocate prepare stmt;
+prepare stmt from "
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2)
+";
+execute stmt;
+2
+2
+1
+3
+execute stmt;
+2
+2
+1
+3
+deallocate prepare stmt;
+prepare stmt from "
+select 3 union all (values (5), (7), (1), (3), (4) limit 2)
+";
+execute stmt;
+3
+3
+5
+7
+execute stmt;
+3
+3
+5
+7
+deallocate prepare stmt;
+prepare stmt from "
+select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2)
+";
+execute stmt;
+3
+3
+1
+3
+execute stmt;
+3
+3
+1
+3
+deallocate prepare stmt;
diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test
index 0dd0a7a..a6d0d47 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1123,3 +1123,152 @@ PREPARE stmt FROM "SELECT * FROM (VALUES(1 + 1,2,'abc')) t";
EXECUTE stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
+
+--echo #
+--echo # MDEV-17894: tvc with ORDER BY ... LIMIT
+--echo #
+
+let $q=
+values (5), (7), (1), (3), (4) limit 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1 limit 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+values (5), (7), (1), (3), (4) order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1);
+eval $q;
+eval explain extended $q;
+
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2;
+eval $q;
+eval explain extended $q;
+
+
+let $q=
+select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3);
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2);
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3;
+eval $q;
+eval explain extended $q;
+
+let $q=
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+eval $q;
+eval explain extended $q;
+
+let $q=
+( values (5), (7), (1), (3), (4) limit 2 offset 1 )
+ union all
+( values (5), (7), (1), (3), (4) order by 1 limit 2 );
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1;
+eval $q;
+eval explain extended $q;
+
+let $q=
+(values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3
+ order by 1 limit 2 offset 1;
+eval $q;
+eval explain extended $q;
+
+prepare stmt from "
+select 2 union (values (5), (7), (1), (3), (4) limit 2)
+";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+prepare stmt from "
+select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2)
+";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+prepare stmt from "
+select 3 union all (values (5), (7), (1), (3), (4) limit 2)
+";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+prepare stmt from "
+select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2)
+";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 207aa9a..475e74d 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -269,7 +269,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
{
if (sl->tvc)
{
- wrap_tvc_in_derived_table(thd, sl);
+ wrap_tvc_into_select(thd, sl);
}
}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 363dbba..5a9968b 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -267,7 +267,7 @@ class Item_subselect :public Item_result_field,
Item* build_clone(THD *thd) { return 0; }
Item* get_copy(THD *thd) { return 0; }
- bool wrap_tvc_in_derived_table(THD *thd, st_select_lex *tvc_sl);
+ bool wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl);
friend class select_result_interceptor;
friend class Item_in_optimizer;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index d6cc62c..638530d 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2275,6 +2275,7 @@ void st_select_lex_unit::init_query()
with_element= 0;
columns_are_renamed= false;
intersect_mark= NULL;
+ with_wrapped_tvc= false;
}
void st_select_lex::init_query()
@@ -3411,6 +3412,19 @@ bool st_select_lex_unit::union_needs_tmp_table()
{
if (with_element && with_element->is_recursive)
return true;
+ if (!with_wrapped_tvc)
+ {
+ for (st_select_lex *sl= first_select(); sl; sl=sl->next_select())
+ {
+ if (sl->tvc && sl->tvc->to_be_wrapped_as_with_tail())
+ {
+ with_wrapped_tvc= true;
+ break;
+ }
+ }
+ }
+ if (with_wrapped_tvc)
+ return true;
return union_distinct != NULL ||
global_parameters()->order_list.elements != 0 ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
@@ -8236,6 +8250,8 @@ bool LEX::tvc_finalize()
current_select->options))))
return true;
many_values.empty();
+ if (!current_select->master_unit()->fake_select_lex)
+ current_select->master_unit()->add_fake_select_lex(thd);
return false;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 926b09e..4123275 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -806,6 +806,12 @@ class st_select_lex_unit: public st_select_lex_node {
*/
Item_int *intersect_mark;
/**
+ TRUE if the unit contained TVC at the top level that has been wrapped
+ into SELECT:
+ VALUES (v1) ... (vn) => SELECT * FROM (VALUES (v1) ... (vn)) as tvc
+ */
+ bool with_wrapped_tvc;
+ /**
Pointer to 'last' select, or pointer to select where we stored
global parameters for union.
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index a5085fd..c400264 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -344,6 +344,7 @@ bool table_value_constr::exec(SELECT_LEX *sl)
DBUG_ENTER("table_value_constr::exec");
List_iterator_fast<List_item> li(lists_of_values);
List_item *elem;
+ ha_rows send_records= 0;
if (select_options & SELECT_DESCRIBE)
DBUG_RETURN(false);
@@ -357,7 +358,13 @@ bool table_value_constr::exec(SELECT_LEX *sl)
while ((elem= li++))
{
- result->send_data(*elem);
+ if (send_records >= sl->master_unit()->select_limit_cnt)
+ break;
+ int rc= result->send_data(*elem);
+ if (!rc)
+ send_records++;
+ else if (rc > 0)
+ DBUG_RETURN(true);
}
if (result->send_eof())
@@ -436,6 +443,12 @@ void table_value_constr::print(THD *thd, String *str,
print_list_item(str, list, query_type);
}
+ if (select_lex->order_list.elements)
+ {
+ str->append(STRING_WITH_LEN(" order by "));
+ select_lex->print_order(str, select_lex->order_list.first, query_type);
+ }
+ select_lex->print_limit(thd, str, query_type);
}
@@ -533,7 +546,8 @@ static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
char buff[6];
alias->length= my_snprintf(buff, sizeof(buff),
- "tvc_%u", parent_select->curr_tvc_name);
+ "tvc_%u",
+ parent_select ? parent_select->curr_tvc_name : 0);
alias->str= thd->strmake(buff, alias->length);
if (!alias->str)
return true;
@@ -542,19 +556,57 @@ static bool create_tvc_name(THD *thd, st_select_lex *parent_select,
}
-bool Item_subselect::wrap_tvc_in_derived_table(THD *thd,
- st_select_lex *tvc_sl)
+/**
+ @brief
+ Check whether TVC used in unit is to be wrapped into select
+
+ @details
+ TVC used in unit that contains more than one members is to be wrapped
+ into select if it is tailed with ORDER BY ... LIMIT n [OFFSET m]
+
+ @retval
+ true if TVC is to be wrapped
+ false otherwise
+*/
+
+bool table_value_constr::to_be_wrapped_as_with_tail()
+{
+ return select_lex->master_unit()->first_select()->next_select() &&
+ select_lex->order_list.elements && select_lex->explicit_limit;
+}
+
+
+/**
+ @brief
+ Wrap table value constructor into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+ @parent_select The parent select if tvc_sl used in a subquery
+
+ @details
+ The function wraps the TVC tvc_sl into a select:
+ the function transforms the TVC of the form VALUES (v1), ... (vn) into
+ the select of the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+
+ @retval pointer to the result of of the transformation if successful
+ NULL - otherwise
+*/
+
+static
+st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
+ st_select_lex *parent_select)
{
LEX *lex= thd->lex;
- /* SELECT_LEX object where the transformation is performed */
- SELECT_LEX *parent_select= lex->current_select;
+ select_result *save_result= thd->lex->result;
uint8 save_derived_tables= lex->derived_tables;
+ thd->lex->result= NULL;
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
-
/*
- Create SELECT_LEX of the subquery SQ used in the result of transformation
+ Create SELECT_LEX of the select used in the result of transformation
*/
lex->current_select= tvc_sl;
if (mysql_new_select(lex, 0, NULL))
@@ -562,15 +614,15 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd,
mysql_init_select(lex);
/* Create item list as '*' for the subquery SQ */
Item *item;
- SELECT_LEX *sq_select; // select for IN subquery;
- sq_select= lex->current_select;
- sq_select->linkage= tvc_sl->linkage;
- sq_select->parsing_place= SELECT_LIST;
- item= new (thd->mem_root) Item_field(thd, &sq_select->context,
+ SELECT_LEX *wrapper_sl;
+ wrapper_sl= lex->current_select;
+ wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->parsing_place= SELECT_LIST;
+ item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
NULL, NULL, &star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
- (sq_select->with_wild)++;
+ (wrapper_sl->with_wild)++;
/* Exclude SELECT with TVC */
tvc_sl->exclude();
@@ -585,11 +637,11 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd,
derived_unit= tvc_select->master_unit();
tvc_select->linkage= DERIVED_TABLE_TYPE;
- lex->current_select= sq_select;
+ lex->current_select= wrapper_sl;
/*
Create the name of the wrapping derived table and
- add it to the FROM list of the subquery SQ
+ add it to the FROM list of the wrapper
*/
Table_ident *ti;
LEX_CSTRING alias;
@@ -598,35 +650,120 @@ bool Item_subselect::wrap_tvc_in_derived_table(THD *thd,
create_tvc_name(thd, parent_select, &alias))
goto err;
if (!(derived_tab=
- sq_select->add_table_to_list(thd,
- ti, &alias, 0,
- TL_READ, MDL_SHARED_READ)))
+ wrapper_sl->add_table_to_list(thd,
+ ti, &alias, 0,
+ TL_READ, MDL_SHARED_READ)))
goto err;
- sq_select->add_joined_table(derived_tab);
- sq_select->add_where_field(derived_unit->first_select());
- sq_select->context.table_list= sq_select->table_list.first;
- sq_select->context.first_name_resolution_table= sq_select->table_list.first;
- sq_select->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
+ wrapper_sl->add_joined_table(derived_tab);
+ wrapper_sl->add_where_field(derived_unit->first_select());
+ wrapper_sl->context.table_list= wrapper_sl->table_list.first;
+ wrapper_sl->context.first_name_resolution_table= wrapper_sl->table_list.first;
+ wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
lex->derived_tables|= DERIVED_SUBQUERY;
- sq_select->where= 0;
- sq_select->set_braces(false);
+ wrapper_sl->where= 0;
+ wrapper_sl->set_braces(false);
derived_unit->set_with_clause(0);
- if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
- ((subselect_single_select_engine *) engine)->change_select(sq_select);
-
if (arena)
thd->restore_active_arena(arena, &backup);
- lex->current_select= sq_select;
- return false;
+ thd->lex->result= save_result;
+ return wrapper_sl;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
+ thd->lex->result= save_result;
lex->derived_tables= save_derived_tables;
- lex->current_select= parent_select;
- return true;
+ return 0;
+}
+
+
+/**
+ @brief
+ Wrap TVC with ORDER BY ... LIMIT tail into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+
+ @details
+ The function wraps the TVC tvc_sl into a select:
+ the function transforms the TVC with tail of the form
+ VALUES (v1), ... (vn) ORDER BY ... LIMIT n [OFFSET m]
+ into the select with the same tail of the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+ ORDER BY ... LIMIT n [OFFSET m]
+
+ @retval pointer to the result of of the transformation if successful
+ NULL - otherwise
+*/
+
+st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl)
+{
+ st_select_lex *wrapper_sl= wrap_tvc(thd, tvc_sl, NULL);
+ if (!wrapper_sl)
+ return NULL;
+
+ wrapper_sl->order_list= tvc_sl->order_list;
+ wrapper_sl->select_limit= tvc_sl->select_limit;
+ wrapper_sl->offset_limit= tvc_sl->offset_limit;
+ wrapper_sl->braces= tvc_sl->braces;
+ wrapper_sl->explicit_limit= tvc_sl->explicit_limit;
+ tvc_sl->order_list.empty();
+ tvc_sl->select_limit= NULL;
+ tvc_sl->offset_limit= NULL;
+ tvc_sl->braces= 0;
+ tvc_sl->explicit_limit= false;
+ if (tvc_sl->select_number == 1)
+ {
+ tvc_sl->select_number= wrapper_sl->select_number;
+ wrapper_sl->select_number= 1;
+ }
+ if (tvc_sl->master_unit()->union_distinct == tvc_sl)
+ {
+ wrapper_sl->master_unit()->union_distinct= wrapper_sl;
+ }
+ thd->lex->current_select= wrapper_sl;
+ return wrapper_sl;
+}
+
+
+/**
+ @brief
+ Wrap TVC in a subselect into a select
+
+ @param thd The context handler
+ @param tvc_sl The TVC to wrap
+
+ @details
+ The function wraps the TVC tvc_sl used in a subselect into a select
+ the function transforms the TVC of the form VALUES (v1), ... (vn)
+ into the select the form
+ SELECT * FROM (VALUES (v1), ... (vn)) tvc_x
+ and replaces the subselect with the result of the transformation.
+
+ @retval false if successfull
+ true otherwise
+*/
+
+bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl)
+{
+ LEX *lex= thd->lex;
+ /* SELECT_LEX object where the transformation is performed */
+ SELECT_LEX *parent_select= lex->current_select;
+ SELECT_LEX *wrapper_sl= wrap_tvc(thd, tvc_sl, parent_select);
+ if (wrapper_sl)
+ {
+ if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
+ ((subselect_single_select_engine *) engine)->change_select(wrapper_sl);
+ lex->current_select= wrapper_sl;
+ return false;
+ }
+ else
+ {
+ lex->current_select= parent_select;
+ return true;
+ }
}
diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h
index 128cc88..594a77a 100644
--- a/sql/sql_tvc.h
+++ b/sql/sql_tvc.h
@@ -57,6 +57,8 @@ class table_value_constr : public Sql_alloc
select_result *tmp_result,
st_select_lex_unit *unit_arg);
+ bool to_be_wrapped_as_with_tail();
+
int save_explain_data_intern(THD *thd_arg,
Explain_query *output);
bool optimize(THD *thd_arg);
@@ -64,4 +66,7 @@ class table_value_constr : public Sql_alloc
void print(THD *thd_arg, String *str, enum_query_type query_type);
};
+
+st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl);
+
#endif /* SQL_TVC_INCLUDED */
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 7b0e796..bcca27c 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -831,7 +831,8 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
bool is_union_select;
bool have_except= FALSE, have_intersect= FALSE;
bool instantiate_tmp_table= false;
- bool single_tvc= !first_sl->next_select() && first_sl->tvc;
+ bool single_tvc= !first_sl->next_select() && first_sl->tvc &&
+ !fake_select_lex;
DBUG_ENTER("st_select_lex_unit::prepare");
DBUG_ASSERT(thd == current_thd);
@@ -986,7 +987,21 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
{
if (sl->tvc)
{
- if (sl->tvc->prepare(thd, sl, tmp_result, this))
+ if (sl->tvc->to_be_wrapped_as_with_tail())
+ {
+ st_select_lex *wrapper_sl= wrap_tvc_with_tail(thd, sl);
+ if (!wrapper_sl)
+ goto err;
+
+ if (sl == first_sl)
+ first_sl= wrapper_sl;
+ sl= wrapper_sl;
+
+ if (prepare_join(thd, sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+ }
+ else if (sl->tvc->prepare(thd, sl, tmp_result, this))
goto err;
}
else if (prepare_join(thd, sl, tmp_result, additional_options,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 98ead67..649ceee 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -9192,7 +9192,7 @@ select_paren:
{
Lex->current_select->set_braces(true);
}
- table_value_constructor
+ table_value_constructor select_part3
{
DBUG_ASSERT(Lex->current_select->braces);
}
@@ -9212,6 +9212,12 @@ select_paren:
| '(' select_paren ')'
;
+select_parent_union_query_term_proper:
+ SELECT_SYM select_options_and_item_list select_part3_union_query_term
+ opt_select_lock_type
+ | table_value_constructor select_part3_union_query_term
+ ;
+
select_paren_union_query_term:
{
/*
@@ -9220,8 +9226,7 @@ select_paren_union_query_term:
*/
Lex->current_select->set_braces(true);
}
- SELECT_SYM select_options_and_item_list select_part3_union_query_term
- opt_select_lock_type
+ select_parent_union_query_term_proper
{
DBUG_ASSERT(Lex->current_select->braces);
}
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index f7aa1c9..d3bb4fe 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -9125,11 +9125,12 @@ union_list_part2:
| '(' select_paren_union_query_term ')' union_order_or_limit
;
+
select_paren:
{
Lex->current_select->set_braces(true);
}
- table_value_constructor
+ table_value_constructor select_part3
{
DBUG_ASSERT(Lex->current_select->braces);
}
@@ -9149,6 +9150,12 @@ select_paren:
| '(' select_paren ')'
;
+select_parent_union_query_term_proper:
+ SELECT_SYM select_options_and_item_list select_part3_union_query_term
+ opt_select_lock_type
+ | table_value_constructor select_part3_union_query_term
+ ;
+
select_paren_union_query_term:
{
/*
@@ -9157,8 +9164,7 @@ select_paren_union_query_term:
*/
Lex->current_select->set_braces(true);
}
- SELECT_SYM select_options_and_item_list select_part3_union_query_term
- opt_select_lock_type
+ select_parent_union_query_term_proper
{
DBUG_ASSERT(Lex->current_select->braces);
}
1
0

[Commits] 647c22692ad: MDEV-17588 replicate-do filters cause errors when creating filtered-out tables on master with syntax unsupported on slave
by sachin.setiyaï¼ mariadb.com 25 Apr '19
by sachin.setiyaï¼ mariadb.com 25 Apr '19
25 Apr '19
revision-id: 647c22692ad1fe466eb14b749520cc520807d4fa (mariadb-10.1.38-114-g647c22692ad)
parent(s): 6c5e4c9bc0d9ac30f7ec7ee334630bacb58687ba
author: Sachin
committer: Sachin
timestamp: 2019-04-23 17:58:03 +0530
message:
MDEV-17588 replicate-do filters cause errors when creating filtered-out tables on master with syntax unsupported on slave
Prototype Commit.
Delay throwing of error in parsing stage untill we know the result from rpl
filter.
---
mysql-test/suite/rpl/r/rpl_mdev_17588.result | 26 ++++++++++++++++++++++
mysql-test/suite/rpl/t/rpl_mdev_17588-slave.opt | 1 +
mysql-test/suite/rpl/t/rpl_mdev_17588.test | 29 +++++++++++++++++++++++++
sql/sql_class.h | 1 +
sql/sql_parse.cc | 7 ++++++
sql/sql_yacc.yy | 12 +++++++---
6 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/mysql-test/suite/rpl/r/rpl_mdev_17588.result b/mysql-test/suite/rpl/r/rpl_mdev_17588.result
new file mode 100644
index 00000000000..976efde2bdd
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_mdev_17588.result
@@ -0,0 +1,26 @@
+include/master-slave.inc
+[connection master]
+set sql_log_bin= 0;
+install soname 'ha_tokudb';
+set sql_log_bin= 1;
+create table t1 (a int) engine=TokuDB;
+create table t2 (a int);
+create table t3 (a int) engine=TokuDB;
+include/wait_for_slave_sql_error.inc [errno=1286]
+show create table t1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+show create table t3;
+ERROR 42S02: Table 'test.t3' doesn't exist
+SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
+START SLAVE;
+drop table t1, t2, t3;
+set sql_log_bin= 0;
+uninstall soname 'ha_tokudb';
+set sql_log_bin= 1;
+CALL mtr.add_suppression('Slave: Unknown storage engine .* Error_code: 1286');
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_mdev_17588-slave.opt b/mysql-test/suite/rpl/t/rpl_mdev_17588-slave.opt
new file mode 100644
index 00000000000..19497afd22a
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_mdev_17588-slave.opt
@@ -0,0 +1 @@
+--replicate-do-table=test.t2 --replicate-do-table=test.t3 --sql-mode='NO_ENGINE_SUBSTITUTION'
diff --git a/mysql-test/suite/rpl/t/rpl_mdev_17588.test b/mysql-test/suite/rpl/t/rpl_mdev_17588.test
new file mode 100644
index 00000000000..c636077e4d1
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_mdev_17588.test
@@ -0,0 +1,29 @@
+--source include/master-slave.inc
+
+--connection master
+set sql_log_bin= 0;
+install soname 'ha_tokudb';
+set sql_log_bin= 1;
+
+create table t1 (a int) engine=TokuDB;
+create table t2 (a int);
+create table t3 (a int) engine=TokuDB;
+
+--connection slave
+let $slave_sql_errno= 1286;
+source include/wait_for_slave_sql_error.inc;
+--error ER_NO_SUCH_TABLE
+show create table t1;
+show create table t2;
+--error ER_NO_SUCH_TABLE
+show create table t3;
+SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
+START SLAVE;
+--connection master
+drop table t1, t2, t3;
+set sql_log_bin= 0;
+uninstall soname 'ha_tokudb';
+set sql_log_bin= 1;
+--sync_slave_with_master
+CALL mtr.add_suppression('Slave: Unknown storage engine .* Error_code: 1286');
+--source include/rpl_end.inc
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6640e02147a..4361700ee6c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -2802,6 +2802,7 @@ class THD :public Statement,
uint8 password; /* 0, 1 or 2 */
uint8 failed_com_change_user;
bool slave_thread;
+ char *rpl_unavailable_storage_plugin= NULL;
bool extra_port; /* If extra connection */
bool no_errors;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 6649c60f827..5f8c833c76f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2656,6 +2656,13 @@ mysql_execute_command(THD *thd)
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE),
MYF(0));
+ thd->rpl_unavailable_storage_plugin= NULL;
+ DBUG_RETURN(0);
+ }
+ if (thd->rpl_unavailable_storage_plugin)
+ {
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), thd->rpl_unavailable_storage_plugin);
+ thd->rpl_unavailable_storage_plugin= NULL;
DBUG_RETURN(0);
}
/*
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 5111f0690ab..0756028c34a 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2476,7 +2476,7 @@ create:
LEX *lex= thd->lex;
lex->current_select= &lex->select_lex;
if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) &&
- !lex->create_info.db_type)
+ !lex->create_info.db_type && !thd->rpl_unavailable_storage_plugin)
{
lex->create_info.use_default_db_type(thd);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -5791,9 +5791,15 @@ storage_engines:
else
{
if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
- my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str));
+ {
+ if (!thd->slave_thread)
+ my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str));
+ else
+ thd->rpl_unavailable_storage_plugin= $1.str;
+ }
$$= 0;
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ if (!thd->rpl_unavailable_storage_plugin)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_STORAGE_ENGINE,
ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE),
$1.str);
2
1

[Commits] 4e01bc8c963: MDEV-16240: Assertion `0' failed in row_sel_convert_mysql_key_to_innobase
by Oleksandr Byelkin 25 Apr '19
by Oleksandr Byelkin 25 Apr '19
25 Apr '19
revision-id: 4e01bc8c963d9513625dd984cd1aca24b8a7b516 (mariadb-10.2.23-79-g4e01bc8c963)
parent(s): 3dffdee667666df9ade9f2c458bf1ea495ffba02
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-04-25 18:02:31 +0200
message:
MDEV-16240: Assertion `0' failed in row_sel_convert_mysql_key_to_innobase
Set table in row ID position mode before using this function.
---
mysql-test/r/multi_update_innodb.result | 40 ++++++++++++++++++++++
mysql-test/t/multi_update_innodb.test | 49 +++++++++++++++++++++++++++
sql/handler.cc | 15 +++++++--
sql/item_subselect.cc | 9 +++--
sql/item_subselect.h | 2 +-
sql/log_event.cc | 7 ++++
sql/sql_insert.cc | 60 +++++++++++++++++++++++++++------
sql/sql_load.cc | 7 ++++
sql/sql_select.cc | 16 +++++++++
sql/sql_update.cc | 8 +++++
10 files changed, 197 insertions(+), 16 deletions(-)
diff --git a/mysql-test/r/multi_update_innodb.result b/mysql-test/r/multi_update_innodb.result
index 5890fd24f5f..294ebfcebdf 100644
--- a/mysql-test/r/multi_update_innodb.result
+++ b/mysql-test/r/multi_update_innodb.result
@@ -151,3 +151,43 @@ create table t2 like t1;
insert into t2 select * from t1;
delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
drop table t1,t2;
+#
+# MDEV-16240: Assertion `0' failed in
+# row_sel_convert_mysql_key_to_innobase
+#
+SET @save_sql_mode=@@sql_mode;
+set sql_mode='';
+CREATE TABLE `t3` (
+`f1` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE current_timestamp(),
+`f2` datetime DEFAULT '2000-01-01 00:00:00' ON UPDATE current_timestamp(),
+`f3` TIMESTAMP NULL DEFAULT '2000-01-01 00:00:00',
+`pk` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+`f4` datetime DEFAULT current_timestamp(),
+PRIMARY KEY (`pk`),
+UNIQUE KEY `f2k` (`f2`),
+KEY `f4k` (`f4`)
+) ENGINE=InnoDB;
+INSERT INTO `t3` VALUES ('2018-05-18 15:08:07','2018-05-18 17:08:07','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00'),('0000-00-00 00:00:00','0000-00-00 00:00:00','1999-12-31 23:00:00','2002-07-03 23:04:40','0000-00-00 00:00:00');
+CREATE VIEW `v1` AS
+SELECT `t3`.`pk` AS `pk`,
+`t3`.`f3` AS `f3`,
+`t3`.`f4` AS `f4`,
+`t3`.`f2` AS `f2`,
+`t3`.`f1` AS `f1`
+FROM `t3`;
+CREATE TABLE `t4` (
+`f1` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+`f3` timestamp NULL DEFAULT NULL,
+`f2` timestamp NULL DEFAULT '1999-12-31 23:00:00' ON UPDATE current_timestamp(),
+`pk` int(11) NOT NULL,
+`f4` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+PRIMARY KEY (`pk`)
+) ENGINE=InnoDB;
+INSERT INTO `t4` VALUES ('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,1,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,3,'2018-05-18 15:08:06'),('0000-00-00 00:00:00','0000-00-00 00:00:00',NULL,1976,'0000-00-00 00:00:00'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2000,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2001,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2002,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2003,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2004,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2005,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2018,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2019,'2018-05-1
8 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2024,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','1999-12-31 23:00:00',2025,'2018-05-18 15:08:06'),('0000-00-00 00:00:00',NULL,'2018-05-18 15:08:06',2026,'2018-05-18 15:08:06'),('2018-05-18 17:08:07','0000-00-00 00:00:00','0000-00-00 00:00:00',2027,'0000-00-00 00:00:00');
+UPDATE `v1` t1, `t4` t2
+SET t1.`f2` = 6452736 WHERE t1.`f4` = 6272000;
+ERROR 23000: Duplicate entry '0000-00-00 00:00:00' for key 'f2k'
+DROP VIEW v1;
+DROP TABLE t3,t4;
+SET @@sql_mode=@save_sql_mode;
+# End of 10.2 tests
diff --git a/mysql-test/t/multi_update_innodb.test b/mysql-test/t/multi_update_innodb.test
index f5f8f91edb8..2e46ee06d4d 100644
--- a/mysql-test/t/multi_update_innodb.test
+++ b/mysql-test/t/multi_update_innodb.test
@@ -173,3 +173,52 @@ delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
drop table t1,t2;
+--echo #
+--echo # MDEV-16240: Assertion `0' failed in
+--echo # row_sel_convert_mysql_key_to_innobase
+--echo #
+
+SET @save_sql_mode=@@sql_mode;
+set sql_mode='';
+
+CREATE TABLE `t3` (
+ `f1` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE current_timestamp(),
+ `f2` datetime DEFAULT '2000-01-01 00:00:00' ON UPDATE current_timestamp(),
+ `f3` TIMESTAMP NULL DEFAULT '2000-01-01 00:00:00',
+ `pk` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `f4` datetime DEFAULT current_timestamp(),
+ PRIMARY KEY (`pk`),
+ UNIQUE KEY `f2k` (`f2`),
+ KEY `f4k` (`f4`)
+ ) ENGINE=InnoDB;
+
+INSERT INTO `t3` VALUES ('2018-05-18 15:08:07','2018-05-18 17:08:07','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00'),('0000-00-00 00:00:00','0000-00-00 00:00:00','1999-12-31 23:00:00','2002-07-03 23:04:40','0000-00-00 00:00:00');
+
+CREATE VIEW `v1` AS
+SELECT `t3`.`pk` AS `pk`,
+ `t3`.`f3` AS `f3`,
+ `t3`.`f4` AS `f4`,
+ `t3`.`f2` AS `f2`,
+ `t3`.`f1` AS `f1`
+FROM `t3`;
+
+CREATE TABLE `t4` (
+ `f1` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+ `f3` timestamp NULL DEFAULT NULL,
+ `f2` timestamp NULL DEFAULT '1999-12-31 23:00:00' ON UPDATE current_timestamp(),
+ `pk` int(11) NOT NULL,
+ `f4` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+ PRIMARY KEY (`pk`)
+) ENGINE=InnoDB;
+
+INSERT INTO `t4` VALUES ('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,1,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,3,'2018-05-18 15:08:06'),('0000-00-00 00:00:00','0000-00-00 00:00:00',NULL,1976,'0000-00-00 00:00:00'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2000,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2001,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2002,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2003,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00',NULL,2004,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2005,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2018,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2019,'2018-05-1
8 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','2018-05-18 15:08:06',2024,'2018-05-18 15:08:06'),('2018-05-18 17:08:06','0000-00-00 00:00:00','1999-12-31 23:00:00',2025,'2018-05-18 15:08:06'),('0000-00-00 00:00:00',NULL,'2018-05-18 15:08:06',2026,'2018-05-18 15:08:06'),('2018-05-18 17:08:07','0000-00-00 00:00:00','0000-00-00 00:00:00',2027,'0000-00-00 00:00:00');
+
+--error ER_DUP_ENTRY
+UPDATE `v1` t1, `t4` t2
+SET t1.`f2` = 6452736 WHERE t1.`f4` = 6272000;
+
+DROP VIEW v1;
+DROP TABLE t3,t4;
+SET @@sql_mode=@save_sql_mode;
+
+--echo # End of 10.2 tests
diff --git a/sql/handler.cc b/sql/handler.cc
index ad1bad59efa..e5b452f9649 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -2649,8 +2649,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos)
DBUG_ENTER("handler::ha_rnd_pos");
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type != F_UNLCK);
- /* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */
- /* DBUG_ASSERT(inited == RND); */
+ DBUG_ASSERT(inited == RND);
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
{ result= rnd_pos(buf, pos); })
@@ -3308,6 +3307,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nr;
int error;
MY_BITMAP *old_read_set;
+ bool rnd_inited= (inited == RND);
+
+ if (rnd_inited && ha_rnd_end())
+ return;
old_read_set= table->prepare_for_keyread(table->s->next_number_index);
@@ -3317,6 +3320,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
DBUG_ASSERT(0);
(void) extra(HA_EXTRA_NO_KEYREAD);
*first_value= ULONGLONG_MAX;
+ if (rnd_inited && ha_rnd_init_with_error(0))
+ {
+ //TODO: it would be nice to return here an error
+ }
return;
}
@@ -3363,6 +3370,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
ha_index_end();
table->restore_column_maps_after_keyread(old_read_set);
*first_value= nr;
+ if (rnd_inited && ha_rnd_init_with_error(0))
+ {
+ //TODO: it would be nice to return here an error
+ }
return;
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 8a9dd083911..8cff8f3a5c4 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -5855,12 +5855,16 @@ Ordered_key::cmp_keys_by_row_data_and_rownum(Ordered_key *key,
}
-void Ordered_key::sort_keys()
+bool Ordered_key::sort_keys()
{
+ if (tbl->file->ha_rnd_init_with_error(0))
+ return TRUE;
my_qsort2(key_buff, (size_t) key_buff_elements, sizeof(rownum_t),
(qsort2_cmp) &cmp_keys_by_row_data_and_rownum, (void*) this);
/* Invalidate the current row position. */
cur_key_idx= HA_POS_ERROR;
+ tbl->file->ha_rnd_end();
+ return FALSE;
}
@@ -6313,7 +6317,8 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
/* Sort the keys in each of the indexes. */
for (uint i= 0; i < merge_keys_count; i++)
- merge_keys[i]->sort_keys();
+ if (merge_keys[i]->sort_keys())
+ return TRUE;
if (init_queue(&pq, merge_keys_count, 0, FALSE,
subselect_rowid_merge_engine::cmp_keys_by_cur_rownum, NULL,
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index bd6a1bdc498..5366c759795 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -1287,7 +1287,7 @@ class Ordered_key : public Sql_alloc
++cur_key_idx;
}
- void sort_keys();
+ bool sort_keys();
double null_selectivity();
/*
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 8990e1953b6..7ebc75dd1bf 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -12845,6 +12845,12 @@ Rows_log_event::write_row(rpl_group_info *rgi,
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
+
+ if ((error= table->file->ha_rnd_init_with_error(0)))
+ {
+ DBUG_RETURN(error);
+ }
+
error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
if (error)
{
@@ -12854,6 +12860,7 @@ Rows_log_event::write_row(rpl_group_info *rgi,
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
+ table->file->ha_rnd_end();
}
else
{
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index c623336fdba..4005153cb64 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -881,7 +881,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
#endif /* EMBEDDED_LIBRARY */
{
if (duplic != DUP_ERROR || ignore)
+ {
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
+ table->file->ha_rnd_init_with_error(0))
+ goto abort;
+ }
/**
This is a simple check for the case when the table has a trigger
that reads from it, or when the statement invokes a stored function
@@ -942,7 +947,10 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
{
DBUG_PRINT("info", ("iteration %llu", iteration));
if (iteration && bulk_parameters_set(thd))
- goto abort;
+ {
+ error= 1;
+ goto values_loop_end;
+ }
while ((values= its++))
{
@@ -1094,7 +1102,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error=1;
}
if (duplic != DUP_ERROR || ignore)
+ {
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ table->file->ha_rnd_end();
+ }
transactional_table= table->file->has_transactions();
@@ -1705,6 +1717,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto err;
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
+ DBUG_ASSERT(table->file->inited == handler::RND);
if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
goto err;
}
@@ -3177,6 +3190,9 @@ bool Delayed_insert::handle_inserts(void)
max_rows= ULONG_MAX; // Do as much as possible
}
+ if (table->file->ha_rnd_init_with_error(0))
+ goto err;
+
/*
We can't use row caching when using the binary log because if
we get a crash, then binary log will contain rows that are not yet
@@ -3352,6 +3368,8 @@ bool Delayed_insert::handle_inserts(void)
}
}
+ table->file->ha_rnd_end();
+
if (WSREP((&thd)))
thd_proc_info(&thd, "insert done");
else
@@ -3649,7 +3667,12 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
thd->cuted_fields=0;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
+ {
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
+ table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
+ }
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -3818,6 +3841,9 @@ bool select_insert::prepare_eof()
if (!error && thd->is_error())
error= thd->get_stmt_da()->sql_errno();
+ if (info.ignore || info.handle_duplicates != DUP_ERROR)
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ table->file->ha_rnd_end();
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -3929,6 +3955,11 @@ void select_insert::abort_result_set() {
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_end_bulk_insert();
+ if (table->file->inited)
+ table->file->ha_rnd_end();
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+
/*
If at least one row has been inserted/modified and will stay in
the table (the table doesn't have transactions) we must write to
@@ -4341,7 +4372,12 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
restore_record(table,s->default_values); // Get empty record
thd->cuted_fields=0;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
+ {
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
+ table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
+ }
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -4522,9 +4558,6 @@ bool select_create::send_eof()
*/
exit_done= 1; // Avoid double calls
- table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
-
send_ok_packet();
if (m_plock)
@@ -4600,13 +4633,6 @@ void select_create::abort_result_set()
thd->locked_tables_list.unlock_locked_table(thd,
create_info->mdl_ticket);
}
- if (m_plock)
- {
- mysql_unlock_tables(thd, *m_plock);
- *m_plock= NULL;
- m_plock= NULL;
- }
-
if (table)
{
bool tmp_table= table->s->tmp_table;
@@ -4617,9 +4643,21 @@ void select_create::abort_result_set()
thd->restore_tmp_table_share(saved_tmp_table_share);
}
+ if (table->file->inited &&
+ (info.ignore || info.handle_duplicates != DUP_ERROR) &&
+ (table->file->ha_table_flags() & HA_DUPLICATE_POS))
+ table->file->ha_rnd_end();
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;
+
+ if (m_plock)
+ {
+ mysql_unlock_tables(thd, *m_plock);
+ *m_plock= NULL;
+ m_plock= NULL;
+ }
+
drop_open_table(thd, table, create_table->db, create_table->table_name);
table=0; // Safety
if (thd->log_current_statement && mysql_bin_log.is_open())
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 49558f8b694..0fcc4efbccd 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -660,6 +660,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
+ if ((table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS) &&
+ (error= table_list->table->file->ha_rnd_init_with_error(0)))
+ goto err;
+
thd_progress_init(thd, 2);
if (table_list->table->validate_default_values_of_unset_fields(thd))
{
@@ -679,6 +683,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
set_fields, set_values, read_info,
*ex->enclosed, skip_lines, ignore);
+ if (table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ table_list->table->file->ha_rnd_end();
+
thd_proc_info(thd, "End bulk insert");
if (!error)
thd_progress_next_stage(thd);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3c1cea6be51..291560dc098 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -20318,6 +20318,15 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
+ /* Prepare table for random positioning */
+ bool rnd_inited= (table->file->inited == handler::RND);
+ if (!rnd_inited &&
+ ((error= table->file->ha_index_end()) ||
+ (error= table->file->ha_rnd_init(0))))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(NESTED_LOOP_ERROR);
+ }
if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
@@ -20331,6 +20340,13 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
+ if (!rnd_inited &&
+ ((error= table->file->ha_rnd_end()) ||
+ (error= table->file->ha_index_init(0, 0))))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(NESTED_LOOP_ERROR);
+ }
}
if (join->thd->check_killed())
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 80ecd820046..1da265d7c16 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -211,6 +211,14 @@ static void prepare_record_for_error_message(int error, TABLE *table)
bitmap_union(table->read_set, &unique_map);
/* Tell the engine about the new set. */
table->file->column_bitmaps_signal();
+
+ if ((error= table->file->ha_index_or_rnd_end()) ||
+ (error= table->file->ha_rnd_init(0)))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_VOID_RETURN;
+ }
+
/* Read record that is identified by table->file->ref. */
(void) table->file->ha_rnd_pos(table->record[1], table->file->ref);
/* Copy the newly read columns into the new record. */
1
0
revision-id: a738ed518dd7d0d0e2d699829e71c3c9fd49c690 (mariadb-10.2.23-80-ga738ed518dd)
parent(s): 2babe7a2adb2ed0d57eb7b44bd92113e64413bb6
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-04-25 18:02:04 +0200
message:
postreview
---
sql/handler.cc | 8 ++++++--
sql/log_event.cc | 6 ++----
sql/sql_load.cc | 6 ++----
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/sql/handler.cc b/sql/handler.cc
index 2c7c9d26324..e5b452f9649 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -3321,7 +3321,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
(void) extra(HA_EXTRA_NO_KEYREAD);
*first_value= ULONGLONG_MAX;
if (rnd_inited && ha_rnd_init_with_error(0))
- DBUG_ASSERT(0);
+ {
+ //TODO: it would be nice to return here an error
+ }
return;
}
@@ -3369,7 +3371,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
table->restore_column_maps_after_keyread(old_read_set);
*first_value= nr;
if (rnd_inited && ha_rnd_init_with_error(0))
- DBUG_ASSERT(0);
+ {
+ //TODO: it would be nice to return here an error
+ }
return;
}
diff --git a/sql/log_event.cc b/sql/log_event.cc
index c17c7ef6e06..7ebc75dd1bf 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -12845,11 +12845,9 @@ Rows_log_event::write_row(rpl_group_info *rgi,
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
- error= table->file->ha_rnd_init(0);
- if (error)
+
+ if ((error= table->file->ha_rnd_init_with_error(0)))
{
- DBUG_PRINT("info",("rnd_init() returns error %d",error));
- table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 8994b436cab..0fcc4efbccd 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -661,11 +661,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
if ((table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS) &&
- (error= table_list->table->file->ha_rnd_init(0)))
- {
- table->file->print_error(error, MYF(0));
+ (error= table_list->table->file->ha_rnd_init_with_error(0)))
goto err;
- }
+
thd_progress_init(thd, 2);
if (table_list->table->validate_default_values_of_unset_fields(thd))
{
1
0

[Commits] a0164e04f7e: MDEV-19334: bool is_eits_usable(Field*): Assertion `field->table->stats_is_read' failed.
by Varun 25 Apr '19
by Varun 25 Apr '19
25 Apr '19
revision-id: a0164e04f7e9c83c9162fdcb76f4f3629ce83f8a (mariadb-10.1.38-134-ga0164e04f7e)
parent(s): caa9023c9ed101acbcf6b9bd821a09daeb8271ee
author: Varun Gupta
committer: Varun Gupta
timestamp: 2019-04-25 18:18:26 +0530
message:
MDEV-19334: bool is_eits_usable(Field*): Assertion `field->table->stats_is_read' failed.
Fixed the assert by making sure that not to use EITS if the column statistics was not allocated
---
mysql-test/r/statistics.result | 14 ++++++++++++++
mysql-test/t/statistics.test | 15 +++++++++++++++
sql/sql_statistics.cc | 15 +++++++++++----
3 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/mysql-test/r/statistics.result b/mysql-test/r/statistics.result
index 295a9b34e49..135a0806bc3 100644
--- a/mysql-test/r/statistics.result
+++ b/mysql-test/r/statistics.result
@@ -1735,4 +1735,18 @@ rename table t1 to t2, t3 to t4;
ERROR 42S02: Table 'test.t3' doesn't exist
drop table t1, mysql.table_stats;
rename table test.table_stats to mysql.table_stats;
+#
+# MDEV-19334: bool is_eits_usable(Field*): Assertion `field->table->stats_is_read' failed.
+#
+create temporary table t1(a int);
+insert into t1 values (1),(2),(3);
+set use_stat_tables=preferably;
+set @optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity;
+set optimizer_use_condition_selectivity=4;
+select * from t1 where a >= 2;
+a
+2
+3
+drop table t1;
+set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity;
set use_stat_tables=@save_use_stat_tables;
diff --git a/mysql-test/t/statistics.test b/mysql-test/t/statistics.test
index e4f9870a622..7d2e7e898d3 100644
--- a/mysql-test/t/statistics.test
+++ b/mysql-test/t/statistics.test
@@ -809,4 +809,19 @@ rename table t1 to t2, t3 to t4;
drop table t1, mysql.table_stats;
rename table test.table_stats to mysql.table_stats;
+--echo #
+--echo # MDEV-19334: bool is_eits_usable(Field*): Assertion `field->table->stats_is_read' failed.
+--echo #
+
+create temporary table t1(a int);
+insert into t1 values (1),(2),(3);
+
+set use_stat_tables=preferably;
+set @optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity;
+set optimizer_use_condition_selectivity=4;
+
+select * from t1 where a >= 2;
+drop table t1;
+set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity;
+
set use_stat_tables=@save_use_stat_tables;
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index b435971a4d6..061622a2cd6 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -4067,6 +4067,14 @@ bool is_stat_table(const char *db, const char *table)
bool is_eits_usable(Field *field)
{
+ Column_statistics* col_stats= field->read_stats;
+
+ // check if column_statistics was allocated for this field
+ if (!col_stats)
+ return false;
+
+ DBUG_ASSERT(field->table->stats_is_read);
+
/*
(1): checks if we have EITS statistics for a particular column
(2): Don't use EITS for GEOMETRY columns
@@ -4074,10 +4082,9 @@ bool is_eits_usable(Field *field)
partition list of a table. We assume the selecticivity for
such columns would be handled during partition pruning.
*/
- DBUG_ASSERT(field->table->stats_is_read);
- Column_statistics* col_stats= field->read_stats;
- return col_stats && !col_stats->no_stat_values_provided() && //(1)
- field->type() != MYSQL_TYPE_GEOMETRY && //(2)
+
+ return !col_stats->no_stat_values_provided() && //(1)
+ field->type() != MYSQL_TYPE_GEOMETRY && //(2)
#ifdef WITH_PARTITION_STORAGE_ENGINE
(!field->table->part_info ||
!field->table->part_info->field_in_partition_expr(field)) && //(3)
1
0

[Commits] 0222f7b3988: MDEV-17036: BULK with replace doesn't take the first parameter in account
by Oleksandr Byelkin 25 Apr '19
by Oleksandr Byelkin 25 Apr '19
25 Apr '19
revision-id: 0222f7b3988496c7289321dc606967c888dc658e (mariadb-10.2.23-76-g0222f7b3988)
parent(s): bc145193c164b895a52b943e73fff53952d48a60
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-04-25 13:43:31 +0200
message:
MDEV-17036: BULK with replace doesn't take the first parameter in account
INSERT and REPLACE served by the same function, so flags (and processing) should be the same.
---
sql/sql_parse.cc | 3 ++-
tests/mysql_client_test.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 146c4d2d02e..68a08dc2b3f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -609,7 +609,8 @@ void init_update_queries(void)
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE |
CF_CAN_BE_EXPLAINED |
- CF_INSERTS_DATA | CF_SP_BULK_SAFE;
+ CF_INSERTS_DATA | CF_SP_BULK_SAFE |
+ CF_SP_BULK_OPTIMIZED;
sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_CAN_GENERATE_ROW_EVENTS |
CF_OPTIMIZER_TRACE |
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index be16db0fc9e..365229e126f 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -19743,6 +19743,66 @@ static void test_bulk_delete()
rc= mysql_query(mysql, "DROP TABLE t1");
myquery(rc);
}
+
+static void test_bulk_replace()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[2];
+ MYSQL_ROW row;
+ int i,
+ id[]= {1, 2, 3, 4},
+ val[]= {1, 1, 1, 1},
+ count= sizeof(id)/sizeof(id[0]);
+ MYSQL_RES *result;
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (id int not null primary key, active int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1, 0), (2, 0), (3, 0)");
+ myquery(rc);
+ verify_affected_rows(3);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "replace into t1 (id, active) values (?, ?)", -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].buffer = (void *)val;
+ bind[1].buffer_length = 0;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT active FROM t1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ i++;
+ DIE_IF(atoi(row[0]) != 1);
+ }
+ DIE_IF(i != 4);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
#endif
static struct my_tests_st my_tests[]= {
@@ -20026,6 +20086,7 @@ static struct my_tests_st my_tests[]= {
{ "test_mdev12579", test_mdev12579 },
#ifndef EMBEDDED_LIBRARY
{ "test_bulk_delete", test_bulk_delete },
+ { "test_bulk_replace", test_bulk_replace },
#endif
{ 0, 0 }
};
1
0
revision-id: 2736a0f1763d2554f346e873b16524e76a2a0692 (mariadb-10.2.23-76-g2736a0f1763)
parent(s): bc145193c164b895a52b943e73fff53952d48a60
author: Sujatha Sivakumar
committer: Sujatha Sivakumar
timestamp: 2019-04-25 12:18:22 +0530
message:
MDEV-17260: Memory leaks in mysqlbinlog
Problem:
========
The mysqlbinlog tool is leaking memory, causing failures in various tests when
compiling and testing with AddressSanitizer or LeakSanitizer like this:
cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source
make -j$(nproc)
cd mysql-test
ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto
Analysis:
=========
Two types of leaks were observed during above execution.
1) Leak in Log_event::read_log_event(char const*, unsigned int, char const**,
Format_description_log_event const*, char)
File: sql/log_event.cc:2150
For all row based replication events the memory which is allocated during
read_log_event is not freed after the event is processed. The event specific
memory has to be retained only when flashback option is enabled with
mysqlbinlog tool. In this case all the events are retained till the end
statement is received and they are processed in reverse order and they are
destroyed. But in the existing code all events are retained irrespective of
flashback mode. Hence the memory leaks are observed.
2) read_remote_annotate_event(unsigned char*, unsigned long, char const**)
File: client/mysqlbinlog.cc:194
In general the Annotate event is not printed immediately because all
subsequent rbr-events can be filtered away. Instead it will be printed
together with the first not filtered away Table map or the last rbr will be
processed. While reading remote annotate events memory is allocated for event
buffer and event's temp_buf is made to point to the allocated buffer as shown
below. The TRUE flag is used for doing proper cleanup using free_temp_buf().
i.e at the time of deletion of annotate event its destructor takes care of
clearing the temp_buf.
/*
Ensure the event->temp_buf is pointing to the allocated buffer.
(TRUE = free temp_buf on the event deletion)
*/
event->register_temp_buf((char*)event_buf, TRUE);
But existing code does the following when it receives a remote annotate_event.
if (remote_opt)
ev->temp_buf= 0;
That is code immediately sets temp_buf=0, because of which free_temp_buf()
call will return empty handed as it has lost the reference to the allocated
temporary buffer. This results in memory leak
Fix:
====
1) If not in flashback mode, destroy the memory for events once they are
processed.
2) Remove the ev->temp_buf=0 code for remote option. Let the proper cleanup to
be done as part of free_temp_buf().
---
client/mysqlbinlog.cc | 9 +++------
mysql-test/suite/binlog/r/flashback.result | 18 ++++++++++++++++++
mysql-test/suite/binlog/t/flashback.test | 30 +++++++++++++++++++++++++++++-
3 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index cfc05cbf794..3dad4fef67b 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -227,8 +227,7 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info)
if (annotate_event)
{
annotate_event->print(result_file, print_event_info);
- delete annotate_event; // the event should not be printed more than once
- annotate_event= 0;
+ free_annotate_event();
}
}
@@ -1465,7 +1464,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1478,7 +1477,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
if (print_row_event(print_event_info, ev, e->get_table_id(),
e->get_flags(Old_rows_log_event::STMT_END_F)))
goto err;
- if (!is_stmt_end)
+ if (opt_flashback && !is_stmt_end)
destroy_evt= FALSE;
break;
}
@@ -1539,8 +1538,6 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
}
}
- if (remote_opt)
- ev->temp_buf= 0;
if (destroy_evt) /* destroy it later if not set (ignored table map) */
delete ev;
}
diff --git a/mysql-test/suite/binlog/r/flashback.result b/mysql-test/suite/binlog/r/flashback.result
index c96eaebe838..0d189b735a3 100644
--- a/mysql-test/suite/binlog/r/flashback.result
+++ b/mysql-test/suite/binlog/r/flashback.result
@@ -674,6 +674,24 @@ world.city 563256876
DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
+# < CASE 7 >
+# Test Case for MDEV-17260
+#
+RESET MASTER;
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+# 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+6
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+# 0- Rows must be present
+include/assert.inc [Table t1 should have 0 rows.]
+# 6- Rows must be present upon restoring from flashback
+include/assert.inc [Table t1 should have six rows.]
+DROP TABLE t1;
SET binlog_format=statement;
Warnings:
Warning 1105 MariaDB Galera and flashback do not support binlog format: STATEMENT
diff --git a/mysql-test/suite/binlog/t/flashback.test b/mysql-test/suite/binlog/t/flashback.test
index 3fc8c44c60c..9782fa4ec83 100644
--- a/mysql-test/suite/binlog/t/flashback.test
+++ b/mysql-test/suite/binlog/t/flashback.test
@@ -335,8 +335,36 @@ DROP TABLE test.test;
DROP TABLE world.city;
DROP DATABASE world;
-## Clear
+--echo # < CASE 7 >
+--echo # Test Case for MDEV-17260
+--echo #
+
+RESET MASTER;
+
+CREATE TABLE t1 ( f INT PRIMARY KEY ) ENGINE=innodb;
+INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6);
+--echo # 6- Rows must be present
+SELECT COUNT(*) FROM t1;
+FLUSH LOGS;
+DELETE FROM t1;
+FLUSH LOGS;
+--echo # 0- Rows must be present
+--let $assert_cond= COUNT(*) = 0 FROM t1
+--let $assert_text= Table t1 should have 0 rows.
+--source include/assert.inc
+
+--exec $MYSQL_BINLOG -vv -B --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002> $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql
+--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback_7.sql;"
+
+--echo # 6- Rows must be present upon restoring from flashback
+--let $assert_cond= COUNT(*) = 6 FROM t1
+--let $assert_text= Table t1 should have six rows.
+--source include/assert.inc
+
+DROP TABLE t1;
+
+## Clear
SET binlog_format=statement;
--error ER_FLASHBACK_NOT_SUPPORTED
SET GLOBAL binlog_format=statement;
1
0