From a399fefd565b333078301f4d7406a2bf632bfb19 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Feb 2008 16:45:24 -0300 Subject: Bug#32265 Server returns different metadata if prepared statement is used Executing a prepared statement associated with a materialized cursor yields to the client a metadata packet with wrong table and database names. The problem was occurring because the server was sending the the name of the temporary table used by the cursor instead of the table name of the original table. The same problem occurs when selecting from views, in which case the table name was being sent and not the name of the view. The solution is to fill the list item from the temporary table but preserving the table and database names of the original fields. This is achieved by tweaking the Select_materialize to accept a pointer to the Materialized_cursor class which contains the item list to be filled. sql/sql_cursor.cc: Fill the item list in the send_fields method and preserve the table and database name of the fields. tests/mysql_client_test.c: Add test case for Bug#32265 --- sql/sql_cursor.cc | 98 ++++++++++++++++++++++++++++++++++++----------- tests/mysql_client_test.c | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 22 deletions(-) diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 2e98da42be1..c2345f1f2cd 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -88,6 +88,7 @@ class Materialized_cursor: public Server_side_cursor public: Materialized_cursor(select_result *result, TABLE *table); + int fill_item_list(THD *thd, List &send_fields); virtual bool is_open() const { return table != 0; } virtual int open(JOIN *join __attribute__((unused))); virtual void fetch(ulong num_rows); @@ -109,6 +110,7 @@ class Select_materialize: public select_union { select_result *result; /* the result object of the caller (PS or SP) */ public: + Materialized_cursor *materialized_cursor; Select_materialize(select_result *result_arg) :result(result_arg) {} virtual bool send_fields(List &list, uint flags); }; @@ -152,7 +154,7 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result))) { - delete result; + delete result_materialize; return 1; } @@ -174,13 +176,13 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, /* Possible options here: - a sensitive cursor is open. In this case rc is 0 and - result_materialize->table is NULL, or + result_materialize->materialized_cursor is NULL, or - a materialized cursor is open. In this case rc is 0 and - result_materialize->table is not NULL - - an error occured during materializaton. - result_materialize->table is not NULL, but rc != 0 + result_materialize->materialized is not NULL + - an error occurred during materialization. + result_materialize->materialized_cursor is not NULL, but rc != 0 - successful completion of mysql_execute_command without - a cursor: rc is 0, result_materialize->table is NULL, + a cursor: rc is 0, result_materialize->materialized_cursor is NULL, sensitive_cursor is not open. This is possible if some command writes directly to the network, bypassing select_result mechanism. An example of @@ -191,7 +193,7 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, if (sensitive_cursor->is_open()) { - DBUG_ASSERT(!result_materialize->table); + DBUG_ASSERT(!result_materialize->materialized_cursor); /* It's safer if we grab THD state after mysql_execute_command is finished and not in Sensitive_cursor::open(), because @@ -202,18 +204,10 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, *pcursor= sensitive_cursor; goto end; } - else if (result_materialize->table) + else if (result_materialize->materialized_cursor) { - Materialized_cursor *materialized_cursor; - TABLE *table= result_materialize->table; - MEM_ROOT *mem_root= &table->mem_root; - - if (!(materialized_cursor= new (mem_root) - Materialized_cursor(result, table))) - { - rc= 1; - goto err_open; - } + Materialized_cursor *materialized_cursor= + result_materialize->materialized_cursor; if ((rc= materialized_cursor->open(0))) { @@ -229,8 +223,6 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, err_open: DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open())); delete sensitive_cursor; - if (result_materialize->table) - free_tmp_table(thd, result_materialize->table); end: delete result_materialize; return rc; @@ -544,6 +536,51 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg, } +/** + Preserve the original metadata that would be sent to the client. + + @param thd Thread identifier. + @param send_fields List of fields that would be sent. +*/ + +int Materialized_cursor::fill_item_list(THD *thd, List &send_fields) +{ + Query_arena backup_arena; + int rc; + List_iterator_fast it_org(send_fields); + List_iterator_fast it_dst(item_list); + Item *item_org; + Item *item_dst; + + thd->set_n_backup_active_arena(this, &backup_arena); + + if ((rc= table->fill_item_list(&item_list))) + goto end; + + DBUG_ASSERT(send_fields.elements == item_list.elements); + + /* + Unless we preserve the original metadata, it will be lost, + since new fields describe columns of the temporary table. + Allocate a copy of the name for safety only. Currently + items with original names are always kept in memory, + but in case this changes a memory leak may be hard to notice. + */ + while ((item_dst= it_dst++, item_org= it_org++)) + { + Send_field send_field; + Item_ident *ident= static_cast(item_dst); + item_org->make_field(&send_field); + + ident->db_name= thd->strdup(send_field.db_name); + ident->table_name= thd->strdup(send_field.table_name); + } +end: + thd->restore_active_arena(this, &backup_arena); + /* Check for thd->is_error() in case of OOM */ + return rc || thd->net.report_error; +} + int Materialized_cursor::open(JOIN *join __attribute__((unused))) { THD *thd= fake_unit.thd; @@ -552,8 +589,7 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused))) thd->set_n_backup_active_arena(this, &backup_arena); /* Create a list of fields and start sequential scan */ - rc= (table->fill_item_list(&item_list) || - result->prepare(item_list, &fake_unit) || + rc= (result->prepare(item_list, &fake_unit) || table->file->ha_rnd_init(TRUE)); thd->restore_active_arena(this, &backup_arena); if (rc == 0) @@ -664,6 +700,24 @@ bool Select_materialize::send_fields(List &list, uint flags) if (create_result_table(unit->thd, unit->get_unit_column_types(), FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "")) return TRUE; + + materialized_cursor= new (&table->mem_root) + Materialized_cursor(result, table); + + if (! materialized_cursor) + { + free_tmp_table(table->in_use, table); + table= 0; + return TRUE; + } + if (materialized_cursor->fill_item_list(unit->thd, list)) + { + delete materialized_cursor; + table= 0; + materialized_cursor= 0; + return TRUE; + } + return FALSE; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 9cc2af25529..bb264ba8a53 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16152,6 +16152,87 @@ static void test_bug31669() DBUG_VOID_RETURN; } + +/** + Bug#32265 Server returns different metadata if prepared statement is used +*/ + +static void test_bug32265() +{ + int rc, i; + MYSQL_STMT *stmt; + MYSQL_FIELD *field; + + DBUG_ENTER("test_bug32265"); + myheader("test_bug32265"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)"); + myquery(rc); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + myquery(rc); + rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT * FROM t1"); + myquery(rc); + + stmt= open_cursor("SELECT * FROM t1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "t1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT * FROM v1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "v1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT * FROM v1 /* SIC */ GROUP BY 1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "v1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + /* Read and parse arguments and MySQL options from my.cnf */ @@ -16446,6 +16527,7 @@ static struct my_tests_st my_tests[]= { { "test_bug29948", test_bug29948 }, { "test_bug29306", test_bug29306 }, { "test_bug31669", test_bug31669 }, + { "test_bug32265", test_bug32265 }, { 0, 0 } }; -- cgit v1.2.1 From d34f8384b3ac5e6d03da17b70877d77a07493e82 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Feb 2008 22:11:06 -0300 Subject: Post-merge fix to silence a compilation warning introduced by patch for bug 32265 . tests/mysql_client_test.c: Removed unused variable. --- tests/mysql_client_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index bb264ba8a53..25522a88445 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16159,7 +16159,7 @@ static void test_bug31669() static void test_bug32265() { - int rc, i; + int rc; MYSQL_STMT *stmt; MYSQL_FIELD *field; -- cgit v1.2.1 From fa08b280857fde69a70c68bd07797624a52ef262 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Feb 2008 12:17:32 +0300 Subject: Fix for Bug#34337: Server crash when Altering a view using a table name. The problem was that fill_defined_view_parts() did not return an error if a table is going to be altered. That happened if the table was already in the table cache. In that case, open_table() returned non-NULL value (valid TABLE-instance from the cache). The fix is to ensure that an error is thrown even if the table is in the cache. (This is a backport of the original patch for 5.1) mysql-test/r/view.result: Fix result file. mysql-test/r/view_grant.result: Fix result file. mysql-test/t/view.test: Add a test case for Bug#34337: Server crash when Altering a view using a table name. mysql-test/t/view_grant.test: Fix order-dependency. sql/sql_view.cc: Report an error if we're going to work with a table. --- mysql-test/r/view.result | 21 ++++++++++++++++++++- mysql-test/r/view_grant.result | 1 + mysql-test/t/view.test | 36 +++++++++++++++++++++++++++++++++++- mysql-test/t/view_grant.test | 1 + sql/sql_view.cc | 29 ++++++++++++++++++++++++++--- 5 files changed, 83 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index fb36304e562..633278a9781 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3618,4 +3618,23 @@ ERROR HY000: Field of view 'test.v1' underlying table doesn't have a default val set @@sql_mode=@old_mode; drop view v1; drop table t1; -End of 5.0 tests. +# ----------------------------------------------------------------- +# -- Bug#34337: Server crash when Altering a view using a table name. +# ----------------------------------------------------------------- + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1(c1 INT); + +SELECT * FROM t1; +c1 +ALTER ALGORITHM=TEMPTABLE SQL SECURITY INVOKER VIEW t1 (c2) AS SELECT (1); +ERROR HY000: 'test.t1' is not VIEW + +DROP TABLE t1; + +# -- End of test case for Bug#34337. + +# ----------------------------------------------------------------- +# -- End of 5.0 tests. +# ----------------------------------------------------------------- diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index eef61c65fb8..53ad8642ba4 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -467,6 +467,7 @@ use test; REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; drop database mysqltest; drop view if exists v1; +drop table if exists t1; create table t1 as select * from mysql.user where user=''; delete from mysql.user where user=''; flush privileges; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 340a34db5a1..5a87128f69e 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3470,5 +3470,39 @@ insert into v1 values(1); set @@sql_mode=@old_mode; drop view v1; drop table t1; ---echo End of 5.0 tests. +########################################################################### + +--echo # ----------------------------------------------------------------- +--echo # -- Bug#34337: Server crash when Altering a view using a table name. +--echo # ----------------------------------------------------------------- +--echo + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo + +CREATE TABLE t1(c1 INT); + +--echo + +SELECT * FROM t1; + +--error ER_WRONG_OBJECT +ALTER ALGORITHM=TEMPTABLE SQL SECURITY INVOKER VIEW t1 (c2) AS SELECT (1); + +--echo + +DROP TABLE t1; + +--echo +--echo # -- End of test case for Bug#34337. +--echo + +########################################################################### + +--echo # ----------------------------------------------------------------- +--echo # -- End of 5.0 tests. +--echo # ----------------------------------------------------------------- diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 7f9eb4e1cff..be9daacec4f 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -608,6 +608,7 @@ drop database mysqltest; # --disable_warnings drop view if exists v1; +drop table if exists t1; --enable_warnings # Backup anonymous users and remove them. (They get in the way of diff --git a/sql/sql_view.cc b/sql/sql_view.cc index dd0b92a06f8..48ab5b3af9e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -182,10 +182,33 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) TABLE_LIST decoy; memcpy (&decoy, view, sizeof (TABLE_LIST)); - if (!open_table(thd, &decoy, thd->mem_root, ¬_used, OPEN_VIEW_NO_PARSE) && - !decoy.view) + + /* + Let's reset decoy.view before calling open_table(): when we start + supporting ALTER VIEW in PS/SP that may save us from a crash. + */ + + decoy.view= NULL; + + /* + open_table() will return NULL if 'decoy' is idenitifying a view *and* + there is no TABLE object for that view in the table cache. However, + decoy.view will be set to 1. + + If there is a TABLE-instance for the oject identified by 'decoy', + open_table() will return that instance no matter if it is a table or + a view. + + Thus, there is no need to check for the return value of open_table(), + since the return value itself does not mean anything. + */ + + open_table(thd, &decoy, thd->mem_root, ¬_used, OPEN_VIEW_NO_PARSE); + + if (!decoy.view) { - /* It's a table */ + /* It's a table. */ + my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); return TRUE; } -- cgit v1.2.1 From 315665cf67dd4e56922955b051eff2c384f2298e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Feb 2008 14:58:29 -0300 Subject: Bug#32890 Crash after repeated create and drop of tables and views The problem is that CREATE VIEW statements inside prepared statements weren't being expanded during the prepare phase, which leads to objects not being allocated in the appropriate memory arenas. The solution is to perform the validation of CREATE VIEW statements during the prepare phase of a prepared statement. The validation during the prepare phase assures that transformations of the parsed tree will use the permanent arena of the prepared statement. mysql-test/r/ps.result: Add test case result for Bug#32890 mysql-test/t/ps.test: Add test case for Bug#32890 sql/item.h: Restore original field name if name is auto generated. sql/sql_prepare.cc: Validate and prepare a CREATE VIEW statement for execution. sql/sql_view.cc: Move privileges check to it's own function. sql/sql_view.h: Export function which check privileges of a CREATE VIEW statement. --- mysql-test/r/ps.result | 152 +++++++++++++++++++++++++++++++++++ mysql-test/t/ps.test | 123 ++++++++++++++++++++++++++++ sql/item.h | 43 +++++----- sql/sql_prepare.cc | 40 ++++++++++ sql/sql_view.cc | 213 +++++++++++++++++++++++++++++-------------------- sql/sql_view.h | 3 + 6 files changed, 467 insertions(+), 107 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 8845f011971..32f48a688e2 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1709,4 +1709,156 @@ a b 9999999999999999 14632475938453979136 deallocate prepare stmt; drop table t1; +drop view if exists v1; +drop table if exists t1; +create table t1 (a int, b int); +insert into t1 values (1,1), (2,2), (3,3); +insert into t1 values (3,1), (1,2), (2,3); +prepare stmt from "create view v1 as select * from t1"; +execute stmt; +drop table t1; +create table t1 (a int, b int); +drop view v1; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` +drop view v1; +prepare stmt from "create view v1 (c,d) as select a,b from t1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `c`,`t1`.`b` AS `d` from `t1` +select * from v1; +c d +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `c`,`t1`.`b` AS `d` from `t1` +select * from v1; +c d +drop view v1; +prepare stmt from "create view v1 (c) as select b+1 from t1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select (`t1`.`b` + 1) AS `c` from `t1` +select * from v1; +c +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select (`t1`.`b` + 1) AS `c` from `t1` +select * from v1; +c +drop view v1; +prepare stmt from "create view v1 (c,d,e,f) as select a,b,a in (select a+2 from t1), a = all (select a from t1) from t1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `c`,`t1`.`b` AS `d`,`t1`.`a` in (select (`t1`.`a` + 2) AS `a+2` from `t1`) AS `e`,`t1`.`a` = all (select `t1`.`a` AS `a` from `t1`) AS `f` from `t1` +select * from v1; +c d e f +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `c`,`t1`.`b` AS `d`,`t1`.`a` in (select (`t1`.`a` + 2) AS `a+2` from `t1`) AS `e`,`t1`.`a` = all (select `t1`.`a` AS `a` from `t1`) AS `f` from `t1` +select * from v1; +c d e f +drop view v1; +prepare stmt from "create or replace view v1 as select 1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1` +select * from v1; +1 +1 +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1` +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1` +select * from v1; +1 +1 +drop view v1; +prepare stmt from "create view v1 as select 1, 1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1`,1 AS `My_exp_1` +select * from v1; +1 My_exp_1 +1 1 +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1`,1 AS `My_exp_1` +select * from v1; +1 My_exp_1 +1 1 +drop view v1; +prepare stmt from "create view v1 (x) as select a from t1 where a > 1"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `x` from `t1` where (`t1`.`a` > 1) +select * from v1; +x +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `x` from `t1` where (`t1`.`a` > 1) +select * from v1; +x +drop view v1; +prepare stmt from "create view v1 as select * from `t1` `b`"; +execute stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `b`.`a` AS `a`,`b`.`b` AS `b` from `t1` `b` +select * from v1; +a b +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `b`.`a` AS `a`,`b`.`b` AS `b` from `t1` `b` +select * from v1; +a b +drop view v1; +prepare stmt from "create view v1 (a,b,c) as select * from t1"; +execute stmt; +ERROR HY000: View's SELECT and view's field list have different column counts +execute stmt; +ERROR HY000: View's SELECT and view's field list have different column counts +deallocate prepare stmt; +drop table t1; +create temporary table t1 (a int, b int); +prepare stmt from "create view v1 as select * from t1"; +execute stmt; +ERROR HY000: View's SELECT refers to a temporary table 't1' +execute stmt; +ERROR HY000: View's SELECT refers to a temporary table 't1' +deallocate prepare stmt; +drop table t1; +prepare stmt from "create view v1 as select * from t1"; +ERROR 42S02: Table 'test.t1' doesn't exist +prepare stmt from "create view v1 as select * from `t1` `b`"; +ERROR 42S02: Table 'test.t1' doesn't exist End of 5.0 tests. diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 3f4b37f13f4..58ba901d82b 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1824,4 +1824,127 @@ select * from t1 where a = @a and b = @b; deallocate prepare stmt; drop table t1; +# +# Bug#32890 Crash after repeated create and drop of tables and views +# + +--disable_warnings +drop view if exists v1; +drop table if exists t1; +--enable_warnings + +create table t1 (a int, b int); +insert into t1 values (1,1), (2,2), (3,3); +insert into t1 values (3,1), (1,2), (2,3); + +prepare stmt from "create view v1 as select * from t1"; +execute stmt; +drop table t1; +create table t1 (a int, b int); +drop view v1; +execute stmt; +show create view v1; +drop view v1; + +prepare stmt from "create view v1 (c,d) as select a,b from t1"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 (c) as select b+1 from t1"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 (c,d,e,f) as select a,b,a in (select a+2 from t1), a = all (select a from t1) from t1"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create or replace view v1 as select 1"; +execute stmt; +show create view v1; +select * from v1; +execute stmt; +show create view v1; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 as select 1, 1"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 (x) as select a from t1 where a > 1"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 as select * from `t1` `b`"; +execute stmt; +show create view v1; +select * from v1; +drop view v1; +execute stmt; +deallocate prepare stmt; +show create view v1; +select * from v1; +drop view v1; + +prepare stmt from "create view v1 (a,b,c) as select * from t1"; +--error ER_VIEW_WRONG_LIST +execute stmt; +--error ER_VIEW_WRONG_LIST +execute stmt; +deallocate prepare stmt; + +drop table t1; +create temporary table t1 (a int, b int); + +prepare stmt from "create view v1 as select * from t1"; +--error ER_VIEW_SELECT_TMPTABLE +execute stmt; +--error ER_VIEW_SELECT_TMPTABLE +execute stmt; +deallocate prepare stmt; + +drop table t1; + +--error ER_NO_SUCH_TABLE +prepare stmt from "create view v1 as select * from t1"; +--error ER_NO_SUCH_TABLE +prepare stmt from "create view v1 as select * from `t1` `b`"; + --echo End of 5.0 tests. diff --git a/sql/item.h b/sql/item.h index 5f511557f47..ae3e240778a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -879,6 +879,23 @@ public: class sp_head; +class Item_basic_constant :public Item +{ +public: + /* to prevent drop fixed flag (no need parent cleanup call) */ + void cleanup() + { + /* + Restore the original field name as it might not have been allocated + in the statement memory. If the name is auto generated, it must be + done again between subsequent executions of a prepared statement. + */ + if (orig_name) + name= orig_name; + } +}; + + /***************************************************************************** The class is a base class for representation of stored routine variables in the Item-hierarchy. There are the following kinds of SP-vars: @@ -1161,7 +1178,7 @@ bool agg_item_charsets(DTCollation &c, const char *name, Item **items, uint nitems, uint flags, int item_sep); -class Item_num: public Item +class Item_num: public Item_basic_constant { public: Item_num() {} /* Remove gcc warning */ @@ -1352,7 +1369,7 @@ public: friend class st_select_lex_unit; }; -class Item_null :public Item +class Item_null :public Item_basic_constant { public: Item_null(char *name_par=0) @@ -1374,8 +1391,6 @@ public: bool send(Protocol *protocol, String *str); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_NULL; } - /* to prevent drop fixed flag (no need parent cleanup call) */ - void cleanup() {} bool basic_const_item() const { return 1; } Item *clone_item() { return new Item_null(name); } bool is_null() { return 1; } @@ -1567,8 +1582,6 @@ public: int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } Item *clone_item() { return new Item_int(name,value,max_length); } - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} void print(String *str); Item_num *neg() { value= -value; return this; } uint decimal_precision() const @@ -1621,8 +1634,6 @@ public: { return new Item_decimal(name, &decimal_value, decimals, max_length); } - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} void print(String *str); Item_num *neg() { @@ -1673,8 +1684,6 @@ public: String *val_str(String*); my_decimal *val_decimal(my_decimal *); bool basic_const_item() const { return 1; } - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} Item *clone_item() { return new Item_float(name, value, decimals, max_length); } Item_num *neg() { value= -value; return this; } @@ -1696,7 +1705,7 @@ public: }; -class Item_string :public Item +class Item_string :public Item_basic_constant { public: Item_string(const char *str,uint length, @@ -1780,8 +1789,6 @@ public: max_length= str_value.numchars() * collation.collation->mbmaxlen; } void print(String *str); - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} }; @@ -1839,10 +1846,10 @@ public: }; -class Item_hex_string: public Item +class Item_hex_string: public Item_basic_constant { public: - Item_hex_string(): Item() {} + Item_hex_string() {} Item_hex_string(const char *str,uint str_length); enum Type type() const { return VARBIN_ITEM; } double val_real() @@ -1858,8 +1865,6 @@ public: enum Item_result result_type () const { return STRING_RESULT; } enum Item_result cast_to_int_type() const { return INT_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} void print(String *str); bool eq(const Item *item, bool binary_cmp) const; virtual Item *safe_charset_converter(CHARSET_INFO *tocs); @@ -2449,7 +2454,7 @@ private: }; -class Item_cache: public Item +class Item_cache: public Item_basic_constant { protected: Item *example; @@ -2486,8 +2491,6 @@ public: static Item_cache* get_cache(const Item *item); table_map used_tables() const { return used_table_map; } virtual void keep_array() {} - // to prevent drop fixed flag (no need parent cleanup call) - void cleanup() {} void print(String *str); bool eq_def(Field *field) { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 74cbd2c5505..4972b259693 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1512,6 +1512,45 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } +/** + @brief Validate and prepare for execution CREATE VIEW statement + + @param stmt prepared statement + + @note This function handles create view commands. + + @retval FALSE Operation was a success. + @retval TRUE An error occured. +*/ + +static bool mysql_test_create_view(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_create_view"); + THD *thd= stmt->thd; + LEX *lex= stmt->lex; + SELECT_LEX *select_lex= &lex->select_lex; + bool res= TRUE; + /* Skip first table, which is the view we are creating */ + bool link_to_local; + TABLE_LIST *view= lex->unlink_first_table(&link_to_local); + TABLE_LIST *tables= lex->query_tables; + + if (create_view_precheck(thd, tables, view, lex->create_view_mode)) + goto err; + + if (open_normal_and_derived_tables(thd, tables, 0)) + goto err; + + lex->view_prepare_mode= 1; + res= select_like_stmt_test(stmt, 0, 0); + +err: + /* put view back for PS rexecuting */ + lex->link_first_table_back(view, link_to_local); + DBUG_RETURN(res); +} + + /* Validate and prepare for execution a multi update statement. @@ -1730,6 +1769,7 @@ static bool check_prepared_statement(Prepared_statement *stmt, my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0)); goto error; } + res= mysql_test_create_view(stmt); break; case SQLCOM_DO: res= mysql_test_do_fields(stmt, tables, lex->insert_list); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 48ab5b3af9e..4c8e6e80c41 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -227,104 +227,31 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) return FALSE; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /** - @brief Creating/altering VIEW procedure + @brief CREATE VIEW privileges pre-check. @param thd thread handler + @param tables tables used in the view @param views views to create @param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE - @note This function handles both create and alter view commands. - @retval FALSE Operation was a success. @retval TRUE An error occured. */ -bool mysql_create_view(THD *thd, TABLE_LIST *views, - enum_view_create_mode mode) +bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, + enum_view_create_mode mode) { LEX *lex= thd->lex; - bool link_to_local; /* first table in list is target VIEW name => cut off it */ - TABLE_LIST *view= lex->unlink_first_table(&link_to_local); - TABLE_LIST *tables= lex->query_tables; TABLE_LIST *tbl; SELECT_LEX *select_lex= &lex->select_lex; -#ifndef NO_EMBEDDED_ACCESS_CHECKS SELECT_LEX *sl; -#endif - SELECT_LEX_UNIT *unit= &lex->unit; - bool res= FALSE; - DBUG_ENTER("mysql_create_view"); - - /* This is ensured in the parser. */ - DBUG_ASSERT(!lex->proc_list.first && !lex->result && - !lex->param_list.elements && !lex->derived_tables); - - if (mode != VIEW_CREATE_NEW) - { - if (mode == VIEW_ALTER && - fill_defined_view_parts(thd, view)) - { - res= TRUE; - goto err; - } - sp_cache_invalidate(); - } + bool res= TRUE; + DBUG_ENTER("create_view_precheck"); - if (!lex->definer) - { - /* - DEFINER-clause is missing; we have to create default definer in - persistent arena to be PS/SP friendly. - If this is an ALTER VIEW then the current user should be set as - the definer. - */ - Query_arena original_arena; - Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); - - if (!(lex->definer= create_default_definer(thd))) - res= TRUE; - - if (ps_arena) - thd->restore_active_arena(ps_arena, &original_arena); - - if (res) - goto err; - } - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* - check definer of view: - - same as current user - - current user has SUPER_ACL - */ - if (lex->definer && - (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host) != 0)) - { - if (!(thd->security_ctx->master_access & SUPER_ACL)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - res= TRUE; - goto err; - } - else - { - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) - { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_SUCH_USER, - ER(ER_NO_SUCH_USER), - lex->definer->user.str, - lex->definer->host.str); - } - } - } /* Privilege check for view creation: - user has CREATE VIEW privilege on view table @@ -346,10 +273,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, (check_access(thd, DROP_ACL, view->db, &view->grant.privilege, 0, 0, is_schema_db(view->db)) || grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0)))) - { - res= TRUE; goto err; - } + for (sl= select_lex; sl; sl= sl->next_select()) { for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) @@ -363,7 +288,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY", thd->security_ctx->priv_user, thd->security_ctx->priv_host, tbl->table_name); - res= TRUE; goto err; } /* @@ -399,10 +323,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, if (check_access(thd, SELECT_ACL, tbl->db, &tbl->grant.privilege, 0, 0, test(tbl->schema_table)) || grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0)) - { - res= TRUE; goto err; - } } } } @@ -426,8 +347,126 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } } } + + res= FALSE; + +err: + DBUG_RETURN(res || thd->net.report_error); +} + +#else + +bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, + enum_view_create_mode mode) +{ + return FALSE; +} + +#endif + + +/** + @brief Creating/altering VIEW procedure + + @param thd thread handler + @param views views to create + @param mode VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE + + @note This function handles both create and alter view commands. + + @retval FALSE Operation was a success. + @retval TRUE An error occured. +*/ + +bool mysql_create_view(THD *thd, TABLE_LIST *views, + enum_view_create_mode mode) +{ + LEX *lex= thd->lex; + bool link_to_local; + /* first table in list is target VIEW name => cut off it */ + TABLE_LIST *view= lex->unlink_first_table(&link_to_local); + TABLE_LIST *tables= lex->query_tables; + TABLE_LIST *tbl; + SELECT_LEX *select_lex= &lex->select_lex; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + SELECT_LEX *sl; +#endif + SELECT_LEX_UNIT *unit= &lex->unit; + bool res= FALSE; + DBUG_ENTER("mysql_create_view"); + + /* This is ensured in the parser. */ + DBUG_ASSERT(!lex->proc_list.first && !lex->result && + !lex->param_list.elements && !lex->derived_tables); + + if (mode != VIEW_CREATE_NEW) + { + if (mode == VIEW_ALTER && + fill_defined_view_parts(thd, view)) + { + res= TRUE; + goto err; + } + sp_cache_invalidate(); + } + + if (!lex->definer) + { + /* + DEFINER-clause is missing; we have to create default definer in + persistent arena to be PS/SP friendly. + If this is an ALTER VIEW then the current user should be set as + the definer. + */ + Query_arena original_arena; + Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); + + if (!(lex->definer= create_default_definer(thd))) + res= TRUE; + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + if (res) + goto err; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* + check definer of view: + - same as current user + - current user has SUPER_ACL + */ + if (lex->definer && + (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 || + my_strcasecmp(system_charset_info, + lex->definer->host.str, + thd->security_ctx->priv_host) != 0)) + { + if (!(thd->security_ctx->master_access & SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + res= TRUE; + goto err; + } + else + { + if (!is_acl_user(lex->definer->host.str, + lex->definer->user.str)) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } + } + } #endif + if ((res= create_view_precheck(thd, tables, view, mode))) + goto err; + if (open_and_lock_tables(thd, tables)) { res= TRUE; diff --git a/sql/sql_view.h b/sql/sql_view.h index ab0920e0bf2..1d45283352b 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -15,6 +15,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, + enum_view_create_mode mode); + bool mysql_create_view(THD *thd, TABLE_LIST *view, enum_view_create_mode mode); -- cgit v1.2.1 From 9f245df8530a560844aca138d103dd2f78301005 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Feb 2008 19:28:25 -0200 Subject: Post-merge fix to silence compiler warning. sql/sql_prepare.cc: Removed unused variable. --- sql/sql_prepare.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4972b259693..18cfd8d7dfc 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1528,7 +1528,6 @@ static bool mysql_test_create_view(Prepared_statement *stmt) DBUG_ENTER("mysql_test_create_view"); THD *thd= stmt->thd; LEX *lex= stmt->lex; - SELECT_LEX *select_lex= &lex->select_lex; bool res= TRUE; /* Skip first table, which is the view we are creating */ bool link_to_local; -- cgit v1.2.1 From 24039016f00e4437fa52a37a716195805280d592 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Feb 2008 15:27:46 +0300 Subject: Fix memory leaks (valgrind) --- tests/mysql_client_test.c | 90 +++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 25522a88445..56c4cff3e5a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -8702,8 +8702,8 @@ static void test_sqlmode() strmov(c1, "My"); strmov(c2, "SQL"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - mysql_stmt_close(stmt); + verify_col_data("test_piping", "name", "MySQL"); rc= mysql_query(mysql, "DELETE FROM test_piping"); @@ -12993,7 +12993,7 @@ from t2);"); static void test_bug8378() { #if defined(HAVE_CHARSET_gbk) && !defined(EMBEDDED_LIBRARY) - MYSQL *old_mysql=mysql; + MYSQL *lmysql; char out[9]; /* strlen(TEST_BUG8378)*2+1 */ char buf[256]; int len, rc; @@ -13002,17 +13002,17 @@ static void test_bug8378() if (!opt_silent) fprintf(stdout, "\n Establishing a test connection ..."); - if (!(mysql= mysql_init(NULL))) + if (!(lmysql= mysql_init(NULL))) { myerror("mysql_init() failed"); exit(1); } - if (mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "gbk")) + if (mysql_options(lmysql, MYSQL_SET_CHARSET_NAME, "gbk")) { myerror("mysql_options() failed"); exit(1); } - if (!(mysql_real_connect(mysql, opt_host, opt_user, + if (!(mysql_real_connect(lmysql, opt_host, opt_user, opt_password, current_db, opt_port, opt_unix_socket, 0))) { @@ -13022,19 +13022,17 @@ static void test_bug8378() if (!opt_silent) fprintf(stdout, " OK"); - len= mysql_real_escape_string(mysql, out, TEST_BUG8378_IN, 4); + len= mysql_real_escape_string(lmysql, out, TEST_BUG8378_IN, 4); /* No escaping should have actually happened. */ DIE_UNLESS(memcmp(out, TEST_BUG8378_OUT, len) == 0); sprintf(buf, "SELECT '%s'", out); - rc=mysql_real_query(mysql, buf, strlen(buf)); + rc=mysql_real_query(lmysql, buf, strlen(buf)); myquery(rc); - mysql_close(mysql); - - mysql=old_mysql; + mysql_close(lmysql); #endif } @@ -14869,7 +14867,7 @@ static void test_opt_reconnect() if (mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true)) { myerror("mysql_options failed: unknown option MYSQL_OPT_RECONNECT\n"); - exit(1); + DIE_UNLESS(0); } /* reconnect should be 1 */ @@ -14882,7 +14880,7 @@ static void test_opt_reconnect() opt_unix_socket, 0))) { myerror("connection failed"); - exit(1); + DIE_UNLESS(0); } /* reconnect should still be 1 */ @@ -14896,7 +14894,7 @@ static void test_opt_reconnect() if (!(lmysql= mysql_init(NULL))) { myerror("mysql_init() failed"); - exit(1); + DIE_UNLESS(0); } if (!opt_silent) @@ -14908,7 +14906,7 @@ static void test_opt_reconnect() opt_unix_socket, 0))) { myerror("connection failed"); - exit(1); + DIE_UNLESS(0); } /* reconnect should still be 0 */ @@ -14926,32 +14924,32 @@ static void test_opt_reconnect() static void test_bug12744() { MYSQL_STMT *prep_stmt = NULL; + MYSQL *lmysql; int rc; myheader("test_bug12744"); - prep_stmt= mysql_stmt_init(mysql); - rc= mysql_stmt_prepare(prep_stmt, "SELECT 1", 8); - DIE_UNLESS(rc==0); + lmysql= mysql_init(NULL); + DIE_UNLESS(lmysql); - mysql_close(mysql); - - if ((rc= mysql_stmt_execute(prep_stmt))) - { - if ((rc= mysql_stmt_reset(prep_stmt))) - printf("OK!\n"); - else - { - printf("Error!"); - DIE_UNLESS(1==0); - } - } - else + if (!mysql_real_connect(lmysql, opt_host, opt_user, opt_password, + current_db, opt_port, opt_unix_socket, 0)) { - fprintf(stderr, "expected error but no error occured\n"); - DIE_UNLESS(1==0); + fprintf(stderr, "Failed to connect to the database\n"); + DIE_UNLESS(0); } + + prep_stmt= mysql_stmt_init(lmysql); + rc= mysql_stmt_prepare(prep_stmt, "SELECT 1", 8); + DIE_UNLESS(rc == 0); + + mysql_close(lmysql); + + rc= mysql_stmt_execute(prep_stmt); + DIE_UNLESS(rc); + rc= mysql_stmt_reset(prep_stmt); + DIE_UNLESS(rc); rc= mysql_stmt_close(prep_stmt); - client_connect(0); + DIE_UNLESS(rc == 0); } #endif /* EMBEDDED_LIBRARY */ @@ -15759,6 +15757,7 @@ static void test_bug24179() mysql_stmt_error(stmt)); } DIE_UNLESS(mysql_stmt_errno(stmt) == 1323); + mysql_stmt_close(stmt); DBUG_VOID_RETURN; } @@ -15801,6 +15800,7 @@ static void test_bug27876() myquery(rc); result= mysql_store_result(mysql); mytest(result); + mysql_free_result(result); sprintf(query, "DROP FUNCTION IF EXISTS %s", utf8_func); rc= mysql_query(mysql, query); @@ -15817,6 +15817,7 @@ static void test_bug27876() myquery(rc); result= mysql_store_result(mysql); mytest(result); + mysql_free_result(result); sprintf(query, "DROP FUNCTION %s", utf8_func); rc= mysql_query(mysql, query); @@ -15965,6 +15966,7 @@ static void test_bug29948() exit(1); } + bzero(&bind, sizeof(bind)); bind.buffer_type= MYSQL_TYPE_LONG; bind.buffer= (char *)&buf; bind.is_null= &is_null; @@ -16162,6 +16164,7 @@ static void test_bug32265() int rc; MYSQL_STMT *stmt; MYSQL_FIELD *field; + MYSQL_RES *metadata; DBUG_ENTER("test_bug32265"); myheader("test_bug32265"); @@ -16179,50 +16182,61 @@ static void test_bug32265() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - field= stmt->mysql->fields; + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + DIE_UNLESS(field); DIE_UNLESS(strcmp(field->table, "t1") == 0); DIE_UNLESS(strcmp(field->org_table, "t1") == 0); DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_free_result(metadata); mysql_stmt_close(stmt); stmt= open_cursor("SELECT a '' FROM t1 ``"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - field= stmt->mysql->fields; + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); DIE_UNLESS(strcmp(field->table, "") == 0); DIE_UNLESS(strcmp(field->org_table, "t1") == 0); DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_free_result(metadata); mysql_stmt_close(stmt); stmt= open_cursor("SELECT a '' FROM t1 ``"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - field= stmt->mysql->fields; + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); DIE_UNLESS(strcmp(field->table, "") == 0); DIE_UNLESS(strcmp(field->org_table, "t1") == 0); DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_free_result(metadata); mysql_stmt_close(stmt); stmt= open_cursor("SELECT * FROM v1"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - field= stmt->mysql->fields; + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); DIE_UNLESS(strcmp(field->table, "v1") == 0); DIE_UNLESS(strcmp(field->org_table, "t1") == 0); DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_free_result(metadata); mysql_stmt_close(stmt); stmt= open_cursor("SELECT * FROM v1 /* SIC */ GROUP BY 1"); rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - field= stmt->mysql->fields; + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); DIE_UNLESS(strcmp(field->table, "v1") == 0); DIE_UNLESS(strcmp(field->org_table, "t1") == 0); DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_free_result(metadata); mysql_stmt_close(stmt); rc= mysql_query(mysql, "DROP VIEW v1"); -- cgit v1.2.1 From e78d896c3feb16cec7bdecc6ea3733730a3826c1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Feb 2008 13:05:46 -0300 Subject: Bug#34889 mysql_client_test::test_mysql_insert_id test fails sporadically Disable the test case. tests/mysql_client_test.c: Disable test case. --- tests/mysql_client_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 56c4cff3e5a..429f6688759 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -16527,7 +16527,7 @@ static struct my_tests_st my_tests[]= { { "test_bug17667", test_bug17667 }, { "test_bug19671", test_bug19671 }, { "test_bug15752", test_bug15752 }, - { "test_mysql_insert_id", test_mysql_insert_id }, + /* { "test_mysql_insert_id", test_mysql_insert_id }, Bug#34889 */ { "test_bug21206", test_bug21206 }, { "test_bug21726", test_bug21726 }, { "test_bug15518", test_bug15518 }, -- cgit v1.2.1 From b43f8f695c152849f47b1a33e54ba0009144bc10 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Feb 2008 22:29:58 +0300 Subject: Eliminate compilation warning. --- tests/mysql_client_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 429f6688759..260e7b1e7a7 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15215,6 +15215,10 @@ static void test_bug14169() Test that mysql_insert_id() behaves as documented in our manual */ +#if 0 + + Commented out because of Bug#34889. + static void test_mysql_insert_id() { my_ulonglong res; @@ -15390,6 +15394,7 @@ static void test_mysql_insert_id() rc= mysql_query(mysql, "drop table t1,t2"); myquery(rc); } +#endif /* -- cgit v1.2.1 From 1164e2bc7a6dc18b33e401f0a9f6949c6f2ff6ea Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Feb 2008 11:34:08 -0300 Subject: Bug#33851 Passing UNSIGNED param to EXECUTE returns ERROR 1210 The problem is that passing anything other than a integer to a limit clause in a prepared statement would fail. This limitation was introduced to avoid replication problems (e.g: replicating the statement with a string argument would cause a parse failure in the slave). The solution is to convert arguments to the limit clause to a integer value and use this converted value when persisting the query to the log. mysql-test/r/limit.result: Update test case result. mysql-test/r/ps.result: Add test case result for Bug#33851 mysql-test/r/rpl_user_variables.result: Test case result for replication of prepared statement with limit clause. mysql-test/t/limit.test: Test parameters to limit clause. mysql-test/t/ps.test: Add test case for Bug#33851 mysql-test/t/rpl_user_variables.test: Test replication of a parameter which value is converted. sql/item.cc: Convert value to integer if it's a parameter to a limit clause. sql/item.h: Flag signal that item is a parameter to a limit clause. sql/item_func.cc: Const member functions, object is not mutated. sql/sql_class.h: Const member functions, object is not mutated. sql/sql_yacc.yy: Flag that item is a parameter to a limit clause. --- mysql-test/r/limit.result | 9 +++++++++ mysql-test/r/ps.result | 30 ++++++++++++++++++++++++++++++ mysql-test/r/rpl_user_variables.result | 16 ++++++++++++++++ mysql-test/t/limit.test | 17 ++++++++++++----- mysql-test/t/ps.test | 19 +++++++++++++++++++ mysql-test/t/rpl_user_variables.test | 17 +++++++++++++++++ sql/item.cc | 11 ++++++++--- sql/item.h | 9 ++------- sql/item_func.cc | 2 +- sql/sql_class.h | 2 +- sql/sql_yacc.yy | 2 +- 11 files changed, 116 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/limit.result b/mysql-test/r/limit.result index 01d7d7ca218..2acf74162a4 100644 --- a/mysql-test/r/limit.result +++ b/mysql-test/r/limit.result @@ -94,6 +94,9 @@ drop table t1; prepare s from "select 1 limit ?"; set @a='qwe'; execute s using @a; +1 +set @a=-1; +execute s using @a; ERROR HY000: Incorrect arguments to EXECUTE prepare s from "select 1 limit 1, ?"; execute s using @a; @@ -101,4 +104,10 @@ ERROR HY000: Incorrect arguments to EXECUTE prepare s from "select 1 limit ?, ?"; execute s using @a, @a; ERROR HY000: Incorrect arguments to EXECUTE +set @a=14632475938453979136; +execute s using @a, @a; +1 +set @a=-14632475938453979136; +execute s using @a, @a; +ERROR HY000: Incorrect arguments to EXECUTE End of 5.0 tests diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 32f48a688e2..9aef58d5702 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1861,4 +1861,34 @@ prepare stmt from "create view v1 as select * from t1"; ERROR 42S02: Table 'test.t1' doesn't exist prepare stmt from "create view v1 as select * from `t1` `b`"; ERROR 42S02: Table 'test.t1' doesn't exist +prepare stmt from "select ?"; +set @arg= 123456789.987654321; +select @arg; +@arg +123456789.987654321 +execute stmt using @arg; +? +123456789.987654321 +set @arg= "string"; +select @arg; +@arg +string +execute stmt using @arg; +? +string +set @arg= 123456; +select @arg; +@arg +123456 +execute stmt using @arg; +? +123456 +set @arg= cast(-12345.54321 as decimal(20, 10)); +select @arg; +@arg +-12345.5432100000 +execute stmt using @arg; +? +-12345.5432100000 +deallocate prepare stmt; End of 5.0 tests. diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index 26ac2b26aaa..b8032a9c362 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -290,6 +290,22 @@ select * from t1; a b 2 1 drop table t1; +create table t1(a int); +insert into t1 values (1),(2); +prepare s1 from 'insert into t1 select a from t1 limit ?'; +set @x='1.1'; +execute s1 using @x; +select * from t1; +a +1 +2 +1 +select * from t1; +a +1 +2 +1 +drop table t1; End of 5.0 tests. DROP FUNCTION IF EXISTS f1; DROP FUNCTION IF EXISTS f2; diff --git a/mysql-test/t/limit.test b/mysql-test/t/limit.test index 286c04785ff..9cccca1adc3 100644 --- a/mysql-test/t/limit.test +++ b/mysql-test/t/limit.test @@ -76,15 +76,22 @@ drop table t1; # Bug #28464: a string argument to 'limit ?' PS # -prepare s from "select 1 limit ?"; -set @a='qwe'; ---error 1210 +prepare s from "select 1 limit ?"; +set @a='qwe'; +execute s using @a; +set @a=-1; +--error ER_WRONG_ARGUMENTS execute s using @a; prepare s from "select 1 limit 1, ?"; ---error 1210 +--error ER_WRONG_ARGUMENTS execute s using @a; prepare s from "select 1 limit ?, ?"; ---error 1210 +--error ER_WRONG_ARGUMENTS +execute s using @a, @a; +set @a=14632475938453979136; +execute s using @a, @a; +set @a=-14632475938453979136; +--error ER_WRONG_ARGUMENTS execute s using @a, @a; --echo End of 5.0 tests diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 58ba901d82b..6c3f98f6a1a 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1947,4 +1947,23 @@ prepare stmt from "create view v1 as select * from t1"; --error ER_NO_SUCH_TABLE prepare stmt from "create view v1 as select * from `t1` `b`"; +# +# Bug#33851: Passing UNSIGNED param to EXECUTE returns ERROR 1210 +# + +prepare stmt from "select ?"; +set @arg= 123456789.987654321; +select @arg; +execute stmt using @arg; +set @arg= "string"; +select @arg; +execute stmt using @arg; +set @arg= 123456; +select @arg; +execute stmt using @arg; +set @arg= cast(-12345.54321 as decimal(20, 10)); +select @arg; +execute stmt using @arg; +deallocate prepare stmt; + --echo End of 5.0 tests. diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index 8f8f0accbd1..70b708be258 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -337,6 +337,23 @@ select * from t1; connection master; drop table t1; +# +# Bug#33851: Passing UNSIGNED param to EXECUTE returns ERROR 1210 +# + +connection master; +create table t1(a int); +insert into t1 values (1),(2); +prepare s1 from 'insert into t1 select a from t1 limit ?'; +set @x='1.1'; +execute s1 using @x; +select * from t1; +sync_slave_with_master; +connection slave; +select * from t1; +connection master; +drop table t1; + --echo End of 5.0 tests. # This test uses a stored function that uses user-defined variables to return data diff --git a/sql/item.cc b/sql/item.cc index ffb18054750..a6a18e27b09 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2385,7 +2385,7 @@ default_set_param_func(Item_param *param, Item_param::Item_param(unsigned pos_in_query_arg) : - strict_type(FALSE), + limit_clause_param(FALSE), state(NO_VALUE), item_result_type(STRING_RESULT), /* Don't pretend to be a literal unless value for this item is set. */ @@ -2581,8 +2581,13 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) { item_result_type= entry->type; unsigned_flag= entry->unsigned_flag; - if (strict_type && required_result_type != item_result_type) - DBUG_RETURN(1); + if (limit_clause_param) + { + my_bool unused; + set_int(entry->val_int(&unused), MY_INT64_NUM_DECIMAL_DIGITS); + item_type= Item::INT_ITEM; + DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0); + } switch (item_result_type) { case REAL_RESULT: set_double(*(double*)entry->value); diff --git a/sql/item.h b/sql/item.h index ae3e240778a..7dc9ed4ec10 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1417,8 +1417,6 @@ class Item_param :public Item char cnvbuf[MAX_FIELD_WIDTH]; String cnvstr; Item *cnvitem; - bool strict_type; - enum Item_result required_result_type; public: enum enum_item_param_state @@ -1548,11 +1546,8 @@ public: Otherwise return FALSE. */ bool eq(const Item *item, bool binary_cmp) const; - void set_strict_type(enum Item_result result_type_arg) - { - strict_type= TRUE; - required_result_type= result_type_arg; - } + /** Item is a argument to a limit clause. */ + bool limit_clause_param; }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 639e069d24e..84dd3e3ebc6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3985,7 +3985,7 @@ double user_var_entry::val_real(my_bool *null_value) /* Get the value of a variable as an integer */ -longlong user_var_entry::val_int(my_bool *null_value) +longlong user_var_entry::val_int(my_bool *null_value) const { if ((*null_value= (value == 0))) return LL(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 97f2d07b1d3..4ca8947de30 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2329,7 +2329,7 @@ class user_var_entry bool unsigned_flag; double val_real(my_bool *null_value); - longlong val_int(my_bool *null_value); + longlong val_int(my_bool *null_value) const; String *val_str(my_bool *null_value, String *str, uint decimals); my_decimal *val_decimal(my_bool *null_value, my_decimal *result); DTCollation collation; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a7c1060025d..04285fea227 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6320,7 +6320,7 @@ limit_options: limit_option: param_marker { - ((Item_param *) $1)->set_strict_type(INT_RESULT); + ((Item_param *) $1)->limit_clause_param= TRUE; } | ULONGLONG_NUM { $$= new Item_uint($1.str, $1.length); } | LONG_NUM { $$= new Item_uint($1.str, $1.length); } -- cgit v1.2.1 From 8b77945615ed06a9ae0c1861943b73f46f3cbb71 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Feb 2008 20:22:11 -0300 Subject: Post-merge fix for Bug 33851. The initialization order of members must match the order which they were declared in the class definition. sql/item.cc: Fix initialization order, parameter was the last one declared. --- sql/item.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index a6a18e27b09..a9e99c65580 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2385,14 +2385,14 @@ default_set_param_func(Item_param *param, Item_param::Item_param(unsigned pos_in_query_arg) : - limit_clause_param(FALSE), state(NO_VALUE), item_result_type(STRING_RESULT), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), param_type(MYSQL_TYPE_VARCHAR), pos_in_query(pos_in_query_arg), - set_param_func(default_set_param_func) + set_param_func(default_set_param_func), + limit_clause_param(FALSE) { name= (char*) "?"; /* -- cgit v1.2.1 From 4d09306dd19820a742b003417d83f76f5d3633f3 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Mar 2008 21:19:58 +0100 Subject: Raise the version number after cloning 5.0.58 --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index e65bbd51432..2ee722b4905 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.58) +AM_INIT_AUTOMAKE(mysql, 5.0.60) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=58 +NDB_VERSION_BUILD=60 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? -- cgit v1.2.1