diff options
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r-- | sql/sql_prepare.cc | 147 |
1 files changed, 63 insertions, 84 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 934b87a938f..94a35db3a2d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 1995-2002 MySQL AB +/* Copyright (C) 1995-2002 MySQL AB, 2008-2009 Sun Microsystems, Inc 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 @@ -84,6 +84,7 @@ When one supplies long data for a placeholder: */ #include "mysql_priv.h" +#include "set_var.h" #include "sql_prepare.h" #include "sql_select.h" // for JOIN #include "sql_cursor.h" @@ -173,8 +174,6 @@ private: SELECT_LEX and other classes). */ MEM_ROOT main_mem_root; - /* Version of the stored functions cache at the time of prepare. */ - ulong m_sp_cache_version; private: bool set_db(const char *db, uint db_length); bool set_parameters(String *expanded_query, @@ -1206,7 +1205,8 @@ static bool mysql_test_insert(Prepared_statement *stmt, If we would use locks, then we have to ensure we are not using TL_WRITE_DELAYED as having two such locks can cause table corruption. */ - if (open_normal_and_derived_tables(thd, table_list, 0)) + if (open_normal_and_derived_tables(thd, table_list, + MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if ((values= its++)) @@ -1287,7 +1287,7 @@ static int mysql_test_update(Prepared_statement *stmt, DBUG_ENTER("mysql_test_update"); if (update_precheck(thd, table_list) || - open_tables(thd, &table_list, &table_count, 0)) + open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if (table_list->multitable_view) @@ -1364,7 +1364,8 @@ static bool mysql_test_delete(Prepared_statement *stmt, DBUG_ENTER("mysql_test_delete"); if (delete_precheck(thd, table_list) || - open_normal_and_derived_tables(thd, table_list, 0)) + open_normal_and_derived_tables(thd, table_list, + MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; if (!table_list->table) @@ -1413,7 +1414,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE)) goto error; } - else if (check_access(thd, privilege, any_db,0,0,0,0)) + else if (check_access(thd, privilege, any_db, NULL, NULL, 0, 0)) goto error; if (!lex->result && !(lex->result= new (stmt->mem_root) select_send)) @@ -1422,7 +1423,7 @@ static int mysql_test_select(Prepared_statement *stmt, goto error; } - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; thd->used_tables= 0; // Updated by setup_fields @@ -1483,7 +1484,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0)); } @@ -1513,7 +1514,7 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || - open_normal_and_derived_tables(thd, tables, 0)) + open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto error; while ((var= it++)) @@ -1550,7 +1551,7 @@ static bool mysql_test_call_fields(Prepared_statement *stmt, if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) || - open_normal_and_derived_tables(thd, tables, 0)) + open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto err; while ((item= it++)) @@ -1633,7 +1634,8 @@ select_like_stmt_test_with_open(Prepared_statement *stmt, prepared EXPLAIN yet so derived tables will clean up after themself. */ - if (open_normal_and_derived_tables(stmt->thd, tables, 0)) + if (open_normal_and_derived_tables(stmt->thd, tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare, @@ -1660,35 +1662,40 @@ static bool mysql_test_create_table(Prepared_statement *stmt) LEX *lex= stmt->lex; SELECT_LEX *select_lex= &lex->select_lex; bool res= FALSE; - /* Skip first table, which is the table we are creating */ bool link_to_local; - TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); - TABLE_LIST *tables= lex->query_tables; + TABLE_LIST *create_table= lex->query_tables; + TABLE_LIST *tables= lex->create_last_non_select_table->next_global; if (create_table_precheck(thd, tables, create_table)) DBUG_RETURN(TRUE); + /* + The open and lock strategies will be set again once the + statement is executed. These values are only meaningful + for the prepare phase. + */ + create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + create_table->lock_strategy= TABLE_LIST::SHARED_MDL; + if (select_lex->item_list.elements) { + /* Base table and temporary table are not in the same name space. */ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - lex->link_first_table_back(create_table, link_to_local); - create_table->create= TRUE; - /* Base table and temporary table are not in the same name space. */ - create_table->skip_temporary= true; - } + create_table->open_type= OT_BASE_ONLY; - if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - create_table= lex->unlink_first_table(&link_to_local); - select_lex->context.resolve_in_select_list= TRUE; + lex->unlink_first_table(&link_to_local); + res= select_like_stmt_test(stmt, 0, 0); + + lex->link_first_table_back(create_table, &link_to_local); } - else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + else { /* Check that the source table exist, and also record @@ -1696,12 +1703,11 @@ static bool mysql_test_create_table(Prepared_statement *stmt) we validate metadata of all CREATE TABLE statements, which keeps metadata validation code simple. */ - if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0)) + if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, + MYSQL_OPEN_FORCE_SHARED_MDL)) DBUG_RETURN(TRUE); } - /* put tables back for PS rexecuting */ - lex->link_first_table_back(create_table, link_to_local); DBUG_RETURN(res); } @@ -1731,7 +1737,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt) if (create_view_precheck(thd, tables, view, lex->create_view_mode)) goto err; - if (open_normal_and_derived_tables(thd, tables, 0)) + if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL)) goto err; lex->view_prepare_mode= 1; @@ -2140,9 +2146,6 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) DBUG_VOID_RETURN; } - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - thd->protocol= &thd->protocol_binary; if (stmt->prepare(packet, packet_length)) @@ -2421,6 +2424,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) { tables->reinit_before_use(thd); } + + /* Reset MDL tickets for procedures/functions */ + for (Sroutine_hash_entry *rt= + (Sroutine_hash_entry*)thd->lex->sroutines_list.first; + rt; rt= rt->next) + rt->mdl_request.ticket= NULL; + /* Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ... (multi-delete). We do a full clean up, although at the moment all we @@ -2514,9 +2524,6 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) DBUG_PRINT("exec_query", ("%s", stmt->query())); DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY); thd->protocol= &thd->protocol_binary; @@ -2617,14 +2624,8 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length) thd->stmt_arena= stmt; thd->set_n_backup_statement(stmt, &stmt_backup); - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(), QUERY_PRIOR); - cursor->fetch(num_rows); - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(), WAIT_PRIOR); - if (!cursor->is_open()) { stmt->close_cursor(); @@ -2972,8 +2973,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) param_array(0), param_count(0), last_errno(0), - flags((uint) IS_IN_USE), - m_sp_cache_version(0) + flags((uint) IS_IN_USE) { init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size, thd_arg->variables.query_prealloc_size); @@ -3138,6 +3138,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) bool error; Statement stmt_backup; Query_arena *old_stmt_arena; + MDL_ticket *mdl_savepoint= NULL; DBUG_ENTER("Prepared_statement::prepare"); /* If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. @@ -3196,6 +3197,12 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) */ DBUG_ASSERT(thd->change_list.is_empty()); + /* + Marker used to release metadata locks acquired while the prepared + statement is being checked. + */ + mdl_savepoint= thd->mdl_context.mdl_savepoint(); + /* The only case where we should have items in the thd->free_list is after stmt->set_params_from_vars(), which may in some cases create @@ -3219,6 +3226,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex_end(lex); cleanup_stmt(); + /* + If not inside a multi-statement transaction, the metadata + locks have already been released and our savepoint points + to ticket which has been released as well. + */ + if (thd->in_multi_stmt_transaction()) + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; @@ -3228,20 +3242,6 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) init_stmt_after_parse(lex); state= Query_arena::PREPARED; flags&= ~ (uint) IS_IN_USE; - /* - This is for prepared statement validation purposes. - A statement looks up and pre-loads all its stored functions - at prepare. Later on, if a function is gone from the cache, - execute may fail. - Remember the cache version to be able to invalidate the prepared - statement at execute if it changes. - We only need to care about version of the stored functions cache: - if a prepared statement uses a stored procedure, it's indirect, - via a stored function. The only exception is SQLCOM_CALL, - but the latter one looks up the stored procedure each time - it's invoked, rather than once at prepare. - */ - m_sp_cache_version= sp_cache_version(&thd->sp_func_cache); /* Log COM_EXECUTE to the general log. Note, that in case of SQL @@ -3388,14 +3388,8 @@ reexecute: thd->m_reprepare_observer = &reprepare_observer; } - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); - error= execute(expanded_query, open_cursor) || thd->is_error(); - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(), WAIT_PRIOR); - thd->m_reprepare_observer= NULL; if (error && !thd->is_fatal_error && !thd->killed && @@ -3423,7 +3417,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) bool error; Query_arena *save_stmt_arena= thd->stmt_arena; Item_change_list save_change_list; - thd->change_list= save_change_list; + thd->change_list.move_elements_to(&save_change_list); state= CONVENTIONAL_EXECUTION; @@ -3447,7 +3441,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= save_stmt_arena; - save_change_list= thd->change_list; + save_change_list.move_elements_to(&thd->change_list); /* Items and memory will freed in destructor */ @@ -3588,13 +3582,12 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) is allocated in the old arena. */ swap_variables(Item_param **, param_array, copy->param_array); - /* Swap flags: this is perhaps unnecessary */ - swap_variables(uint, flags, copy->flags); + /* Don't swap flags: the copy has IS_SQL_PREPARE always set. */ + /* swap_variables(uint, flags, copy->flags); */ /* Swap names, the old name is allocated in the wrong memory root */ swap_variables(LEX_STRING, name, copy->name); /* Ditto */ swap_variables(char *, db, copy->db); - swap_variables(ulong, m_sp_cache_version, copy->m_sp_cache_version); DBUG_ASSERT(db_length == copy->db_length); DBUG_ASSERT(param_count == copy->param_count); @@ -3654,19 +3647,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) } /* - Reprepare the statement if we're using stored functions - and the version of the stored routines cache has changed. - */ - if (lex->uses_stored_routines() && - m_sp_cache_version != sp_cache_version(&thd->sp_func_cache) && - thd->m_reprepare_observer && - thd->m_reprepare_observer->report_error(thd)) - { - return TRUE; - } - - - /* For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT command. For such queries we don't return an error and don't open a cursor -- the client library will recognize this case and @@ -3789,7 +3769,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (state == Query_arena::PREPARED) state= Query_arena::EXECUTED; - if (this->lex->sql_command == SQLCOM_CALL) + if (error == 0 && this->lex->sql_command == SQLCOM_CALL) { if (is_sql_prepare()) thd->protocol_text.send_out_parameters(&this->lex->param_list); @@ -3797,7 +3777,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) thd->protocol->send_out_parameters(&this->lex->param_list); } - /* Log COM_EXECUTE to the general log. Note, that in case of SQL prepared statements this causes two records to be output: |