diff options
author | unknown <konstantin@oak.local> | 2003-12-20 02:16:10 +0300 |
---|---|---|
committer | unknown <konstantin@oak.local> | 2003-12-20 02:16:10 +0300 |
commit | 9c6eed72f04c15711d4de70bb8eb3f02bac8c30c (patch) | |
tree | c9441e15b696703bb24911352787db69a6784020 | |
parent | 0b098472371384f074706e51bd619ed911015d06 (diff) | |
download | mariadb-git-9c6eed72f04c15711d4de70bb8eb3f02bac8c30c.tar.gz |
Prepared_statement deployed instead of PREP_STMT.
libmysqld/lib_sql.cc:
Prepared_statement now resides entirely in sql_prepare.cc
Embedded versions of setup_params_data moved to sql_prepare.cc
sql/mysql_priv.h:
removed declarations for non-existing functions
sql/slave.cc:
no thd->init_for_queries() any more
sql/sql_class.cc:
added Statement and Statement_map classes.
PREP_STMT replaced with Statement (Prepared_statement) and moved to
sql_prepare.cc
sql/sql_class.h:
added Statement and Statement_map classes.
PREP_STMT replaced with Statement (Prepared_statement) and moved to
sql_prepare.cc
sql/sql_parse.cc:
thd->init_for_queries() doesn't exist any more
comment moved to proper place
sql/sql_prepare.cc:
PREP_STMT replaced with Prepared_statement
minor code cleanups
tests/client_test.c:
Later in the test we rely on order of rows, which normally is not defined.
My patch changes the order.
-rw-r--r-- | libmysqld/lib_sql.cc | 87 | ||||
-rw-r--r-- | sql/mysql_priv.h | 4 | ||||
-rw-r--r-- | sql/slave.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.cc | 140 | ||||
-rw-r--r-- | sql/sql_class.h | 208 | ||||
-rw-r--r-- | sql/sql_parse.cc | 8 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 593 | ||||
-rw-r--r-- | tests/client_test.c | 2 |
8 files changed, 613 insertions, 430 deletions
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 89012a84857..365e9bc820a 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -743,90 +743,3 @@ bool Protocol::convert_str(const char *from, uint length) } #endif -bool setup_params_data(st_prep_stmt *stmt) -{ - THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - ulong param_no= 0; - MYSQL_BIND *client_param= thd->client_params; - - DBUG_ENTER("setup_params_data"); - - for (;(param= (Item_param *)param_iterator++); client_param++) - { - setup_param_functions(param, client_param->buffer_type); - if (!param->long_data_supplied) - { - if (*client_param->is_null) - param->maybe_null= param->null_value= 1; - else - { - uchar *buff= (uchar*)client_param->buffer; - param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&buff, - client_param->length ? - *client_param->length : - client_param->buffer_length); - } - } - param_no++; - } - DBUG_RETURN(0); -} - -bool setup_params_data_withlog(st_prep_stmt *stmt) -{ - THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - MYSQL_BIND *client_param= thd->client_params; - - DBUG_ENTER("setup_params_data"); - - String str, *res, *query= new String(stmt->query->alloced_length()); - query->copy(*stmt->query); - - ulong param_no= 0; - uint32 length= 0; - - for (;(param= (Item_param *)param_iterator++); client_param++) - { - setup_param_functions(param, client_param->buffer_type); - if (param->long_data_supplied) - res= param->query_val_str(&str); - - else - { - if (*client_param->is_null) - { - param->maybe_null= param->null_value= 1; - res= &my_null_string; - } - else - { - uchar *buff= (uchar*)client_param->buffer; - param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&buff, - client_param->length ? - *client_param->length : - client_param->buffer_length); - res= param->query_val_str(&str); - } - } - if (query->replace(param->pos_in_query+length, 1, *res)) - DBUG_RETURN(1); - - length+= res->length()-1; - param_no++; - } - - if (alloc_query(stmt->thd, (char *)query->ptr(), query->length()+1)) - DBUG_RETURN(1); - - query->free(); - DBUG_RETURN(0); -} - diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 863c1bdd419..ff50993ad50 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -612,8 +612,6 @@ int mysqld_show_column_types(THD *thd); int mysqld_help (THD *thd, const char *text); /* sql_prepare.cc */ -int compare_prep_stmt(void *not_used, PREP_STMT *stmt, ulong *key); -void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used); bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); void mysql_stmt_execute(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet); @@ -855,7 +853,7 @@ extern I_List<THD> threads; extern I_List<NAMED_LIST> key_caches; extern MY_BITMAP temp_pool; extern String my_empty_string; -extern String my_null_string; +extern const String my_null_string; extern SHOW_VAR init_vars[],status_vars[], internal_vars[]; extern SHOW_COMP_OPTION have_isam; extern SHOW_COMP_OPTION have_innodb; diff --git a/sql/slave.cc b/sql/slave.cc index 8af38624df6..d2a7d397fa7 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3111,7 +3111,6 @@ slave_begin: sql_print_error("Failed during slave thread initialization"); goto err; } - thd->init_for_queries(); rli->sql_thd= thd; thd->temporary_tables = rli->save_temporary_tables; // restore temp tables pthread_mutex_lock(&LOCK_thread_count); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0469497790a..606ecdbecbb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -88,21 +88,20 @@ THD::THD():user_time(0), is_fatal_error(0), insert_id_used(0), rand_used(0), in_lock_tables(0), global_read_lock(0), bootstrap(0) { - host=user=priv_user=db=query=ip=0; - lex= &main_lex; + host= user= priv_user= db= ip=0; host_or_ip= "connecting host"; locked=killed=some_tables_deleted=no_errors=password= 0; query_start_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; - db_length=query_length=col_access=0; + db_length= col_access= 0; query_error= tmp_table_used= 0; next_insert_id=last_insert_id=0; open_tables= temporary_tables= handler_tables= derived_tables= 0; - handler_items=0; tmp_table=0; lock=locked_tables=0; used_tables=0; - cuted_fields= sent_row_count= current_stmt_id= 0L; + cuted_fields= sent_row_count= 0L; + statement_id_counter= 0UL; // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; start_time=(time_t) 0; @@ -138,7 +137,6 @@ THD::THD():user_time(0), is_fatal_error(0), server_id = ::server_id; slave_net = 0; command=COM_CONNECT; - set_query_id=1; #ifndef NO_EMBEDDED_ACCESS_CHECKS db_access=NO_ACCESS; #endif @@ -146,10 +144,11 @@ THD::THD():user_time(0), is_fatal_error(0), *scramble= '\0'; init(); + init_sql_alloc(&mem_root, // must be after init() + variables.query_alloc_block_size, + variables.query_prealloc_size); /* Initialize sub structures */ - bzero((char*) &mem_root,sizeof(mem_root)); bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); - bzero((char*) &con_root,sizeof(con_root)); bzero((char*) &warn_root,sizeof(warn_root)); init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); user_connect=(USER_CONN *)0; @@ -166,12 +165,6 @@ THD::THD():user_time(0), is_fatal_error(0), else bzero((char*) &user_var_events, sizeof(user_var_events)); - /* Prepared statements */ - last_prepared_stmt= 0; - init_tree(&prepared_statements, 0, 0, sizeof(PREP_STMT), - (qsort_cmp2) compare_prep_stmt, 1, - (tree_element_free) free_prep_stmt, 0); - /* Protocol */ protocol= &protocol_simple; // Default protocol protocol_simple.init(this); @@ -189,7 +182,9 @@ THD::THD():user_time(0), is_fatal_error(0), transaction.trans_log.end_of_file= max_binlog_cache_size; } #endif - + init_sql_alloc(&transaction.mem_root, + variables.trans_alloc_block_size, + variables.trans_prealloc_size); /* We need good random number initialization for new thread Just coping global one will not work @@ -233,22 +228,6 @@ void THD::init(void) /* - Init THD for query processing - - This has to be called once before we call mysql_parse() -*/ - -void THD::init_for_queries() -{ - init_sql_alloc(&mem_root, variables.query_alloc_block_size, - variables.query_prealloc_size); - init_sql_alloc(&transaction.mem_root, - variables.trans_alloc_block_size, - variables.trans_prealloc_size); -} - - -/* Do what's needed when one invokes change user SYNOPSIS @@ -276,7 +255,6 @@ void THD::cleanup(void) { DBUG_ENTER("THD::cleanup"); ha_rollback(this); - delete_tree(&prepared_statements); if (locked_tables) { lock=locked_tables; locked_tables=0; @@ -340,8 +318,6 @@ THD::~THD() safeFree(user); safeFree(db); safeFree(ip); - free_root(&mem_root,MYF(0)); - free_root(&con_root,MYF(0)); free_root(&warn_root,MYF(0)); free_root(&transaction.mem_root,MYF(0)); mysys_var=0; // Safety (shouldn't be needed) @@ -1193,6 +1169,102 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) } return 0; } + + +/* + Statement functions +*/ + +Statement::Statement(THD *thd) + :id(++thd->statement_id_counter), + query_id(thd->query_id), + set_query_id(1), + allow_sum_func(0), + command(thd->command), + lex(&main_lex), + query(0), + query_length(0), + free_list(0) +{ + init_sql_alloc(&mem_root, + thd->variables.query_alloc_block_size, + thd->variables.query_prealloc_size); +} + +/* + This constructor is called when statement is a subobject of THD: + Some variables are initialized in THD::init due to locking problems + This statement object will be used to +*/ + +Statement::Statement() + :id(0), + query_id(0), /* initialized later */ + set_query_id(1), + allow_sum_func(0), /* initialized later */ + command(COM_SLEEP), /* initialized later */ + lex(&main_lex), + query(0), /* these two are set */ + query_length(0), /* in alloc_query() */ + free_list(0) +{ + bzero((char *) &mem_root, sizeof(mem_root)); +} + + +Statement::Type Statement::type() const +{ + return STATEMENT; +} + + +void Statement::set_statement(Statement *stmt) +{ + id= stmt->id; + query_id= stmt->query_id; + set_query_id= stmt->set_query_id; + allow_sum_func= stmt->allow_sum_func; + command= stmt->command; + lex= stmt->lex; + query= stmt->query; + query_length= stmt->query_length; + free_list= stmt->free_list; + mem_root= stmt->mem_root; +} + + +Statement::~Statement() +{ + free_root(&mem_root, MYF(0)); +} + +C_MODE_START + +static byte * +get_statement_id_as_hash_key(const byte *record, uint *key_length, + my_bool not_used __attribute__((unused))) +{ + const Statement *statement= (const Statement *) record; + *key_length= sizeof(statement->id); + return (byte *) &((const Statement *) statement)->id; +} + +static void delete_statement_as_hash_key(void *key) +{ + delete (Statement *) key; +} + +C_MODE_END + +Statement_map::Statement_map() : + last_found_statement(0) +{ + enum { START_HASH_SIZE = 16 }; + hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0, + get_statement_id_as_hash_key, + delete_statement_as_hash_key, MYF(0)); +} + bool select_dumpvar::send_data(List<Item> &items) { List_iterator_fast<Item_func_set_user_var> li(vars); diff --git a/sql/sql_class.h b/sql/sql_class.h index 2a7523c890b..d663521b296 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -328,30 +328,6 @@ public: }; -/* This is a struct as it's allocated in tree_insert */ - -typedef struct st_prep_stmt -{ - THD *thd; - LEX lex; - Item_param **param; - Item *free_list; - MEM_ROOT mem_root; - String *query; - ulong stmt_id; - uint param_count; - uint last_errno; - char last_error[MYSQL_ERRMSG_SIZE]; - bool error_in_prepare, long_data_used; - bool log_full_query; -#ifndef EMBEDDED_LIBRARY - bool (*setup_params)(st_prep_stmt *stmt, uchar *pos, uchar *read_pos); -#else - bool (*setup_params_data)(st_prep_stmt *stmt); -#endif -} PREP_STMT; - - class delayed_insert; class select_result; @@ -428,12 +404,158 @@ struct system_variables }; void free_tmp_table(THD *thd, TABLE *entry); + +class Prepared_statement; + +/* + State of a single command executed against this connection. + One connection can contain a lot of simultaneously running statements, + some of which could be: + - prepared, that is, contain placeholders, + - opened as cursors. We maintain 1 to 1 relationship between + statement and cursor - if user wants to create another cursor for his + query, we create another statement for it. + To perform some action with statement we reset THD part to the state of + that statement, do the action, and then save back modified state from THD + to the statement. It will be changed in near future, and Statement will + be used explicitly. +*/ + +class Statement +{ + Statement(const Statement &rhs); /* not implemented: */ + Statement &operator=(const Statement &rhs); /* non-copyable */ +public: + /* FIXME: must be private */ + LEX main_lex; +public: + /* + Uniquely identifies each statement object in thread scope; change during + statement lifetime. FIXME: must be const + */ + ulong id; + + /* + Id of current query. Statement can be reused to execute several queries + query_id is global in context of the whole MySQL server. + ID is automatically generated from mutex-protected counter. + It's used in handler code for various purposes: to check which columns + from table are necessary for this select, to check if it's necessary to + update auto-updatable fields (like auto_increment and timestamp). + */ + ulong query_id; + /* + - if set_query_id=1, we set field->query_id for all fields. In that case + field list can not contain duplicates. + */ + bool set_query_id; + /* + This variable is used in post-parse stage to declare that sum-functions, + or functions which have sense only if GROUP BY is present, are allowed. + For example in queries + SELECT MIN(i) FROM foo + SELECT GROUP_CONCAT(a, b, MIN(i)) FROM ... GROUP BY ... + MIN(i) have no sense. + Though it's grammar-related issue, it's hard to catch it out during the + parse stage because GROUP BY clause goes in the end of query. This + variable is mainly used in setup_fields/fix_fields. + See item_sum.cc for details. + */ + bool allow_sum_func; + /* + Type of current query: COM_PREPARE, COM_QUERY, etc. Set from + first byte of the packet in do_command() + */ + enum enum_server_command command; + + LEX *lex; // parse tree descriptor + /* + Points to the query associated with this statement. It's const, but + we need to declare it char * because all table handlers are written + in C and need to point to it. + */ + char *query; + uint32 query_length; // current query length + /* + List of items created in the parser for this query. Every item puts + itself to the list on creation (see Item::Item() for details)) + */ + Item *free_list; + MEM_ROOT mem_root; + +public: + /* We build without RTTI, so dynamic_cast can't be used. */ + enum Type + { + STATEMENT, + PREPARED_STATEMENT + }; + + /* + This constructor is called when statement is a subobject of THD: + some variables are initialized in THD::init due to locking problems + */ + Statement(); + + Statement(THD *thd); + virtual ~Statement(); + + /* Assign execution context (note: not all members) of given stmt to self */ + void set_statement(Statement *stmt); + /* return class type */ + virtual Type type() const; +}; + + +/* + Used to seek all existing statements in the connection + Deletes all statements in destructor. +*/ + +class Statement_map +{ +public: + Statement_map(); + + int insert(Statement *statement) + { + int rc= my_hash_insert(&st_hash, (byte *) statement); + if (rc == 0) + last_found_statement= statement; + return rc; + } + + Statement *find(ulong id) + { + if (last_found_statement == 0 || id != last_found_statement->id) + last_found_statement= (Statement *) hash_search(&st_hash, (byte *) &id, + sizeof(id)); + return last_found_statement; + } + void erase(Statement *statement) + { + if (statement == last_found_statement) + last_found_statement= 0; + hash_delete(&st_hash, (byte *) statement); + } + + ~Statement_map() + { + hash_free(&st_hash); + } +private: + HASH st_hash; + Statement *last_found_statement; +}; + + /* For each client connection we create a separate thread with THD serving as a thread/connection descriptor */ -class THD :public ilink +class THD :public ilink, + public Statement { public: #ifdef EMBEDDED_LIBRARY @@ -446,23 +568,25 @@ public: ulong extra_length; #endif NET net; // client connection descriptor - LEX main_lex; - LEX *lex; // parse tree descriptor - MEM_ROOT mem_root; // 1 command-life memory pool - MEM_ROOT con_root; // connection-life memory MEM_ROOT warn_root; // For warnings and errors Protocol *protocol; // Current protocol Protocol_simple protocol_simple; // Normal protocol Protocol_prep protocol_prep; // Binary protocol HASH user_vars; // hash for user variables - TREE prepared_statements; String packet; // dynamic buffer for network I/O struct sockaddr_in remote; // client socket address struct rand_struct rand; // used for authentication struct system_variables variables; // Changeable local variables pthread_mutex_t LOCK_delete; // Locked before thd is deleted - char *query; // Points to the current query, + /* all prepared statements and cursors of this connection */ + Statement_map stmt_map; + /* + keeps THD state while it is used for active statement + Note, that double free_root() is safe, so we don't need to do any + special cleanup for it in THD destructor. + */ + Statement stmt_backup; /* A pointer to the stack frame of handle_one_connection(), which is called first in the thread for handling a client @@ -502,7 +626,6 @@ public: and are still in use by this thread */ TABLE *open_tables,*temporary_tables, *handler_tables, *derived_tables; - // TODO: document the variables below MYSQL_LOCK *lock; /* Current locks */ MYSQL_LOCK *locked_tables; /* Tables locked with LOCK */ /* @@ -511,12 +634,10 @@ public: chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ ULL *ull; - PREP_STMT *last_prepared_stmt; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; - enum enum_server_command command; uint32 server_id; uint32 file_id; // for LOAD DATA INFILE /* @@ -549,7 +670,6 @@ public: free_root(&mem_root,MYF(MY_KEEP_PREALLOC)); } } transaction; - Item *free_list, *handler_items; Field *dupp_field; #ifndef __WIN__ sigset_t signals,block_signals; @@ -580,18 +700,25 @@ public: USER_CONN *user_connect; CHARSET_INFO *db_charset; List<TABLE> temporary_tables_should_be_free; // list of temporary tables + /* + FIXME: this, and some other variables like 'count_cuted_fields' + maybe should be statement/cursor local, that is, moved to Statement + class. With current implementation warnings produced in each prepared + statement/cursor settle here. + */ List <MYSQL_ERROR> warn_list; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint total_warn_count; - ulong query_id, warn_id, version, options, thread_id, col_access; - ulong current_stmt_id; + ulong warn_id, version, options, thread_id, col_access; + + /* Statement id is thread-wide. This counter is used to generate ids */ + ulong statement_id_counter; ulong rand_saved_seed1, rand_saved_seed2; ulong row_count; // Row counter, mainly for errors and warnings long dbug_thread_id; pthread_t real_id; uint current_tablenr,tmp_table; uint server_status,open_options,system_thread; - uint32 query_length; uint32 db_length; uint select_number; //number of select (used for EXPLAIN) /* variables.transaction_isolation is reset to this after each commit */ @@ -604,9 +731,9 @@ public: char scramble[SCRAMBLE_LENGTH+1]; bool slave_thread; - bool set_query_id,locked,some_tables_deleted; + bool locked, some_tables_deleted; bool last_cuted_field; - bool no_errors, allow_sum_func, password, is_fatal_error; + bool no_errors, password, is_fatal_error; bool query_start_used,last_insert_id_used,insert_id_used,rand_used; bool in_lock_tables,global_read_lock; bool query_error, bootstrap, cleanup_done; @@ -634,7 +761,6 @@ public: void init(void); void change_user(void); - void init_for_queries(); void cleanup(void); bool store_globals(); #ifdef SIGNAL_WITH_VIO_CLOSE diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3f2325de703..85659f2d2a2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -974,7 +974,6 @@ pthread_handler_decl(handle_one_connection,arg) thd->proc_info=0; thd->set_time(); - thd->init_for_queries(); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) @@ -1055,7 +1054,6 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME)); buff= (char*) thd->net.buff; - thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { uint length=(uint) strlen(buff); @@ -1221,13 +1219,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { NET *net= &thd->net; bool error= 0; + DBUG_ENTER("dispatch_command"); + + thd->command=command; /* Commands which will always take a long time should be marked with this so that they will not get logged to the slow query log */ - DBUG_ENTER("dispatch_command"); - - thd->command=command; thd->slow_command=FALSE; thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bb5f27672fe..f60e0db58d0 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -71,127 +71,110 @@ Long data handling: #include "sql_acl.h" #include "sql_select.h" // for JOIN #include <m_ctype.h> // for isspace() +#ifdef EMBEDDED_LIBRARY +/* include MYSQL_BIND headers */ +#include <mysql.h> +#endif -#define IS_PARAM_NULL(pos, param_no) (pos[param_no/8] & (1 << (param_no & 7))) +const String my_null_string("NULL", 4, default_charset_info); -#define STMT_QUERY_LOG_LENGTH 8192 +/****************************************************************************** + Prepared_statement: statement which can contain placeholders +******************************************************************************/ -#ifdef EMBEDDED_LIBRARY -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos, ulong data_len) +class Prepared_statement: public Statement +{ +public: + THD *thd; + Item_param **param; /* array of all placeholders */ + uint param_count; + uint last_errno; + char last_error[MYSQL_ERRMSG_SIZE]; + bool error_in_prepare, long_data_used; + bool log_full_query; +#ifndef EMBEDDED_LIBRARY + bool (*setup_params)(Prepared_statement *st, uchar *pos, uchar *read_pos); #else -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos) + bool (*setup_params_data)(Prepared_statement *st); #endif +public: + Prepared_statement(THD *thd_arg); + virtual ~Prepared_statement(); + virtual Statement::Type type() const; +}; -String my_null_string("NULL", 4, default_charset_info); -/* - Find prepared statement in thd +/****************************************************************************** + Implementation +******************************************************************************/ - SYNOPSIS - find_prepared_statement() - thd Thread handler - stmt_id Statement id server specified to the client on prepare - RETURN VALUES - 0 error. In this case the error is sent with my_error() - ptr Pointer to statement -*/ - -static PREP_STMT *find_prepared_statement(THD *thd, ulong stmt_id, - const char *when) +inline bool is_param_null(const uchar *pos, ulong param_no) { - PREP_STMT *stmt; - DBUG_ENTER("find_prepared_statement"); - DBUG_PRINT("enter",("stmt_id: %d", stmt_id)); - - if (thd->last_prepared_stmt && thd->last_prepared_stmt->stmt_id == stmt_id) - DBUG_RETURN(thd->last_prepared_stmt); - if ((stmt= (PREP_STMT*) tree_search(&thd->prepared_statements, &stmt_id, - (void*) 0))) - DBUG_RETURN (thd->last_prepared_stmt= stmt); - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, when); - DBUG_RETURN(0); + return pos[param_no/8] & (1 << (param_no & 7)); } -/* - Compare two prepared statements; Used to find a prepared statement -*/ +enum { STMT_QUERY_LOG_LENGTH= 8192 }; -int compare_prep_stmt(void *not_used, PREP_STMT *stmt, ulong *key) -{ - return (stmt->stmt_id == *key) ? 0 : (stmt->stmt_id < *key) ? -1 : 1; -} +#ifdef EMBEDDED_LIBRARY +#define SETUP_PARAM_FUNCTION(fn_name) \ +static void fn_name(Item_param *param, uchar **pos, ulong data_len) +#else +#define SETUP_PARAM_FUNCTION(fn_name) \ +static void fn_name(Item_param *param, uchar **pos) +#endif /* - Free prepared statement. - - SYNOPSIS - standard tree_element_free function. - - DESCRIPTION - We don't have to free the stmt itself as this was stored in the tree - and will be freed when the node is deleted + Seek prepared statement in statement map by id: returns zero if statement + was not found, pointer otherwise. */ -void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used) -{ - my_free((char *)stmt->param, MYF(MY_ALLOW_ZERO_PTR)); - if (stmt->query) - stmt->query->free(); - free_items(stmt->free_list); - free_root(&stmt->mem_root, MYF(0)); +static Prepared_statement * +find_prepared_statement(THD *thd, ulong id, const char *where) +{ + Statement *stmt= thd->stmt_map.find(id); + + if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT) + { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where); + send_error(thd); + return 0; + } + return (Prepared_statement *) stmt; } + /* Send prepared stmt info to client after prepare */ #ifndef EMBEDDED_LIBRARY -static bool send_prep_stmt(PREP_STMT *stmt, uint columns) +static bool send_prep_stmt(Prepared_statement *stmt, uint columns) { - NET *net=&stmt->thd->net; + NET *net= &stmt->thd->net; char buff[9]; buff[0]= 0; - int4store(buff+1, stmt->stmt_id); + int4store(buff+1, stmt->id); int2store(buff+5, columns); int2store(buff+7, stmt->param_count); - /* This should be fixed to work with prepared statements - */ + /* This should be fixed to work with prepared statements */ return (my_net_write(net, buff, sizeof(buff)) || net_flush(net)); } #else -static bool send_prep_stmt(PREP_STMT *stmt, uint columns __attribute__((unused))) +static bool send_prep_stmt(Prepared_statement *stmt, + uint columns __attribute__((unused))) { THD *thd= stmt->thd; - thd->client_stmt_id= stmt->stmt_id; + thd->client_stmt_id= stmt->id; thd->client_param_count= stmt->param_count; thd->net.last_errno= 0; return 0; } -#endif /*!EMBEDDED_LIBRAYR*/ - -/* - Send information about all item parameters +#endif /*!EMBEDDED_LIBRARY*/ - TODO: Not yet ready -*/ - -static bool send_item_params(PREP_STMT *stmt) -{ -#if 0 - char buff[1]; - buff[0]=0; - if (my_net_write(&stmt->thd->net, buff, sizeof(buff))) - return 1; - send_eof(stmt->thd); -#endif - return 0; -} /* Read the length of the parameter data and retun back to @@ -418,17 +401,20 @@ void setup_param_functions(Item_param *param, uchar param_type) and if binary/update log is set, generate the valid query. */ -static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos) +static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, + uchar *read_pos) { THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; + List<Item> ¶ms= stmt->lex->param_list; List_iterator<Item> param_iterator(params); Item_param *param; - DBUG_ENTER("insert_params_withlog"); - String str, query, *res; + String str, query; + const String *res; + + DBUG_ENTER("insert_params_withlog"); - if (query.copy(*stmt->query)) + if (query.copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); ulong param_no= 0; @@ -438,10 +424,9 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos) { if (param->long_data_supplied) res= param->query_val_str(&str); - else { - if (IS_PARAM_NULL(pos,param_no)) + if (is_param_null(pos,param_no)) { param->maybe_null= param->null_value= 1; res= &my_null_string; @@ -461,23 +446,26 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos) } if (alloc_query(thd, (char *)query.ptr(), query.length()+1)) DBUG_RETURN(1); + DBUG_RETURN(0); } -static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos) + +static bool insert_params(Prepared_statement *stmt, uchar *pos, + uchar *read_pos) { - THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; + List<Item> ¶ms= stmt->lex->param_list; List_iterator<Item> param_iterator(params); Item_param *param; - DBUG_ENTER("insert_params"); - ulong param_no= 0; + + DBUG_ENTER("insert_params"); + while ((param= (Item_param *)param_iterator++)) { if (!param->long_data_supplied) { - if (IS_PARAM_NULL(pos,param_no)) + if (is_param_null(pos,param_no)) param->maybe_null= param->null_value= 1; else { @@ -490,17 +478,18 @@ static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos) DBUG_RETURN(0); } -static bool setup_params_data(PREP_STMT *stmt) +static bool setup_params_data(Prepared_statement *stmt) { - THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; + List<Item> ¶ms= stmt->lex->param_list; List_iterator<Item> param_iterator(params); Item_param *param; - DBUG_ENTER("setup_params_data"); - uchar *pos=(uchar*) thd->net.read_pos+1+MYSQL_STMT_HEADER; //skip header + uchar *pos= (uchar*) stmt->thd->net.read_pos + 1 + + MYSQL_STMT_HEADER; //skip header uchar *read_pos= pos+(stmt->param_count+7) / 8; //skip null bits + DBUG_ENTER("setup_params_data"); + if (*read_pos++) //types supplied / first execute { /* @@ -518,6 +507,91 @@ static bool setup_params_data(PREP_STMT *stmt) DBUG_RETURN(0); } +#else + +bool setup_params_data(Prepared_statement *stmt) +{ + List<Item> ¶ms= stmt->lex->param_list; + List_iterator<Item> param_iterator(params); + Item_param *param; + MYSQL_BIND *client_param= stmt->thd->client_params; + + DBUG_ENTER("setup_params_data"); + + for (;(param= (Item_param *)param_iterator++); client_param++) + { + setup_param_functions(param, client_param->buffer_type); + if (!param->long_data_supplied) + { + if (*client_param->is_null) + param->maybe_null= param->null_value= 1; + else + { + uchar *buff= (uchar*)client_param->buffer; + param->maybe_null= param->null_value= 0; + param->setup_param_func(param,&buff, + client_param->length ? + *client_param->length : + client_param->buffer_length); + } + } + } + DBUG_RETURN(0); +} + +bool setup_params_data_withlog(Prepared_statement *stmt) +{ + THD *thd= stmt->thd; + List<Item> ¶ms= stmt->lex->param_list; + List_iterator<Item> param_iterator(params); + Item_param *param; + MYSQL_BIND *client_param= thd->client_params; + + String str, query; + const String *res; + + DBUG_ENTER("setup_params_data_withlog"); + + if (query.copy(stmt->query, stmt->query_length, default_charset_info)) + DBUG_RETURN(1); + + uint32 length= 0; + + for (;(param= (Item_param *)param_iterator++); client_param++) + { + setup_param_functions(param, client_param->buffer_type); + if (param->long_data_supplied) + res= param->query_val_str(&str); + else + { + if (*client_param->is_null) + { + param->maybe_null= param->null_value= 1; + res= &my_null_string; + } + else + { + uchar *buff= (uchar*)client_param->buffer; + param->maybe_null= param->null_value= 0; + param->setup_param_func(param,&buff, + client_param->length ? + *client_param->length : + client_param->buffer_length); + res= param->query_val_str(&str); + } + } + if (query.replace(param->pos_in_query+length, 1, *res)) + DBUG_RETURN(1); + + length+= res->length()-1; + } + + if (alloc_query(thd, (char *) query.ptr(), query.length()+1)) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + #endif /*!EMBEDDED_LIBRARY*/ /* @@ -526,7 +600,7 @@ static bool setup_params_data(PREP_STMT *stmt) - fields count */ -static bool mysql_test_insert_fields(PREP_STMT *stmt, +static bool mysql_test_insert_fields(Prepared_statement *stmt, TABLE_LIST *table_list, List<Item> &fields, List<List_item> &values_list) @@ -535,18 +609,18 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt, TABLE *table; List_iterator_fast<List_item> its(values_list); List_item *values; + DBUG_ENTER("mysql_test_insert_fields"); #ifndef NO_EMBEDDED_ACCESS_CHECKS - my_bool update=(thd->lex->value_list.elements ? UPDATE_ACL : 0); - ulong privilege= (thd->lex->duplicates == DUP_REPLACE ? + my_bool update=(stmt->lex->value_list.elements ? UPDATE_ACL : 0); + ulong privilege= (stmt->lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL | update); - if (check_access(thd,privilege,table_list->db, &table_list->grant.privilege,0,0) || (grant_option && check_grant(thd,privilege,table_list,0,0))) DBUG_RETURN(1); -#endif +#endif if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(1); table= table_list->table; @@ -574,7 +648,7 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt, } } } - if (send_prep_stmt(stmt, 0) || send_item_params(stmt)) + if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); DBUG_RETURN(0); } @@ -589,13 +663,14 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt, and return no fields information back to client. */ -static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, +static bool mysql_test_upd_fields(Prepared_statement *stmt, + TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, COND *conds) { THD *thd= stmt->thd; - DBUG_ENTER("mysql_test_upd_fields"); + DBUG_ENTER("mysql_test_upd_fields"); #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_access(thd,UPDATE_ACL,table_list->db, &table_list->grant.privilege,0,0) || @@ -613,7 +688,7 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, Currently return only column list info only, and we are not sending any info on where clause. */ - if (send_prep_stmt(stmt, 0) || send_item_params(stmt)) + if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); DBUG_RETURN(0); } @@ -630,7 +705,9 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, And send column list fields info back to client. */ -static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, + +static bool mysql_test_select_fields(Prepared_statement *stmt, + TABLE_LIST *tables, uint wild_num, List<Item> &fields, COND *conds, uint og_num, ORDER *order, ORDER *group, @@ -640,8 +717,9 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, SELECT_LEX *select_lex) { THD *thd= stmt->thd; - LEX *lex= &thd->main_lex; - select_result *result= thd->lex->result; + LEX *lex= stmt->lex; + select_result *result= lex->result; + DBUG_ENTER("mysql_test_select_fields"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -663,12 +741,12 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, if (lex->describe) { - if (send_prep_stmt(stmt, 0) || send_item_params(stmt)) + if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); - } + } else { - fix_tables_pointers(thd->lex->all_selects_list); + fix_tables_pointers(lex->all_selects_list); if (!result && !(result= new select_send())) { send_error(thd, ER_OUT_OF_RESOURCES); @@ -683,11 +761,11 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, select_lex, unit)) DBUG_RETURN(1); if (send_prep_stmt(stmt, fields.elements) || - thd->protocol_simple.send_fields(&fields, 0) || + thd->protocol_simple.send_fields(&fields, 0) #ifndef EMBEDDED_LIBRARY - net_flush(&thd->net) || + || net_flush(&thd->net) #endif - send_item_params(stmt)) + ) DBUG_RETURN(1); join->cleanup(); } @@ -699,19 +777,18 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, Send the prepare query results back to client */ -static bool send_prepare_results(PREP_STMT *stmt) +static bool send_prepare_results(Prepared_statement *stmt) { THD *thd= stmt->thd; - LEX *lex= &thd->main_lex; - enum enum_sql_command sql_command= thd->lex->sql_command; + LEX *lex= stmt->lex; + enum enum_sql_command sql_command= lex->sql_command; + DBUG_ENTER("send_prepare_results"); DBUG_PRINT("enter",("command: %d, param_count: %ld", sql_command, lex->param_count)); /* Setup prepared stmt */ stmt->param_count= lex->param_count; - stmt->free_list= thd->free_list; // Save items used in stmt - thd->free_list= 0; SELECT_LEX *select_lex= &lex->select_lex; TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; @@ -769,56 +846,13 @@ abort: } /* - Parse the prepare query -*/ - -static bool parse_prepare_query(PREP_STMT *stmt, - char *packet, uint length) -{ - bool error= 1; - THD *thd= stmt->thd; - DBUG_ENTER("parse_prepare_query"); - - mysql_log.write(thd,COM_PREPARE,"%s",packet); - mysql_init_query(thd); - LEX *lex=lex_start(thd, (uchar*) packet, length); - lex->safe_to_cache_query= 0; - thd->lex->param_count= 0; - if (!yyparse((void *)thd) && !thd->is_fatal_error) - error= send_prepare_results(stmt); - lex_end(lex); - DBUG_RETURN(error); -} - -/* Initialize parameter items in statement */ -static bool init_param_items(PREP_STMT *stmt) +static bool init_param_items(Prepared_statement *stmt) { - THD *thd= stmt->thd; - List<Item> ¶ms= thd->lex->param_list; Item_param **to; - uint32 length= thd->query_length; - stmt->lex= thd->main_lex; - - if (mysql_bin_log.is_open() || mysql_update_log.is_open()) - { - stmt->log_full_query= 1; -#ifndef EMBEDDED_LIBRARY - stmt->setup_params= insert_params_withlog; -#else - stmt->setup_params_data= setup_params_data_withlog; -#endif - } - else -#ifndef EMBEDDED_LIBRARY - stmt->setup_params= insert_params; // not fully qualified query -#else - stmt->setup_params_data= setup_params_data; -#endif - if (!stmt->param_count) stmt->param= (Item_param **)0; else @@ -828,44 +862,12 @@ static bool init_param_items(PREP_STMT *stmt) MYF(MY_WME)))) return 1; - if (stmt->log_full_query) - { - length= thd->query_length+(stmt->param_count*2)+1; - - if ( length < STMT_QUERY_LOG_LENGTH ) - length= STMT_QUERY_LOG_LENGTH; - } - List_iterator<Item> param_iterator(params); + List_iterator<Item> param_iterator(stmt->lex->param_list); while ((*(to++)= (Item_param *)param_iterator++)); } - stmt->query= new String(length); - stmt->query->copy(thd->query, thd->query_length, default_charset_info); return 0; } -/* - Initialize stmt execution -*/ - -static void init_stmt_execute(PREP_STMT *stmt) -{ - THD *thd= stmt->thd; - TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first; - - /* - TODO: When the new table structure is ready, then have a status bit - to indicate the table is altered, and re-do the setup_* - and open the tables back. - */ - for (; tables ; tables= tables->next) - tables->table= 0; //safety - nasty init - - if (!(stmt->log_full_query && stmt->param_count)) - { - thd->query= stmt->query->c_ptr(); - thd->query_length= stmt->query->length(); - } -} /* Parse the query and send the total number of parameters @@ -877,58 +879,73 @@ static void init_stmt_execute(PREP_STMT *stmt) If parameter markers are found in the query, then store the information using Item_param along with maintaining a list in lex->param_list, so that a fast and direct - retrieveal can be made without going through all field + retrieval can be made without going through all field items. */ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { - MEM_ROOT thd_root= thd->mem_root; - PREP_STMT stmt; - SELECT_LEX *sl; + LEX *lex; + Prepared_statement *stmt= new Prepared_statement(thd); + DBUG_ENTER("mysql_stmt_prepare"); - bzero((char*) &stmt, sizeof(stmt)); - - stmt.stmt_id= ++thd->current_stmt_id; - init_sql_alloc(&stmt.mem_root, - thd->variables.query_alloc_block_size, - thd->variables.query_prealloc_size); - - stmt.thd= thd; - stmt.thd->mem_root= stmt.mem_root; + if (stmt == 0) + DBUG_RETURN(0); + + if (thd->stmt_map.insert(stmt)) + goto insert_stmt_err; - if (alloc_query(stmt.thd, packet, packet_length)) - goto err; + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); - if (parse_prepare_query(&stmt, thd->query, thd->query_length)) - goto err; + if (alloc_query(thd, packet, packet_length)) + goto alloc_query_err; + + mysql_log.write(thd, COM_PREPARE, "%s", packet); + + lex= lex_start(thd, (uchar *) thd->query, thd->query_length); + mysql_init_query(thd); + lex->safe_to_cache_query= 0; + lex->param_count= 0; + + if (yyparse((void *)thd) || thd->is_fatal_error || send_prepare_results(stmt)) + goto yyparse_err; + + lex_end(lex); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); // save WHERE clause pointers to avoid damaging they by optimisation - for (sl= thd->lex->all_selects_list; + for (SELECT_LEX *sl= thd->lex->all_selects_list; sl; sl= sl->next_select_in_list()) { sl->prep_where= sl->where; } - - if (init_param_items(&stmt)) - goto err; + stmt->set_statement(thd); + thd->set_statement(&thd->stmt_backup); + + if (init_param_items(stmt)) + goto init_param_err; + + stmt->command= COM_EXECUTE; // set it only once here - - stmt.mem_root= stmt.thd->mem_root; - tree_insert(&thd->prepared_statements, (void *)&stmt, 0, (void *)0); - thd->mem_root= thd_root; // restore main mem_root DBUG_RETURN(0); -err: - stmt.mem_root= stmt.thd->mem_root; - free_prep_stmt(&stmt, free_free, (void*) 0); - thd->mem_root= thd_root; // restore main mem_root +yyparse_err: + lex_end(lex); + stmt->set_statement(thd); + thd->set_statement(&thd->stmt_backup); +init_param_err: +alloc_query_err: + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + DBUG_RETURN(1); +insert_stmt_err: + delete stmt; DBUG_RETURN(1); } @@ -936,7 +953,7 @@ err: /* Executes previously prepared query - If there is any parameters(thd->param_count), then replace + If there is any parameters (stmt->param_count), then replace markers with the data supplied from client, and then execute the query */ @@ -944,15 +961,12 @@ err: void mysql_stmt_execute(THD *thd, char *packet) { ulong stmt_id= uint4korr(packet); - PREP_STMT *stmt; - SELECT_LEX *sl; - DBUG_ENTER("mysql_stmt_execute"); + Prepared_statement *stmt; - if (!(stmt=find_prepared_statement(thd, stmt_id, "execute"))) - { - send_error(thd); + DBUG_ENTER("mysql_stmt_execute"); + + if (!(stmt= find_prepared_statement(thd, stmt_id, "execute"))) DBUG_VOID_RETURN; - } /* Check if we got an error when sending long data */ if (stmt->error_in_prepare) @@ -961,10 +975,28 @@ void mysql_stmt_execute(THD *thd, char *packet) DBUG_VOID_RETURN; } - LEX thd_lex= thd->main_lex; - thd->main_lex= stmt->lex; - - for (sl= stmt->lex.all_selects_list; + /* + XXX: while thd->query_id is incremented for each command, stmt->query_id + holds query_id of prepare stage. Keeping old query_id seems to be more + natural, but differs from the way prepared statements work in 4.1: + */ + /* stmt->query_id= thd->query_id; */ + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); + + /* + To make sure that all runtime data is stored in its own memory root and + does not interfere with data possibly present in thd->mem_root. + This root is cleaned up in the end of execution. + FIXME: to be replaced with more efficient approach, and verified why we + can not use thd->mem_root safely. + */ + init_sql_alloc(&thd->mem_root, + thd->variables.query_alloc_block_size, + thd->variables.query_prealloc_size); + + + for (SELECT_LEX *sl= stmt->lex->all_selects_list; sl; sl= sl->next_select_in_list()) { @@ -975,7 +1007,16 @@ void mysql_stmt_execute(THD *thd, char *packet) sl->where= sl->prep_where->copy_andor_structure(thd); DBUG_ASSERT(sl->join == 0); } - init_stmt_execute(stmt); + + /* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* + and open the tables back. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first; + tables; + tables= tables->next) + tables->table= 0; // safety - nasty init #ifndef EMBEDDED_LIBRARY if (stmt->param_count && setup_params_data(stmt)) @@ -1001,7 +1042,8 @@ void mysql_stmt_execute(THD *thd, char *packet) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - thd->main_lex= thd_lex; + free_root(&thd->mem_root, MYF(0)); + thd->set_statement(&thd->stmt_backup); DBUG_VOID_RETURN; } @@ -1023,14 +1065,12 @@ void mysql_stmt_execute(THD *thd, char *packet) void mysql_stmt_reset(THD *thd, char *packet) { ulong stmt_id= uint4korr(packet); - PREP_STMT *stmt; + Prepared_statement *stmt; + DBUG_ENTER("mysql_stmt_reset"); if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) - { - send_error(thd); DBUG_VOID_RETURN; - } stmt->error_in_prepare= 0; Item_param *item= *stmt->param, *end= item + stmt->param_count; @@ -1053,15 +1093,15 @@ void mysql_stmt_reset(THD *thd, char *packet) void mysql_stmt_free(THD *thd, char *packet) { ulong stmt_id= uint4korr(packet); + Prepared_statement *stmt; + DBUG_ENTER("mysql_stmt_free"); - if (!find_prepared_statement(thd, stmt_id, "close")) - { - send_error(thd); // Not seen by the client + if (!(stmt= find_prepared_statement(thd, stmt_id, "close"))) DBUG_VOID_RETURN; - } - tree_delete(&thd->prepared_statements, (void*) &stmt_id, (void *)0); - thd->last_prepared_stmt= (PREP_STMT *)0; + + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); DBUG_VOID_RETURN; } @@ -1087,7 +1127,8 @@ void mysql_stmt_free(THD *thd, char *packet) void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) { - PREP_STMT *stmt; + Prepared_statement *stmt; + DBUG_ENTER("mysql_stmt_get_longdata"); #ifndef EMBEDDED_LIBRARY @@ -1103,14 +1144,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) uint param_number= uint2korr(pos+4); if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) - { - /* - There is a chance that the client will never see this as - it doesn't expect an answer from this call... - */ - send_error(thd); DBUG_VOID_RETURN; - } #ifndef EMBEDDED_LIBRARY if (param_number >= stmt->param_count) @@ -1134,3 +1168,46 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) DBUG_VOID_RETURN; } + +Prepared_statement::Prepared_statement(THD *thd_arg) + :Statement(thd_arg), + thd(thd_arg), + param(0), + param_count(0), + last_errno(0), + error_in_prepare(0), + long_data_used(0), + log_full_query(0) +{ + *last_error= '\0'; + if (mysql_bin_log.is_open()) + { + log_full_query= 1; +#ifndef EMBEDDED_LIBRARY + setup_params= insert_params_withlog; +#else + setup_params_data= setup_params_data_withlog; +#endif + } + else +#ifndef EMBEDDED_LIBRARY + setup_params= insert_params; // not fully qualified query +#else + setup_params_data= setup_params_data; +#endif +} + + +Prepared_statement::~Prepared_statement() +{ + my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR)); + free_items(free_list); +} + + +Statement::Type Prepared_statement::type() const +{ + return PREPARED_STATEMENT; +} + + diff --git a/tests/client_test.c b/tests/client_test.c index 464d5b632ae..66637dcb04b 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -7350,7 +7350,7 @@ static void test_fetch_column() rc = mysql_query(mysql, "insert into test_column(c2) values('venu'),('mysql')"); myquery(rc); - stmt = mysql_prepare(mysql,"select * from test_column",50); + stmt = mysql_prepare(mysql,"select * from test_column order by c2 desc", 50); mystmt_init(stmt); bind[0].buffer_type= MYSQL_TYPE_LONG; |