mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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`.
This commit is contained in:
committed by
Raghunandan Bhat
parent
7ab205b009
commit
2c7cea28da
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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<Select_materialize*>(this->lex->result);
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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> 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<Item> &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<Item> &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.
|
||||
|
||||
|
@@ -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> 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<Item> &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<Item> &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);
|
||||
|
||||
|
@@ -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());
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user