diff --git a/mysql-test/main/show.result b/mysql-test/main/show.result index e453ee90c7e..f03fb6f6e4b 100644 --- a/mysql-test/main/show.result +++ b/mysql-test/main/show.result @@ -65,3 +65,37 @@ drop table t1; # # End of 10.3 tests # +# +# MDEV-31721: Cursor protocol increases the counter of "Empty_queries" for select +# +FLUSH STATUS; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1); +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT * FROM t1; +a +1 +SELECT * FROM t1 LIMIT 0; +a +SHOW STATUS LIKE "Empty_queries"; +Variable_name Value +Empty_queries 1 +DROP TABLE t1; +#------------------------------ +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (b INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t1 UNION SELECT * FROM t2; +a +1 +2 +SELECT * FROM t1 UNION SELECT * FROM t2 LIMIT 0; +a +SHOW STATUS LIKE "Empty_queries"; +Variable_name Value +Empty_queries 2 +DROP TABLE t1, t2; +# End of 10.11 tests diff --git a/mysql-test/main/show.test b/mysql-test/main/show.test index 37c30000e59..27b7ab39b0b 100644 --- a/mysql-test/main/show.test +++ b/mysql-test/main/show.test @@ -56,3 +56,40 @@ drop table t1; --echo # --echo # End of 10.3 tests --echo # + +--echo # +--echo # MDEV-31721: Cursor protocol increases the counter of "Empty_queries" for select +--echo # + +FLUSH STATUS; + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1); + +--disable_ps2_protocol +SELECT COUNT(*) FROM t1; +SELECT * FROM t1; +SELECT * FROM t1 LIMIT 0; +--enable_ps2_protocol + +SHOW STATUS LIKE "Empty_queries"; + +DROP TABLE t1; + +--echo #------------------------------ + +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (b INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); + +--disable_ps2_protocol +SELECT * FROM t1 UNION SELECT * FROM t2; +SELECT * FROM t1 UNION SELECT * FROM t2 LIMIT 0; +--enable_ps2_protocol + +SHOW STATUS LIKE "Empty_queries"; + +DROP TABLE t1, t2; + +--echo # End of 10.11 tests diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4b2d38449e1..2d99fa1c81f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -65,6 +65,7 @@ #include "lock.h" #include "wsrep_mysqld.h" #include "sql_connect.h" +#include "sql_cursor.h" //Select_materialize #ifdef WITH_WSREP #include "wsrep_thd.h" #include "wsrep_trans_observer.h" @@ -8737,3 +8738,9 @@ void Charset_loader_server::raise_not_applicable_error(const char *cs, { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), cl, cs); } + + +bool THD::is_cursor_execution() const +{ + return dynamic_cast(this->lex->result); +} diff --git a/sql/sql_class.h b/sql/sql_class.h index c8d21feb0e1..ba690001e9c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5850,6 +5850,17 @@ public: return false; return !is_set_timestamp_forbidden(this); } + + /** + @brief + Return true if current statement uses cursor protocol for execution. + + @details + Cursor protocol execution is determined by checking if lex->result is a + Select_materialize object, which is exclusively used by the server for + cursor result set materialization. + */ + bool is_cursor_execution() const; }; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 4ba082934f3..f17aceb1597 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -24,72 +24,6 @@ #include "probes_mysql.h" #include "sql_parse.h" // mysql_execute_command -/**************************************************************************** - Declarations. -****************************************************************************/ - -/** - Materialized_cursor -- an insensitive materialized server-side - cursor. The result set of this cursor is saved in a temporary - table at open. The cursor itself is simply an interface for the - handler of the temporary table. -*/ - -class Materialized_cursor: public Server_side_cursor -{ - MEM_ROOT main_mem_root; - /* A fake unit to supply to select_send when fetching */ - SELECT_LEX_UNIT fake_unit; - TABLE *table; - List item_list; - ulong fetch_limit; - ulong fetch_count; - bool is_rnd_inited; -public: - Materialized_cursor(select_result *result, TABLE *table); - - int send_result_set_metadata(THD *thd, List &send_result_set_metadata); - bool is_open() const override { return table != 0; } - int open(JOIN *join __attribute__((unused))) override; - void fetch(ulong num_rows) override; - void close() override; - bool export_structure(THD *thd, Row_definition_list *defs) override - { - return table->export_structure(thd, defs); - } - ~Materialized_cursor() override; - - void on_table_fill_finished(); -}; - - -/** - Select_materialize -- a mediator between a cursor query and the - protocol. In case we were not able to open a non-materialzed - cursor, it creates an internal temporary HEAP table, and insert - all rows into it. When the table reaches max_heap_table_size, - it's converted to a MyISAM table. Later this table is used to - create a Materialized_cursor. -*/ - -class Select_materialize: public select_unit -{ - select_result *result; /**< the result object of the caller (PS or SP) */ -public: - Materialized_cursor *materialized_cursor; - Select_materialize(THD *thd_arg, select_result *result_arg): - select_unit(thd_arg), result(result_arg), materialized_cursor(0) {} - bool send_result_set_metadata(List &list, uint flags) override; - bool send_eof() override { return false; } - bool view_structure_only() const override - { - return result->view_structure_only(); - } -}; - - -/**************************************************************************/ - /** Attempt to open a materialized cursor. diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index b9d0b41ea19..9c115c7d501 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -68,6 +68,66 @@ public: }; +/** + Materialized_cursor -- an insensitive materialized server-side + cursor. The result set of this cursor is saved in a temporary + table at open. The cursor itself is simply an interface for the + handler of the temporary table. +*/ + +class Materialized_cursor: public Server_side_cursor +{ + MEM_ROOT main_mem_root; + /* A fake unit to supply to select_send when fetching */ + SELECT_LEX_UNIT fake_unit; + TABLE *table; + List item_list; + ulong fetch_limit; + ulong fetch_count; + bool is_rnd_inited; +public: + Materialized_cursor(select_result *result, TABLE *table); + + int send_result_set_metadata(THD *thd, List &send_result_set_metadata); + bool is_open() const override { return table != 0; } + int open(JOIN *join __attribute__((unused))) override; + void fetch(ulong num_rows) override; + void close() override; + bool export_structure(THD *thd, Row_definition_list *defs) override + { + return table->export_structure(thd, defs); + } + ~Materialized_cursor() override; + + void on_table_fill_finished(); +}; + + +/** + Select_materialize -- a mediator between a cursor query and the + protocol. In case we were not able to open a non-materialzed + cursor, it creates an internal temporary HEAP table, and insert + all rows into it. When the table reaches max_heap_table_size, + it's converted to a MyISAM table. Later this table is used to + create a Materialized_cursor. +*/ + +class Select_materialize: public select_unit +{ + select_result *result; /**< the result object of the caller (PS or SP) */ +public: + Materialized_cursor *materialized_cursor; + Select_materialize(THD *thd_arg, select_result *result_arg): + select_unit(thd_arg), result(result_arg), materialized_cursor(0) {} + bool send_result_set_metadata(List &list, uint flags) override; + bool send_eof() override { return false; } + bool view_structure_only() const override + { + return result->view_structure_only(); + } +}; + + int mysql_open_cursor(THD *thd, select_result *result, Server_side_cursor **res); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3b9ac4a7c79..e74212f90f4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -56,6 +56,7 @@ #include "sql_test.h" // mysql_print_status #include "sql_select.h" // handle_select, mysql_select, // mysql_explain_union +#include "sql_cursor.h" // Select_materialzie #include "sql_load.h" // mysql_load #include "sql_servers.h" // create_servers, alter_servers, // drop_servers, servers_reload @@ -6443,7 +6444,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) } } /* Count number of empty select queries */ - if (!thd->get_sent_row_count() && !res) + if (!thd->is_cursor_execution() && !thd->get_sent_row_count() && !res) status_var_increment(thd->status_var.empty_queries); else status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count()); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f9698cc11a5..f9a9fb45ae7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3767,6 +3767,9 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length) cursor->fetch(num_rows); + if (!thd->get_sent_row_count()) + status_var_increment(thd->status_var.empty_queries); + if (!cursor->is_open()) { stmt->close_cursor();