Commit ae03e16b authored by Alexander Nozdrin's avatar Alexander Nozdrin

A patch for Bug#11763413 (56115: SELECT doesn't work in

prepared statements with cursor protocol).

The problem was a bug in Materialized-cursor implementation.
Materialized_cursor::open() called send_result_metadata()
with items pointing to already closed table.

The fix is to send metadata when the table is still open.

NOTE: this is a "partial" fix: metadata are different with
and without --cursor-protocol, but that's a different large
problem, one indication of which is reported as Bug 24176.
parent 5ad3f443
...@@ -3731,5 +3731,19 @@ CREATE TABLE t1 (a INT); ...@@ -3731,5 +3731,19 @@ CREATE TABLE t1 (a INT);
BEGIN; BEGIN;
PREPARE stmt1 FROM "SELECT * FROM t1"; PREPARE stmt1 FROM "SELECT * FROM t1";
DROP TABLE t1; DROP TABLE t1;
#
# Bug#56115: invalid memory reads when PS selecting from
# information_schema tables
# Bug#58701: crash in Field::make_field, cursor-protocol
#
# NOTE: MTR should be run both with --ps-protocol and --cursor-protocol.
#
SELECT *
FROM (SELECT 1 UNION SELECT 2) t;
1
1
2
# #
# End of 6.0 tests. # End of 5.5 tests.
...@@ -3344,7 +3344,21 @@ connection default; ...@@ -3344,7 +3344,21 @@ connection default;
DROP TABLE t1; DROP TABLE t1;
disconnect con1; disconnect con1;
--echo
--echo #
--echo # Bug#56115: invalid memory reads when PS selecting from
--echo # information_schema tables
--echo # Bug#58701: crash in Field::make_field, cursor-protocol
--echo #
--echo # NOTE: MTR should be run both with --ps-protocol and --cursor-protocol.
--echo #
--echo
SELECT *
FROM (SELECT 1 UNION SELECT 2) t;
--echo # --echo #
--echo # End of 6.0 tests. --echo # End of 5.5 tests.
########################################################################### ###########################################################################
/* Copyright (C) 2005-2006 MySQL AB /* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -46,7 +46,7 @@ class Materialized_cursor: public Server_side_cursor ...@@ -46,7 +46,7 @@ class Materialized_cursor: public Server_side_cursor
public: public:
Materialized_cursor(select_result *result, TABLE *table); Materialized_cursor(select_result *result, TABLE *table);
int fill_item_list(THD *thd, List<Item> &send_result_set_metadata); int send_result_set_metadata(THD *thd, List<Item> &send_result_set_metadata);
virtual bool is_open() const { return table != 0; } virtual bool is_open() const { return table != 0; }
virtual int open(JOIN *join __attribute__((unused))); virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows); virtual void fetch(ulong num_rows);
...@@ -133,7 +133,13 @@ int mysql_open_cursor(THD *thd, select_result *result, ...@@ -133,7 +133,13 @@ int mysql_open_cursor(THD *thd, select_result *result,
if (rc) if (rc)
{ {
if (result_materialize->materialized_cursor) if (result_materialize->materialized_cursor)
{
/* Rollback metadata in the client-server protocol. */
result_materialize->abort_result_set();
delete result_materialize->materialized_cursor; delete result_materialize->materialized_cursor;
}
goto end; goto end;
} }
...@@ -142,6 +148,12 @@ int mysql_open_cursor(THD *thd, select_result *result, ...@@ -142,6 +148,12 @@ int mysql_open_cursor(THD *thd, select_result *result,
Materialized_cursor *materialized_cursor= Materialized_cursor *materialized_cursor=
result_materialize->materialized_cursor; result_materialize->materialized_cursor;
/*
NOTE: close_thread_tables() has been called in
mysql_execute_command(), so all tables except from the cursor
temporary table have been closed.
*/
if ((rc= materialized_cursor->open(0))) if ((rc= materialized_cursor->open(0)))
{ {
delete materialized_cursor; delete materialized_cursor;
...@@ -202,14 +214,16 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg, ...@@ -202,14 +214,16 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg,
/** /**
Preserve the original metadata that would be sent to the client. Preserve the original metadata to be sent to the client.
Initiate sending of the original metadata to the client
(call Protocol::send_result_set_metadata()).
@param thd Thread identifier. @param thd Thread identifier.
@param send_result_set_metadata List of fields that would be sent. @param send_result_set_metadata List of fields that would be sent.
*/ */
int Materialized_cursor::fill_item_list(THD *thd, int Materialized_cursor::send_result_set_metadata(
List<Item> &send_result_set_metadata) THD *thd, List<Item> &send_result_set_metadata)
{ {
Query_arena backup_arena; Query_arena backup_arena;
int rc; int rc;
...@@ -241,6 +255,14 @@ int Materialized_cursor::fill_item_list(THD *thd, ...@@ -241,6 +255,14 @@ int Materialized_cursor::fill_item_list(THD *thd,
ident->db_name= thd->strdup(send_field.db_name); ident->db_name= thd->strdup(send_field.db_name);
ident->table_name= thd->strdup(send_field.table_name); ident->table_name= thd->strdup(send_field.table_name);
} }
/*
Original metadata result set should be sent here. After
mysql_execute_command() is finished, item_list can not be used for
sending metadata, because it references closed table.
*/
rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
end: end:
thd->restore_active_arena(this, &backup_arena); thd->restore_active_arena(this, &backup_arena);
/* Check for thd->is_error() in case of OOM */ /* Check for thd->is_error() in case of OOM */
...@@ -253,31 +275,29 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused))) ...@@ -253,31 +275,29 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
THD *thd= fake_unit.thd; THD *thd= fake_unit.thd;
int rc; int rc;
Query_arena backup_arena; Query_arena backup_arena;
thd->set_n_backup_active_arena(this, &backup_arena); thd->set_n_backup_active_arena(this, &backup_arena);
/* Create a list of fields and start sequential scan */
/* Create a list of fields and start sequential scan. */
rc= result->prepare(item_list, &fake_unit); rc= result->prepare(item_list, &fake_unit);
if (!rc && !(rc= table->file->ha_rnd_init(TRUE))) rc= !rc && table->file->ha_rnd_init(TRUE);
is_rnd_inited= 1; is_rnd_inited= !rc;
thd->restore_active_arena(this, &backup_arena); thd->restore_active_arena(this, &backup_arena);
if (rc == 0)
/* Commit or rollback metadata in the client-server protocol. */
if (!rc)
{ {
/*
Now send the result set metadata to the client. We need to
do it here, as in Select_materialize::send_result_set_metadata the items
for column types are not yet created (send_result_set_metadata requires
a list of items). The new types may differ from the original
ones sent at prepare if some of them were altered by MySQL
HEAP tables mechanism -- used when create_tmp_field_from_item
may alter the original column type.
We can't simply supply SEND_EOF flag to send_result_set_metadata, because
send_result_set_metadata doesn't flush the network buffer.
*/
rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof(); result->send_eof();
} }
else
{
result->abort_result_set();
}
return rc; return rc;
} }
...@@ -370,13 +390,14 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags) ...@@ -370,13 +390,14 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
materialized_cursor= new (&table->mem_root) materialized_cursor= new (&table->mem_root)
Materialized_cursor(result, table); Materialized_cursor(result, table);
if (! materialized_cursor) if (!materialized_cursor)
{ {
free_tmp_table(table->in_use, table); free_tmp_table(table->in_use, table);
table= 0; table= 0;
return TRUE; return TRUE;
} }
if (materialized_cursor->fill_item_list(unit->thd, list))
if (materialized_cursor->send_result_set_metadata(unit->thd, list))
{ {
delete materialized_cursor; delete materialized_cursor;
table= 0; table= 0;
......
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -15694,8 +15694,11 @@ static void test_bug13488() ...@@ -15694,8 +15694,11 @@ static void test_bug13488()
check_execute(stmt1, rc); check_execute(stmt1, rc);
if (!opt_silent) if (!opt_silent)
printf("data is: %s", (f1 == 1 && f2 == 1 && f3 == 2)?"OK": {
"wrong"); printf("data: f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
printf("data is: %s\n",
(f1 == 1 && f2 == 1 && f3 == 2) ? "OK" : "wrong");
}
DIE_UNLESS(f1 == 1 && f2 == 1 && f3 == 2); DIE_UNLESS(f1 == 1 && f2 == 1 && f3 == 2);
rc= mysql_query(mysql, "drop table t1, t2"); rc= mysql_query(mysql, "drop table t1, t2");
myquery(rc); myquery(rc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment