diff --git a/client/mysqltest.cc b/client/mysqltest.cc index b1a5b3119a6..d1a1315a82c 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -8975,7 +8975,9 @@ static void run_query_stmt(MYSQL *mysql, struct st_command *command, // parameter markers. if (cursor_protocol_enabled) { // Use cursor when retrieving result. - unsigned long type = CURSOR_TYPE_READ_ONLY; + unsigned long type = search_protocol_re(&psc_re, query) + ? CURSOR_TYPE_READ_ONLY + : CURSOR_TYPE_NO_CURSOR; if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void *)&type)) die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); diff --git a/client/mysqltest/regular_expressions.cc b/client/mysqltest/regular_expressions.cc index 5e4ac20acb5..e70979ed661 100644 --- a/client/mysqltest/regular_expressions.cc +++ b/client/mysqltest/regular_expressions.cc @@ -88,6 +88,20 @@ static const char *const ps_re_str = "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; +/* + Filter for queries that can be run using the + MySQL Prepared Statements C API with C with cursor flags. + + Only DML statements may have assigned a cursor, others will get + ER_WRONG_ARGUMENTS with cursor. However, to really open a cursor, + the statement must return data. See sql_cmd->may_use_cursor(), + which limits to SELECT. If there is an opened cursor, the server + will respond with SERVER_STATUS_CURSOR_EXISTS in server_status. +*/ +static const char *const psc_re_str = + "^(" + "[[:space:]]*SELECT[[:space:]])"; + /* Filter for queries that can be run using the Stored procedures. @@ -117,6 +131,8 @@ static const char *const explain_re_str = /* Precompiled regular expressions. */ std::regex ps_re(ps_re_str, std::regex_constants::nosubs | std::regex_constants::icase); +std::regex psc_re(psc_re_str, + std::regex_constants::nosubs | std::regex_constants::icase); std::regex sp_re(sp_re_str, std::regex_constants::nosubs | std::regex_constants::icase); std::regex view_re(view_re_str, diff --git a/client/mysqltest/regular_expressions.h b/client/mysqltest/regular_expressions.h index d62ea205098..ba71baa50e0 100644 --- a/client/mysqltest/regular_expressions.h +++ b/client/mysqltest/regular_expressions.h @@ -32,6 +32,7 @@ extern std::regex explain_re; extern std::regex opt_trace_re; extern std::regex ps_re; +extern std::regex psc_re; extern std::regex sp_re; extern std::regex view_re; diff --git a/mysql-test/r/psc.result b/mysql-test/r/psc.result new file mode 100644 index 00000000000..6236f48e506 --- /dev/null +++ b/mysql-test/r/psc.result @@ -0,0 +1,28 @@ +SET SESSION DEBUG="+d,crash_real_open_cursor"; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +EXPLAIN SELECT 1 FROM t1; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 1 100.00 NULL +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t1` +SET SESSION DEBUG="-d,crash_real_open_cursor"; +FLUSH STATUS; +SELECT 1 FROM t1; +1 +1 +SHOW STATUS LIKE 'Created_tmp_tables'; +Variable_name Value +Created_tmp_tables 1 +SET SESSION DEBUG="+d,crash_real_open_cursor"; +UPDATE t1 SET c1 = 2; +DELETE FROM t1 WHERE c1 = 2; +CREATE PROCEDURE simple_proc() +BEGIN +SELECT 1 FROM t1; +END|; +CALL simple_proc(); +1 +DROP PROCEDURE simple_proc; +DROP TABLE t1; +SET SESSION DEBUG="-d,crash_real_open_cursor"; diff --git a/mysql-test/t/psc.test b/mysql-test/t/psc.test new file mode 100644 index 00000000000..d25db502a13 --- /dev/null +++ b/mysql-test/t/psc.test @@ -0,0 +1,40 @@ +--source include/have_debug.inc + +if (!$CURSOR_PROTOCOL) { + --skip Test requires --cursor-protocol +} + +SET SESSION DEBUG="+d,crash_real_open_cursor"; + +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); + +EXPLAIN SELECT 1 FROM t1; + +SET SESSION DEBUG="-d,crash_real_open_cursor"; + +# Verify real open cursor +FLUSH STATUS; +SELECT 1 FROM t1; +SHOW STATUS LIKE 'Created_tmp_tables'; + +SET SESSION DEBUG="+d,crash_real_open_cursor"; + +#--error ER_SP_BAD_CURSOR_SELECT +#SELECT 1 INTO @aux; + +UPDATE t1 SET c1 = 2; +DELETE FROM t1 WHERE c1 = 2; + +--delimiter |; +CREATE PROCEDURE simple_proc() +BEGIN +SELECT 1 FROM t1; +END|; +--delimiter ; +CALL simple_proc(); +DROP PROCEDURE simple_proc; + +DROP TABLE t1; + +SET SESSION DEBUG="-d,crash_real_open_cursor"; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 26e3e2c5739..1debaad1d6f 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -365,6 +365,8 @@ bool Materialized_cursor::open(THD *thd) { bool rc; Query_arena backup_arena; + DBUG_EXECUTE_IF("crash_real_open_cursor", DBUG_SUICIDE();); + thd->swap_query_arena(m_arena, &backup_arena); /* Create a list of fields and start sequential scan. */