From: konstantin Date: December 16 2003 1:15pm Subject: bk commit into 5.0 tree (konstantin:1.1642) List-Archive: http://lists.mysql.com/internals/11378 Message-Id: <20031216131520.92F6C5987@oak.local> Below is the list of changes that have just been committed into a local 5.0 repository of kostja. When kostja does a push these changes will be propagated to the main repository and, within 24 hours after the push, to the public repository. For information on how to access the public repository see http://www.mysql.com/doc/I/n/Installing_source_tree.html ChangeSet 1.1642 03/12/16 16:15:16 konstantin@stripped +5 -0 attempt to make prepared statements work in 5.0: st_prep_stmt replaced with Prepared_statement sql/sql_prepare.cc 1.51 03/12/16 16:15:14 konstantin@stripped +339 -255 rewritten to use Prepared_statement instead of st_prep_stmt sql/sql_class.h 1.171 03/12/16 16:15:14 konstantin@stripped +31 -33 Few prepared-statement related fixes: st_prep_stmt removed sql/sql_class.cc 1.135 03/12/16 16:15:14 konstantin@stripped +31 -17 Statement:set_statement() added constructor fixes sql/mysql_priv.h 1.203 03/12/16 16:15:14 konstantin@stripped +1 -3 no need for compare_prep_stmt/free_prep_stmt: prepared statements now stored in the map by pointer, not by value. libmysqld/lib_sql.cc 1.79 03/12/16 16:15:14 konstantin@stripped +0 -87 prepared statements functions moved to sql_prepare.cc # This is a BitKeeper patch. What follows are the unified diffs for the # set of deltas contained in the patch. The rest of the patch, the part # that BitKeeper cares about, is below these diffs. # User: konstantin # Host: oak.local # Root: /home/kostja/mysql/mysql-5.0-c --- 1.202/sql/mysql_priv.h Mon Dec 1 17:10:17 2003 +++ 1.203/sql/mysql_priv.h Tue Dec 16 16:15:14 2003 @@ -615,8 +615,6 @@ 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); @@ -858,7 +856,7 @@ extern I_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 struct show_table_type_st table_type_vars[]; extern SHOW_COMP_OPTION have_isam; --- 1.134/sql/sql_class.cc Thu Dec 4 22:08:24 2003 +++ 1.135/sql/sql_class.cc Tue Dec 16 16:15:14 2003 @@ -171,12 +171,6 @@ 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); @@ -269,7 +263,6 @@ { DBUG_ENTER("THD::cleanup"); ha_rollback(this); - delete_tree(&prepared_statements); if (locked_tables) { lock=locked_tables; locked_tables=0; @@ -1274,13 +1267,13 @@ Statement::Statement(THD *thd) :id(++thd->statement_id_counter), - query_id(0), /* initialized later */ + query_id(thd->query_id), set_query_id(1), - allow_sum_func(0), /* initialized later */ - command(COM_SLEEP), /* initialized later */ + allow_sum_func(0), + command(thd->command), lex(&main_lex), - query(0), /* these two are set */ - query_length(0), /* in alloc_query() */ + query(0), + query_length(0), free_list(0) { init_sql_alloc(&mem_root, @@ -1291,16 +1284,37 @@ Statement::Statement() :id(0), - query_id(0), + query_id(0), /* initialized later */ set_query_id(1), - allow_sum_func(0), - command(COM_SLEEP), + allow_sum_func(0), /* initialized later */ + command(COM_SLEEP), /* initialized later */ lex(&main_lex), - query(0), - query_length(0), + query(0), /* these two are set */ + query_length(0), /* in alloc_query() */ free_list(0) { bzero((char *) &mem_root, sizeof(mem_root)); +} + + +Prepared_statement *Statement::as_prepared_statement() +{ + return 0; +} + + +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; } --- 1.170/sql/sql_class.h Thu Dec 4 22:08:24 2003 +++ 1.171/sql/sql_class.h Tue Dec 16 16:15:14 2003 @@ -330,30 +330,6 @@ }; -/* 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; @@ -432,6 +408,7 @@ void free_tmp_table(THD *thd, TABLE *entry); +class Prepared_statement; /* State of a single command executed against this connection. @@ -449,13 +426,15 @@ 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. + statement lifetime. FIXME: must be const */ ulong id; @@ -508,20 +487,29 @@ MEM_ROOT mem_root; protected: +public: /* This constructor is called when statement is a subobject of THD: some variables are initialized in THD::init due to locking problems */ Statement(); -public: + Statement(THD *thd); virtual ~Statement(); + + /* Assign execution context (note: not all members) of given stmt to self */ + void set_statement(Statement *stmt); + /* + Returns this in Prepared_statement and descendants (we build without + RTTI, so dynamic_cast can't be used. + */ + virtual Prepared_statement *as_prepared_statement(); }; /* Used to seek all existing statements in the connection - Not responsible for statements memory. + Deletes all statements in destructor. */ class Statement_map @@ -542,7 +530,12 @@ hash_delete(&st_hash, (byte *) statement); } - ~Statement_map() { hash_free(&st_hash); } + ~Statement_map() + { + for (uint i= 0; i < st_hash.records; ++i) + delete (Statement *) hash_element(&st_hash, i); + hash_free(&st_hash); + } private: HASH st_hash; }; @@ -572,13 +565,20 @@ 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 + /* 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 @@ -618,11 +618,9 @@ 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 */ ULL *ull; - PREP_STMT *last_prepared_stmt; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif @@ -692,8 +690,8 @@ /* 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. + class. With current implementation warnings produced in each prepared + statement/cursor settle here. */ List warn_list; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; --- 1.78/libmysqld/lib_sql.cc Wed Nov 19 14:52:59 2003 +++ 1.79/libmysqld/lib_sql.cc Tue Dec 16 16:15:14 2003 @@ -751,90 +751,3 @@ } #endif -bool setup_params_data(st_prep_stmt *stmt) -{ - THD *thd= stmt->thd; - List ¶ms= thd->lex->param_list; - List_iterator 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 ¶ms= thd->lex->param_list; - List_iterator 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); -} - --- 1.50/sql/sql_prepare.cc Thu Dec 4 18:08:40 2003 +++ 1.51/sql/sql_prepare.cc Tue Dec 16 16:15:14 2003 @@ -73,102 +73,91 @@ #include // for isspace() #include "sp_head.h" -#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 - -#ifdef EMBEDDED_LIBRARY -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos, ulong data_len) +/****************************************************************************** + Prepared_statement: statement which can contain placeholders +******************************************************************************/ + +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 Prepared_statement *as_prepared_statement(); +}; -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 inline Prepared_statement *seek_prepared_statement(THD *thd, ulong id) +{ + if (Statement *statement= thd->stmt_map.seek(id)) + return statement->as_prepared_statement(); + return 0; } + /* 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; @@ -176,23 +165,6 @@ } #endif /*!EMBEDDED_LIBRARY*/ -/* - Send information about all item parameters - - 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 @@ -419,17 +391,20 @@ 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) { + DBUG_ENTER("insert_params_withlog"); + THD *thd= stmt->thd; - List ¶ms= thd->lex->param_list; + List ¶ms= stmt->lex->param_list; List_iterator param_iterator(params); Item_param *param; - DBUG_ENTER("insert_params_withlog"); - String str, query, *res; + String str, query; + const String *res; - if (query.copy(*stmt->query)) + if (query.copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); ulong param_no= 0; @@ -439,10 +414,9 @@ { 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; @@ -462,23 +436,26 @@ } 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 ¶ms= thd->lex->param_list; + DBUG_ENTER("insert_params"); + + List ¶ms= stmt->lex->param_list; List_iterator param_iterator(params); Item_param *param; - DBUG_ENTER("insert_params"); - ulong param_no= 0; + 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 { @@ -491,15 +468,16 @@ DBUG_RETURN(0); } -static bool setup_params_data(PREP_STMT *stmt) +static bool setup_params_data(Prepared_statement *stmt) { - THD *thd= stmt->thd; - List ¶ms= thd->lex->param_list; + DBUG_ENTER("setup_params_data"); + + List ¶ms= stmt->lex->param_list; List_iterator 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 if (*read_pos++) //types supplied / first execute @@ -519,6 +497,95 @@ DBUG_RETURN(0); } +#else + +bool setup_params_data(Prepared_statement *stmt) +{ + DBUG_ENTER("setup_params_data"); + + List ¶ms= stmt->lex->param_list; + List_iterator param_iterator(params); + Item_param *param; + ulong param_no= 0; + MYSQL_BIND *client_param= stmt->thd->client_params; + + 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(Prepared_statement *stmt) +{ + DBUG_ENTER("setup_params_data_withlog"); + + THD *thd= stmt->thd; + List ¶ms= stmt->lex->param_list; + List_iterator param_iterator(params); + Item_param *param; + MYSQL_BIND *client_param= thd->client_params; + + String str, query; + const String *res; + + if (query.copy(stmt->query, stmt->query_length, default_charset_info)) + DBUG_RETURN(1); + + 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(thd, (char *) query.ptr(), query.length()+1)) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + #endif /*!EMBEDDED_LIBRARY*/ /* @@ -527,20 +594,21 @@ - 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 &fields, List &values_list) { + DBUG_ENTER("mysql_test_insert_fields"); + THD *thd= stmt->thd; TABLE *table; List_iterator_fast 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, @@ -575,7 +643,7 @@ } } } - if (send_prep_stmt(stmt, 0) || send_item_params(stmt)) + if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); DBUG_RETURN(0); } @@ -590,13 +658,15 @@ 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 &fields, List &values, COND *conds) { - THD *thd= stmt->thd; DBUG_ENTER("mysql_test_upd_fields"); + THD *thd= stmt->thd; + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_access(thd,UPDATE_ACL,table_list->db, &table_list->grant.privilege,0,0) || @@ -614,7 +684,7 @@ 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); } @@ -631,7 +701,9 @@ 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 &fields, COND *conds, uint og_num, ORDER *order, ORDER *group, @@ -640,11 +712,12 @@ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex) { - THD *thd= stmt->thd; - LEX *lex= thd->lex; - select_result *result= thd->lex->result; DBUG_ENTER("mysql_test_select_fields"); + THD *thd= stmt->thd; + LEX *lex= stmt->lex; + select_result *result= lex->result; + #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) @@ -664,12 +737,12 @@ 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); @@ -686,9 +759,9 @@ if (send_prep_stmt(stmt, fields.elements) || 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(); } @@ -700,19 +773,19 @@ 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->lex; - enum enum_sql_command sql_command= thd->lex->sql_command; DBUG_ENTER("send_prepare_results"); + + THD *thd= stmt->thd; + LEX *lex= stmt->lex; + enum enum_sql_command sql_command= lex->sql_command; + 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; @@ -770,66 +843,13 @@ } /* - 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); - else - { - if (thd->lex->sphead) - { - if (lex != thd->lex) - thd->lex->sphead->restore_lex(thd); - delete thd->lex->sphead; - thd->lex->sphead= NULL; - } - } - 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 ¶ms= thd->lex->param_list; Item_param **to; - uint32 length= thd->query_length; - stmt->lex= *thd->lex; - - if (mysql_bin_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 @@ -839,44 +859,12 @@ 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 param_iterator(params); + List_iterator 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 @@ -894,52 +882,71 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { - MEM_ROOT thd_root= thd->mem_root; - PREP_STMT stmt; - SELECT_LEX *sl; DBUG_ENTER("mysql_stmt_prepare"); - bzero((char*) &stmt, sizeof(stmt)); - - stmt.stmt_id= ++thd->statement_id_counter; - 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; + LEX *lex; + Prepared_statement *stmt= new Prepared_statement(thd); + + if (stmt == 0) + DBUG_RETURN(0); + + if (thd->stmt_map.insert(stmt)) + goto insert_stmt_err; + + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); - if (alloc_query(stmt.thd, packet, packet_length)) - goto err; + if (alloc_query(thd, packet, packet_length)) + goto alloc_query_err; - if (parse_prepare_query(&stmt, thd->query, thd->query_length)) - goto 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: + if (thd->lex->sphead) + { + if (lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } + lex_end(lex); + thd->set_statement(&thd->stmt_backup); +init_param_err: +alloc_query_err: + thd->stmt_map.erase(stmt); +insert_stmt_err: + delete stmt; DBUG_RETURN(1); } @@ -947,20 +954,21 @@ /* 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 */ void mysql_stmt_execute(THD *thd, char *packet) { - ulong stmt_id= uint4korr(packet); - PREP_STMT *stmt; - SELECT_LEX *sl; DBUG_ENTER("mysql_stmt_execute"); - if (!(stmt=find_prepared_statement(thd, stmt_id, "execute"))) + ulong stmt_id= uint4korr(packet); + Prepared_statement *stmt= seek_prepared_statement(thd, stmt_id); + + if (stmt == 0) { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "execute"); send_error(thd); DBUG_VOID_RETURN; } @@ -972,10 +980,28 @@ DBUG_VOID_RETURN; } - LEX *thd_lex= thd->lex; - thd->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()) { @@ -986,7 +1012,16 @@ 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)) @@ -1012,7 +1047,8 @@ if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - thd->lex= thd_lex; + free_root(&thd->mem_root, MYF(0)); + thd->set_statement(&thd->stmt_backup); DBUG_VOID_RETURN; } @@ -1033,12 +1069,14 @@ void mysql_stmt_reset(THD *thd, char *packet) { - ulong stmt_id= uint4korr(packet); - PREP_STMT *stmt; DBUG_ENTER("mysql_stmt_reset"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) + ulong stmt_id= uint4korr(packet); + Prepared_statement *stmt= seek_prepared_statement(thd, stmt_id); + + if (stmt == 0) { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "reset"); send_error(thd); DBUG_VOID_RETURN; } @@ -1063,16 +1101,18 @@ void mysql_stmt_free(THD *thd, char *packet) { - ulong stmt_id= uint4korr(packet); DBUG_ENTER("mysql_stmt_free"); + ulong stmt_id= uint4korr(packet); + Prepared_statement *stmt= seek_prepared_statement(thd, stmt_id); - if (!find_prepared_statement(thd, stmt_id, "close")) + if (stmt == 0) { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "close"); send_error(thd); // Not seen by the client DBUG_VOID_RETURN; } - tree_delete(&thd->prepared_statements, (void*) &stmt_id, (void *)0); - thd->last_prepared_stmt= (PREP_STMT *)0; + thd->stmt_map.erase(stmt); + delete stmt; DBUG_VOID_RETURN; } @@ -1098,8 +1138,8 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) { - PREP_STMT *stmt; DBUG_ENTER("mysql_stmt_get_longdata"); + Prepared_statement *stmt; #ifndef EMBEDDED_LIBRARY /* The following should never happen */ @@ -1113,8 +1153,9 @@ ulong stmt_id= uint4korr(pos); uint param_number= uint2korr(pos+4); - if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) + if (!(stmt=seek_prepared_statement(thd, stmt_id))) { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 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... @@ -1144,4 +1185,47 @@ stmt->long_data_used= 1; 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); +} + + +Prepared_statement *Prepared_statement::as_prepared_statement() +{ + return this; +} +