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.
This commit is contained in:
Alexander Nozdrin 2011-03-22 14:48:56 +03:00
parent b629abf3ae
commit 7117e0635c
4 changed files with 81 additions and 29 deletions

View file

@ -3731,5 +3731,19 @@ CREATE TABLE t1 (a INT);
BEGIN;
PREPARE stmt1 FROM "SELECT * FROM t1";
DROP TABLE t1;
#
# End of 6.0 tests.
# 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 5.5 tests.

View file

@ -3344,7 +3344,21 @@ connection default;
DROP TABLE t1;
disconnect con1;
--echo
--echo #
--echo # End of 6.0 tests.
--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 # End of 5.5 tests.
###########################################################################

View file

@ -1,4 +1,4 @@
/* 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
it under the terms of the GNU General Public License as published by
@ -46,7 +46,7 @@ class Materialized_cursor: public Server_side_cursor
public:
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 int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
@ -133,7 +133,13 @@ int mysql_open_cursor(THD *thd, select_result *result,
if (rc)
{
if (result_materialize->materialized_cursor)
{
/* Rollback metadata in the client-server protocol. */
result_materialize->abort_result_set();
delete result_materialize->materialized_cursor;
}
goto end;
}
@ -142,6 +148,12 @@ int mysql_open_cursor(THD *thd, select_result *result,
Materialized_cursor *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)))
{
delete materialized_cursor;
@ -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 send_result_set_metadata List of fields that would be sent.
*/
int Materialized_cursor::fill_item_list(THD *thd,
List<Item> &send_result_set_metadata)
int Materialized_cursor::send_result_set_metadata(
THD *thd, List<Item> &send_result_set_metadata)
{
Query_arena backup_arena;
int rc;
@ -241,6 +255,14 @@ int Materialized_cursor::fill_item_list(THD *thd,
ident->db_name= thd->strdup(send_field.db_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:
thd->restore_active_arena(this, &backup_arena);
/* Check for thd->is_error() in case of OOM */
@ -253,31 +275,29 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
THD *thd= fake_unit.thd;
int rc;
Query_arena 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);
if (!rc && !(rc= table->file->ha_rnd_init(TRUE)))
is_rnd_inited= 1;
rc= !rc && table->file->ha_rnd_init(TRUE);
is_rnd_inited= !rc;
thd->restore_active_arena(this, &backup_arena);
if (rc == 0)
{
/*
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);
/* Commit or rollback metadata in the client-server protocol. */
if (!rc)
{
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
}
else
{
result->abort_result_set();
}
return rc;
}
@ -370,13 +390,14 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
materialized_cursor= new (&table->mem_root)
Materialized_cursor(result, table);
if (! materialized_cursor)
if (!materialized_cursor)
{
free_tmp_table(table->in_use, table);
table= 0;
return TRUE;
}
if (materialized_cursor->fill_item_list(unit->thd, list))
if (materialized_cursor->send_result_set_metadata(unit->thd, list))
{
delete materialized_cursor;
table= 0;

View file

@ -1,4 +1,4 @@
/* 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
it under the terms of the GNU General Public License as published by
@ -15694,8 +15694,11 @@ static void test_bug13488()
check_execute(stmt1, rc);
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);
rc= mysql_query(mysql, "drop table t1, t2");
myquery(rc);