Skip to content

Commit 2c7cea2

Browse files
MDEV-31721: Cursor protocol increases the counter of "Empty_queries" for select
Problem: Empty queries are incremented if no rows are sent to the client in the EXECUTE phase of select query. With cursor protocol, rows are not sent during EXECUTE phase; they are sent later in FETCH phase. Hence, queries executed with cursor protocol are always falsely treated as empty in EXECUTE phase. Fix: For cursor protocol, empty queries are now counted during the FETCH phase. This ensures counter correctly reflects whether any rows were actually sent to the client. Tests included in `mysql-test/main/show.test`.
1 parent 7ab205b commit 2c7cea2

File tree

8 files changed

+154
-67
lines changed

8 files changed

+154
-67
lines changed

mysql-test/main/show.result

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,37 @@ drop table t1;
6565
#
6666
# End of 10.3 tests
6767
#
68+
#
69+
# MDEV-31721: Cursor protocol increases the counter of "Empty_queries" for select
70+
#
71+
FLUSH STATUS;
72+
CREATE TABLE t1 (a INT);
73+
INSERT INTO t1 VALUES (1);
74+
SELECT COUNT(*) FROM t1;
75+
COUNT(*)
76+
1
77+
SELECT * FROM t1;
78+
a
79+
1
80+
SELECT * FROM t1 LIMIT 0;
81+
a
82+
SHOW STATUS LIKE "Empty_queries";
83+
Variable_name Value
84+
Empty_queries 1
85+
DROP TABLE t1;
86+
#------------------------------
87+
CREATE TABLE t1 (a INT);
88+
CREATE TABLE t2 (b INT);
89+
INSERT INTO t1 VALUES (1);
90+
INSERT INTO t2 VALUES (2);
91+
SELECT * FROM t1 UNION SELECT * FROM t2;
92+
a
93+
1
94+
2
95+
SELECT * FROM t1 UNION SELECT * FROM t2 LIMIT 0;
96+
a
97+
SHOW STATUS LIKE "Empty_queries";
98+
Variable_name Value
99+
Empty_queries 2
100+
DROP TABLE t1, t2;
101+
# End of 10.11 tests

mysql-test/main/show.test

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,40 @@ drop table t1;
5656
--echo #
5757
--echo # End of 10.3 tests
5858
--echo #
59+
60+
--echo #
61+
--echo # MDEV-31721: Cursor protocol increases the counter of "Empty_queries" for select
62+
--echo #
63+
64+
FLUSH STATUS;
65+
66+
CREATE TABLE t1 (a INT);
67+
INSERT INTO t1 VALUES (1);
68+
69+
--disable_ps2_protocol
70+
SELECT COUNT(*) FROM t1;
71+
SELECT * FROM t1;
72+
SELECT * FROM t1 LIMIT 0;
73+
--enable_ps2_protocol
74+
75+
SHOW STATUS LIKE "Empty_queries";
76+
77+
DROP TABLE t1;
78+
79+
--echo #------------------------------
80+
81+
CREATE TABLE t1 (a INT);
82+
CREATE TABLE t2 (b INT);
83+
INSERT INTO t1 VALUES (1);
84+
INSERT INTO t2 VALUES (2);
85+
86+
--disable_ps2_protocol
87+
SELECT * FROM t1 UNION SELECT * FROM t2;
88+
SELECT * FROM t1 UNION SELECT * FROM t2 LIMIT 0;
89+
--enable_ps2_protocol
90+
91+
SHOW STATUS LIKE "Empty_queries";
92+
93+
DROP TABLE t1, t2;
94+
95+
--echo # End of 10.11 tests

sql/sql_class.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "lock.h"
6666
#include "wsrep_mysqld.h"
6767
#include "sql_connect.h"
68+
#include "sql_cursor.h" //Select_materialize
6869
#ifdef WITH_WSREP
6970
#include "wsrep_thd.h"
7071
#include "wsrep_trans_observer.h"
@@ -8737,3 +8738,9 @@ void Charset_loader_server::raise_not_applicable_error(const char *cs,
87378738
{
87388739
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), cl, cs);
87398740
}
8741+
8742+
8743+
bool THD::is_cursor_execution() const
8744+
{
8745+
return dynamic_cast<Select_materialize*>(this->lex->result);
8746+
}

sql/sql_class.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5850,6 +5850,17 @@ class THD: public THD_count, /* this must be first */
58505850
return false;
58515851
return !is_set_timestamp_forbidden(this);
58525852
}
5853+
5854+
/**
5855+
@brief
5856+
Return true if current statement uses cursor protocol for execution.
5857+
5858+
@details
5859+
Cursor protocol execution is determined by checking if lex->result is a
5860+
Select_materialize object, which is exclusively used by the server for
5861+
cursor result set materialization.
5862+
*/
5863+
bool is_cursor_execution() const;
58535864
};
58545865

58555866

sql/sql_cursor.cc

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -24,72 +24,6 @@
2424
#include "probes_mysql.h"
2525
#include "sql_parse.h" // mysql_execute_command
2626

27-
/****************************************************************************
28-
Declarations.
29-
****************************************************************************/
30-
31-
/**
32-
Materialized_cursor -- an insensitive materialized server-side
33-
cursor. The result set of this cursor is saved in a temporary
34-
table at open. The cursor itself is simply an interface for the
35-
handler of the temporary table.
36-
*/
37-
38-
class Materialized_cursor: public Server_side_cursor
39-
{
40-
MEM_ROOT main_mem_root;
41-
/* A fake unit to supply to select_send when fetching */
42-
SELECT_LEX_UNIT fake_unit;
43-
TABLE *table;
44-
List<Item> item_list;
45-
ulong fetch_limit;
46-
ulong fetch_count;
47-
bool is_rnd_inited;
48-
public:
49-
Materialized_cursor(select_result *result, TABLE *table);
50-
51-
int send_result_set_metadata(THD *thd, List<Item> &send_result_set_metadata);
52-
bool is_open() const override { return table != 0; }
53-
int open(JOIN *join __attribute__((unused))) override;
54-
void fetch(ulong num_rows) override;
55-
void close() override;
56-
bool export_structure(THD *thd, Row_definition_list *defs) override
57-
{
58-
return table->export_structure(thd, defs);
59-
}
60-
~Materialized_cursor() override;
61-
62-
void on_table_fill_finished();
63-
};
64-
65-
66-
/**
67-
Select_materialize -- a mediator between a cursor query and the
68-
protocol. In case we were not able to open a non-materialzed
69-
cursor, it creates an internal temporary HEAP table, and insert
70-
all rows into it. When the table reaches max_heap_table_size,
71-
it's converted to a MyISAM table. Later this table is used to
72-
create a Materialized_cursor.
73-
*/
74-
75-
class Select_materialize: public select_unit
76-
{
77-
select_result *result; /**< the result object of the caller (PS or SP) */
78-
public:
79-
Materialized_cursor *materialized_cursor;
80-
Select_materialize(THD *thd_arg, select_result *result_arg):
81-
select_unit(thd_arg), result(result_arg), materialized_cursor(0) {}
82-
bool send_result_set_metadata(List<Item> &list, uint flags) override;
83-
bool send_eof() override { return false; }
84-
bool view_structure_only() const override
85-
{
86-
return result->view_structure_only();
87-
}
88-
};
89-
90-
91-
/**************************************************************************/
92-
9327
/**
9428
Attempt to open a materialized cursor.
9529

sql/sql_cursor.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,66 @@ class Server_side_cursor: protected Query_arena
6868
};
6969

7070

71+
/**
72+
Materialized_cursor -- an insensitive materialized server-side
73+
cursor. The result set of this cursor is saved in a temporary
74+
table at open. The cursor itself is simply an interface for the
75+
handler of the temporary table.
76+
*/
77+
78+
class Materialized_cursor: public Server_side_cursor
79+
{
80+
MEM_ROOT main_mem_root;
81+
/* A fake unit to supply to select_send when fetching */
82+
SELECT_LEX_UNIT fake_unit;
83+
TABLE *table;
84+
List<Item> item_list;
85+
ulong fetch_limit;
86+
ulong fetch_count;
87+
bool is_rnd_inited;
88+
public:
89+
Materialized_cursor(select_result *result, TABLE *table);
90+
91+
int send_result_set_metadata(THD *thd, List<Item> &send_result_set_metadata);
92+
bool is_open() const override { return table != 0; }
93+
int open(JOIN *join __attribute__((unused))) override;
94+
void fetch(ulong num_rows) override;
95+
void close() override;
96+
bool export_structure(THD *thd, Row_definition_list *defs) override
97+
{
98+
return table->export_structure(thd, defs);
99+
}
100+
~Materialized_cursor() override;
101+
102+
void on_table_fill_finished();
103+
};
104+
105+
106+
/**
107+
Select_materialize -- a mediator between a cursor query and the
108+
protocol. In case we were not able to open a non-materialzed
109+
cursor, it creates an internal temporary HEAP table, and insert
110+
all rows into it. When the table reaches max_heap_table_size,
111+
it's converted to a MyISAM table. Later this table is used to
112+
create a Materialized_cursor.
113+
*/
114+
115+
class Select_materialize: public select_unit
116+
{
117+
select_result *result; /**< the result object of the caller (PS or SP) */
118+
public:
119+
Materialized_cursor *materialized_cursor;
120+
Select_materialize(THD *thd_arg, select_result *result_arg):
121+
select_unit(thd_arg), result(result_arg), materialized_cursor(0) {}
122+
bool send_result_set_metadata(List<Item> &list, uint flags) override;
123+
bool send_eof() override { return false; }
124+
bool view_structure_only() const override
125+
{
126+
return result->view_structure_only();
127+
}
128+
};
129+
130+
71131
int mysql_open_cursor(THD *thd, select_result *result,
72132
Server_side_cursor **res);
73133

sql/sql_parse.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include "sql_test.h" // mysql_print_status
5757
#include "sql_select.h" // handle_select, mysql_select,
5858
// mysql_explain_union
59+
#include "sql_cursor.h" // Select_materialzie
5960
#include "sql_load.h" // mysql_load
6061
#include "sql_servers.h" // create_servers, alter_servers,
6162
// drop_servers, servers_reload
@@ -6443,7 +6444,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
64436444
}
64446445
}
64456446
/* Count number of empty select queries */
6446-
if (!thd->get_sent_row_count() && !res)
6447+
if (!thd->is_cursor_execution() && !thd->get_sent_row_count() && !res)
64476448
status_var_increment(thd->status_var.empty_queries);
64486449
else
64496450
status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());

sql/sql_prepare.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3767,6 +3767,9 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
37673767

37683768
cursor->fetch(num_rows);
37693769

3770+
if (!thd->get_sent_row_count())
3771+
status_var_increment(thd->status_var.empty_queries);
3772+
37703773
if (!cursor->is_open())
37713774
{
37723775
stmt->close_cursor();

0 commit comments

Comments
 (0)