diff options
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r-- | sql/sql_prepare.cc | 913 |
1 files changed, 529 insertions, 384 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 43bf3e3651c..af1b85ae8a7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -39,7 +39,7 @@ Prepare-execute: - Server gets the command 'COM_EXECUTE' to execute the previously prepared query. If there is any param markers; then client - will send the data in the following format: + will send the data in the following format: [COM_EXECUTE:1] [STMT_ID:4] [NULL_BITS:(param_count+7)/8)] @@ -87,16 +87,18 @@ class Prepared_statement: public Statement { public: THD *thd; - Item_param **param; /* array of all placeholders */ + Item_param **param_array; uint param_count; uint last_errno; char last_error[MYSQL_ERRMSG_SIZE]; - bool error_in_prepare, long_data_used; + bool get_longdata_error; + bool long_data_used; bool log_full_query; #ifndef EMBEDDED_LIBRARY - bool (*setup_params)(Prepared_statement *st, uchar *pos, uchar *read_pos); + bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, + uchar *read_pos); #else - bool (*setup_params_data)(Prepared_statement *st); + bool (*set_params_data)(Prepared_statement *st); #endif public: Prepared_statement(THD *thd_arg); @@ -117,14 +119,7 @@ inline bool is_param_null(const uchar *pos, ulong param_no) enum { 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) -#else -#define SETUP_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos) -#endif - +enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR }; /* Seek prepared statement in statement map by id: returns zero if statement @@ -132,14 +127,16 @@ static void fn_name(Item_param *param, uchar **pos) */ static Prepared_statement * -find_prepared_statement(THD *thd, ulong id, const char *where) +find_prepared_statement(THD *thd, ulong id, const char *where, + enum enum_send_error se) { 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); + if (se == SEND_ERROR) + send_error(thd); return 0; } return (Prepared_statement *) stmt; @@ -155,12 +152,20 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) { NET *net= &stmt->thd->net; char buff[9]; - buff[0]= 0; + buff[0]= 0; /* OK packet indicator */ int4store(buff+1, stmt->id); int2store(buff+5, columns); int2store(buff+7, stmt->param_count); - /* This should be fixed to work with prepared statements */ - return (my_net_write(net, buff, sizeof(buff)) || net_flush(net)); + /* + Send types and names of placeholders to the client + XXX: fix this nasty upcast from List<Item_param> to List<Item> + */ + return my_net_write(net, buff, sizeof(buff)) || + (stmt->param_count && + stmt->thd->protocol_simple.send_fields((List<Item> *) + &stmt->lex->param_list, 0)) || + net_flush(net); + return 0; } #else static bool send_prep_stmt(Prepared_statement *stmt, @@ -178,104 +183,138 @@ static bool send_prep_stmt(Prepared_statement *stmt, /* - Read the length of the parameter data and retun back to - caller by positing the pointer to param data + Read the length of the parameter data and return back to + caller by positing the pointer to param data. */ #ifndef EMBEDDED_LIBRARY -static ulong get_param_length(uchar **packet) +static ulong get_param_length(uchar **packet, ulong len) { reg1 uchar *pos= *packet; + if (len < 1) + return 0; if (*pos < 251) { (*packet)++; return (ulong) *pos; } + if (len < 3) + return 0; if (*pos == 252) { (*packet)+=3; return (ulong) uint2korr(pos+1); } + if (len < 4) + return 0; if (*pos == 253) { (*packet)+=4; return (ulong) uint3korr(pos+1); } + if (len < 5) + return 0; (*packet)+=9; // Must be 254 when here + /* TODO: why uint4korr here? (should be uint8korr) */ return (ulong) uint4korr(pos+1); } #else -#define get_param_length(A) data_len +#define get_param_length(packet, len) len #endif /*!EMBEDDED_LIBRARY*/ /* - Setup param conversion routines - - setup_param_xx() - param Parameter Item - pos Input data buffer + Data conversion routines + SYNOPSIS + set_param_xx() + param parameter item + pos input data buffer + len length of data in the buffer - All these functions reads the data from pos and sets up that data - through 'param' and advances the buffer position to predifined - length position. + All these functions read the data from pos, convert it to requested type + and assign to param; pos is advanced to predefined length. Make a note that the NULL handling is examined at first execution (i.e. when input types altered) and for all subsequent executions we don't read any values for this. - RETURN VALUES - + RETURN VALUE + none */ -SETUP_PARAM_FUNCTION(setup_param_tiny) +void set_param_tiny(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 1) + return; +#endif param->set_int((longlong)(**pos)); *pos+= 1; } -SETUP_PARAM_FUNCTION(setup_param_short) +void set_param_short(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 2) + return; +#endif param->set_int((longlong)sint2korr(*pos)); *pos+= 2; } -SETUP_PARAM_FUNCTION(setup_param_int32) +void set_param_int32(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif param->set_int((longlong)sint4korr(*pos)); *pos+= 4; } -SETUP_PARAM_FUNCTION(setup_param_int64) +void set_param_int64(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif param->set_int((longlong)sint8korr(*pos)); *pos+= 8; } -SETUP_PARAM_FUNCTION(setup_param_float) +void set_param_float(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif float data; float4get(data,*pos); param->set_double((double) data); *pos+= 4; } -SETUP_PARAM_FUNCTION(setup_param_double) +void set_param_double(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif double data; float8get(data,*pos); param->set_double((double) data); *pos+= 8; } -SETUP_PARAM_FUNCTION(setup_param_time) +void set_param_time(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 8) { uchar *to= *pos; - TIME tm; + TIME tm; + /* TODO: why length is compared with 8 here? */ tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; tm.day= (ulong) sint4korr(to+1); @@ -291,11 +330,11 @@ SETUP_PARAM_FUNCTION(setup_param_time) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_datetime) +void set_param_datetime(Item_param *param, uchar **pos, ulong len) { - uint length= get_param_length(pos); + uint length; - if (length) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -321,11 +360,11 @@ SETUP_PARAM_FUNCTION(setup_param_datetime) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_date) +void set_param_date(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -343,55 +382,55 @@ SETUP_PARAM_FUNCTION(setup_param_date) *pos+= length; } -SETUP_PARAM_FUNCTION(setup_param_str) +void set_param_str(Item_param *param, uchar **pos, ulong len) { - ulong len= get_param_length(pos); - param->set_value((const char *)*pos, len); - *pos+= len; + ulong length= get_param_length(pos, len); + param->set_value((const char *)*pos, length); + *pos+= length; } -void setup_param_functions(Item_param *param, uchar param_type) +static void setup_one_conversion_function(Item_param *param, uchar param_type) { switch (param_type) { case FIELD_TYPE_TINY: - param->setup_param_func= setup_param_tiny; + param->set_param_func= set_param_tiny; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_SHORT: - param->setup_param_func= setup_param_short; + param->set_param_func= set_param_short; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_LONG: - param->setup_param_func= setup_param_int32; + param->set_param_func= set_param_int32; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_LONGLONG: - param->setup_param_func= setup_param_int64; + param->set_param_func= set_param_int64; param->item_result_type= INT_RESULT; break; case FIELD_TYPE_FLOAT: - param->setup_param_func= setup_param_float; + param->set_param_func= set_param_float; param->item_result_type= REAL_RESULT; break; case FIELD_TYPE_DOUBLE: - param->setup_param_func= setup_param_double; + param->set_param_func= set_param_double; param->item_result_type= REAL_RESULT; break; case FIELD_TYPE_TIME: - param->setup_param_func= setup_param_time; + param->set_param_func= set_param_time; param->item_result_type= STRING_RESULT; break; case FIELD_TYPE_DATE: - param->setup_param_func= setup_param_date; + param->set_param_func= set_param_date; param->item_result_type= STRING_RESULT; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: - param->setup_param_func= setup_param_datetime; + param->set_param_func= set_param_datetime; param->item_result_type= STRING_RESULT; break; default: - param->setup_param_func= setup_param_str; + param->set_param_func= set_param_str; param->item_result_type= STRING_RESULT; } } @@ -402,14 +441,14 @@ 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(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { - THD *thd= stmt->thd; - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - + THD *thd= stmt->thd; + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + uint32 length= 0; + String str, query; const String *res; @@ -418,16 +457,14 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, if (query.copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); - ulong param_no= 0; - uint32 length= 0; - - while ((param= (Item_param *)param_iterator++)) + for (Item_param **it= begin; it < end; ++it) { + Item_param *param= *it; if (param->long_data_supplied) - res= param->query_val_str(&str); + res= param->query_val_str(&str); else { - if (is_param_null(pos,param_no)) + if (is_param_null(null_array, it - begin)) { param->maybe_null= param->null_value= 1; res= &my_null_string; @@ -435,7 +472,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, else { param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); res= param->query_val_str(&str); } } @@ -443,7 +482,6 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, DBUG_RETURN(1); length+= res->length()-1; - param_no++; } if (alloc_query(thd, (char *)query.ptr(), query.length()+1)) DBUG_RETURN(1); @@ -452,76 +490,76 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, } -static bool insert_params(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - ulong param_no= 0; + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; DBUG_ENTER("insert_params"); - while ((param= (Item_param *)param_iterator++)) + for (Item_param **it= begin; it < end; ++it) { - if (!param->long_data_supplied) + Item_param *param= *it; + if (!param->long_data_supplied) { - if (is_param_null(pos,param_no)) + if (is_param_null(null_array, it - begin)) param->maybe_null= param->null_value= 1; else { param->maybe_null= param->null_value= 0; - param->setup_param_func(param,&read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); } } - param_no++; } DBUG_RETURN(0); } -static bool setup_params_data(Prepared_statement *stmt) -{ - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; - 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 +static bool setup_conversion_functions(Prepared_statement *stmt, + uchar **data, uchar *data_end) +{ + /* skip null bits */ + uchar *read_pos= *data + (stmt->param_count+7) / 8; - DBUG_ENTER("setup_params_data"); + DBUG_ENTER("setup_conversion_functions"); if (*read_pos++) //types supplied / first execute - { + { /* First execute or types altered by the client, setup the conversion routines for all parameters (one time) */ - while ((param= (Item_param *)param_iterator++)) - { - setup_param_functions(param,*read_pos); + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; + for (; it < end; ++it) + { + if (read_pos >= data_end) + DBUG_RETURN(1); + setup_one_conversion_function(*it, *read_pos); read_pos+= 2; } - param_iterator.rewind(); - } - stmt->setup_params(stmt,pos,read_pos); + } + *data= read_pos; 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; +static bool emb_insert_params(Prepared_statement *stmt) +{ + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= stmt->thd->client_params; - DBUG_ENTER("setup_params_data"); + DBUG_ENTER("emb_insert_params"); - for (;(param= (Item_param *)param_iterator++); client_param++) - { - setup_param_functions(param, client_param->buffer_type); + for (; it < end; ++it, ++client_param) + { + Item_param *param= *it; + setup_one_conversion_function(param, client_param->buffer_type); if (!param->long_data_supplied) { if (*client_param->is_null) @@ -530,39 +568,39 @@ bool setup_params_data(Prepared_statement *stmt) { 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->set_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) -{ + +static bool emb_insert_params_withlog(Prepared_statement *stmt) +{ THD *thd= stmt->thd; - List<Item> ¶ms= stmt->lex->param_list; - List_iterator<Item> param_iterator(params); - Item_param *param; + Item_param **it= stmt->param_array; + Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= thd->client_params; String str, query; const String *res; + uint32 length= 0; - DBUG_ENTER("setup_params_data_withlog"); + DBUG_ENTER("emb_insert_params_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); + for (; it < end; ++it, ++client_param) + { + Item_param *param= *it; + setup_one_conversion_function(param, client_param->buffer_type); if (param->long_data_supplied) - res= param->query_val_str(&str); + res= param->query_val_str(&str); else { if (*client_param->is_null) @@ -574,16 +612,15 @@ bool setup_params_data_withlog(Prepared_statement *stmt) { 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->set_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; } @@ -596,15 +633,21 @@ bool setup_params_data_withlog(Prepared_statement *stmt) #endif /*!EMBEDDED_LIBRARY*/ /* - Validate the following information for INSERT statement: - - field existance - - fields count + Validate the following information for INSERT statement: + - field existence + - fields count + SYNOPSIS + mysql_test_insert_fields() + RETURN VALUE + 0 ok + 1 error, sent to the client + -1 error, not sent to client */ -static bool mysql_test_insert_fields(Prepared_statement *stmt, - TABLE_LIST *table_list, - List<Item> &fields, - List<List_item> &values_list) +static int mysql_test_insert_fields(Prepared_statement *stmt, + TABLE_LIST *table_list, + List<Item> &fields, + List<List_item> &values_list) { THD *thd= stmt->thd; TABLE *table; @@ -622,8 +665,18 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,privilege,table_list,0,0))) DBUG_RETURN(1); #endif + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); + { + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); + } + table= table_list->table; if ((values= its++)) @@ -632,7 +685,11 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ulong counter= 0; if (check_insert_fields(thd,table,fields,*values,1)) - DBUG_RETURN(1); + { + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); + } + thd->free_temporary_memory_pool_for_ps_preparing(); value_count= values->elements; its.rewind(); @@ -645,29 +702,34 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, ER(ER_WRONG_VALUE_COUNT_ON_ROW), MYF(0), counter); - DBUG_RETURN(1); + DBUG_RETURN(-1); } } } - if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + else + { + thd->free_temporary_memory_pool_for_ps_preparing(); + } DBUG_RETURN(0); } /* - Validate the following information - UPDATE - set and where clause DELETE - where clause - - And send update-set clause column list fields info - back to client. For DELETE, just validate where clause - and return no fields information back to client. + Validate the following information: + UPDATE - set and where clause + DELETE - where clause + SYNOPSIS + mysql_test_upd_fields() + RETURN VALUE + 0 success + 1 error, sent to client + -1 error, not sent to client */ -static bool mysql_test_upd_fields(Prepared_statement *stmt, - TABLE_LIST *table_list, - List<Item> &fields, List<Item> &values, - COND *conds) +static int mysql_test_upd_fields(Prepared_statement *stmt, + TABLE_LIST *table_list, + List<Item> &fields, List<Item> &values, + COND *conds) { THD *thd= stmt->thd; @@ -678,48 +740,59 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,UPDATE_ACL,table_list,0,0))) DBUG_RETURN(1); #endif - if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); - if (setup_tables(table_list, 0) || - setup_fields(thd, 0, table_list, fields, 1, 0, 0) || - setup_conds(thd, table_list, &conds) || thd->net.report_error) - DBUG_RETURN(1); /* - Currently return only column list info only, and we are not - sending any info on where clause. + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure */ - if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + thd->allocate_temporary_memory_pool_for_ps_preparing(); + + if (open_and_lock_tables(thd, table_list)) + goto err; + if (setup_tables(table_list) || + setup_fields(thd, 0, table_list, fields, 1, 0, 0) || + setup_conds(thd, table_list, &conds) || thd->net.report_error) + goto err; + + thd->free_temporary_memory_pool_for_ps_preparing(); + + /* TODO: here we should send types of placeholders to the client. */ DBUG_RETURN(0); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(-1); } /* - Validate the following information: - + Validate the following information: SELECT - column list - where clause - order clause - having clause - group by clause - if no column spec i.e. '*', then setup all fields - - And send column list fields info back to client. + In case of success, if this query is not EXPLAIN, send column list info + back to client. + SYNOPSIS + mysql_test_select_fields() + RETURN VALUE + 0 success + 1 error, sent to client + -1 error, not sent to client */ -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, - Item *having, ORDER *proc, - ulong select_options, - SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex) +static int 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, + Item *having, ORDER *proc, + ulong select_options, + SELECT_LEX_UNIT *unit, + SELECT_LEX *select_lex) { THD *thd= stmt->thd; LEX *lex= stmt->lex; - select_result *result= lex->result; DBUG_ENTER("mysql_test_select_fields"); @@ -730,142 +803,166 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, if (check_table_access(thd, privilege, tables,0)) DBUG_RETURN(1); } - else if (check_access(thd, privilege, "*any*",0,0,0)) + else if (check_access(thd, privilege, any_db,0,0,0)) DBUG_RETURN(1); #endif if ((&lex->select_lex != lex->all_selects_list && lex->unit.create_total_list(thd, lex, &tables))) DBUG_RETURN(1); - + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, tables)) - DBUG_RETURN(1); + { + send_error(thd); + goto err; + } if (lex->describe) { if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + goto err; } else { + select_result *result= lex->result; if (!result && !(result= new select_send())) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_RETURN(1); + goto err; } - JOIN *join= new JOIN(thd, fields, select_options, result); - thd->used_tables= 0; // Updated by setup_fields + thd->used_tables= 0; // Updated by setup_fields + + if (unit->prepare(thd, result, 0)) + { + send_error(thd); + goto err_prep; + } - if (join->prepare(&select_lex->ref_pointer_array, - (TABLE_LIST*)select_lex->get_table_list(), - wild_num, conds, og_num, order, group, having, proc, - select_lex, unit)) - DBUG_RETURN(1); 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 ) - DBUG_RETURN(1); - join->cleanup(); + goto err_prep; + + unit->cleanup(); } - DBUG_RETURN(0); + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(0); + +err_prep: + unit->cleanup(); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(1); } /* - Send the prepare query results back to client + Send the prepare query results back to client + SYNOPSIS + send_prepare_results() + stmt prepared statement + RETURN VALUE + 0 success + 1 error, sent to client */ -static bool send_prepare_results(Prepared_statement *stmt) +static int send_prepare_results(Prepared_statement *stmt) { THD *thd= stmt->thd; LEX *lex= stmt->lex; + SELECT_LEX *select_lex= &lex->select_lex; + TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; enum enum_sql_command sql_command= lex->sql_command; + int res; 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; - - SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; + sql_command, stmt->param_count)); switch (sql_command) { case SQLCOM_INSERT: - if (mysql_test_insert_fields(stmt, tables, lex->field_list, - lex->many_values)) - goto abort; + if ((res= mysql_test_insert_fields(stmt, tables, lex->field_list, + lex->many_values))) + goto error; break; case SQLCOM_UPDATE: - if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, - lex->value_list, select_lex->where)) - goto abort; - break; - + /* XXX: fallthrough */ case SQLCOM_DELETE: - if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, - lex->value_list, select_lex->where)) - goto abort; + if ((res= mysql_test_upd_fields(stmt, tables, select_lex->item_list, + lex->value_list, select_lex->where))) + goto error; break; case SQLCOM_SELECT: - if (mysql_test_select_fields(stmt, tables, select_lex->with_wild, - select_lex->item_list, - select_lex->where, - select_lex->order_list.elements + - select_lex->group_list.elements, - (ORDER*) select_lex->order_list.first, - (ORDER*) select_lex->group_list.first, - select_lex->having, - (ORDER*)lex->proc_list.first, - select_lex->options | thd->options, - &(lex->unit), select_lex)) - goto abort; - break; + if ((res= mysql_test_select_fields(stmt, tables, select_lex->with_wild, + select_lex->item_list, + select_lex->where, + select_lex->order_list.elements + + select_lex->group_list.elements, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*)lex->proc_list.first, + select_lex->options | thd->options, + &(lex->unit), select_lex))) + goto error; + /* Statement and field info has already been sent */ + DBUG_RETURN(0); default: - { - /* - Rest fall through to default category, no parsing - for non-DML statements - */ - if (send_prep_stmt(stmt, 0)) - goto abort; - } + /* + Rest fall through to default category, no parsing + for non-DML statements + */ + break; } - DBUG_RETURN(0); + DBUG_RETURN(send_prep_stmt(stmt, 0)); -abort: - send_error(thd,thd->killed_errno()); +error: + if (res < 0) + send_error(thd,thd->killed_errno()); DBUG_RETURN(1); } /* - Initialize parameter items in statement + Initialize array of parametes in statement from LEX. + (We need to have quick access to items by number in mysql_send_longdata). + This is to avoid using malloc/realloc in the parser. */ -static bool init_param_items(Prepared_statement *stmt) +static bool init_param_array(Prepared_statement *stmt) { - Item_param **to; - - if (!stmt->param_count) - stmt->param= (Item_param **)0; - else - { - if (!(stmt->param= to= (Item_param **) - my_malloc(sizeof(Item_param *)*(stmt->param_count+1), - MYF(MY_WME)))) + LEX *lex= stmt->lex; + if ((stmt->param_count= lex->param_list.elements)) + { + Item_param **to; + List_iterator<Item_param> param_iterator(lex->param_list); + /* Use thd->mem_root as it points at statement mem_root */ + stmt->param_array= (Item_param **) + alloc_root(&stmt->thd->mem_root, + sizeof(Item_param*) * stmt->param_count); + if (!stmt->param_array) + { + send_error(stmt->thd, ER_OUT_OF_RESOURCES); return 1; - - List_iterator<Item> param_iterator(stmt->lex->param_list); - while ((*(to++)= (Item_param *)param_iterator++)); - } + } + for (to= stmt->param_array; + to < stmt->param_array + stmt->param_count; + ++to) + { + *to= param_iterator++; + } + } return 0; } @@ -873,72 +970,71 @@ static bool init_param_items(Prepared_statement *stmt) /* Parse the query and send the total number of parameters and resultset metadata information back to client (if any), - without executing the query i.e. with out any log/disk + without executing the query i.e. without any log/disk writes. This will allow the queries to be re-executed - without re-parsing during execute. - - 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 - retrieval can be made without going through all field - items. + without re-parsing during execute. + + If parameter markers are found in the query, then store + the information using Item_param along with maintaining a + list in lex->param_array, so that a fast and direct + retrieval can be made without going through all field + items. */ -bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) +void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { LEX *lex; Prepared_statement *stmt= new Prepared_statement(thd); - SELECT_LEX *sl; + int error; DBUG_ENTER("mysql_stmt_prepare"); + DBUG_PRINT("prep_query", ("%s", packet)); + if (stmt == 0) - DBUG_RETURN(0); + { + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } if (thd->stmt_map.insert(stmt)) - goto insert_stmt_err; + { + delete stmt; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } thd->stmt_backup.set_statement(thd); + thd->stmt_backup.set_item_arena(thd); thd->set_statement(stmt); + thd->set_item_arena(stmt); if (alloc_query(thd, packet, packet_length)) - goto alloc_query_err; + { + stmt->set_statement(thd); + stmt->set_item_arena(thd); + thd->set_statement(&thd->stmt_backup); + thd->set_item_arena(&thd->stmt_backup); + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } - mysql_log.write(thd, COM_PREPARE, "%s", packet); + mysql_log.write(thd, COM_PREPARE, "%s", packet); + thd->current_statement= stmt; 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); + error= yyparse((void *)thd) || thd->is_fatal_error || + init_param_array(stmt) || + send_prepare_results(stmt); + /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ 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; - sl; - sl= sl->next_select_in_list()) - { - sl->prep_where= sl->where; - } - - cleanup_items(thd->free_list); - 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 - - DBUG_RETURN(0); - -yyparse_err: - if (thd->lex->sphead) + if (error && thd->lex->sphead) { if (lex != thd->lex) thd->lex->sphead->restore_lex(thd); @@ -947,171 +1043,221 @@ yyparse_err: } lex_end(lex); stmt->set_statement(thd); + stmt->set_item_arena(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); + thd->set_item_arena(&thd->stmt_backup); + thd->current_statement= 0; + + if (error) + { + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + /* error is sent inside yyparse/send_prepare_results */ + } + else + { + SELECT_LEX *sl= stmt->lex->all_selects_list; + /* + Save WHERE clause pointers, because they may be changed during query + optimisation. + */ + for (; sl; sl= sl->next_select_in_list()) + { + sl->prep_where= sl->where; + } + } + DBUG_VOID_RETURN; } +/* Reinit statement before execution */ -/* - Executes previously prepared query +static void reset_stmt_for_execute(Prepared_statement *stmt) +{ + THD *thd= stmt->thd; + SELECT_LEX *sl= stmt->lex->all_selects_list; - If there is any parameters (stmt->param_count), then replace - markers with the data supplied from client, and then - execute the query + for (; sl; sl= sl->next_select_in_list()) + { + /* + Copy WHERE clause pointers to avoid damaging they by optimisation + */ + if (sl->prep_where) + sl->where= sl->prep_where->copy_andor_structure(thd); + DBUG_ASSERT(sl->join == 0); + ORDER *order; + /* Fix GROUP list */ + for (order= (ORDER *)sl->group_list.first; order; order= order->next) + order->item= &order->item_ptr; + /* Fix ORDER list */ + for (order= (ORDER *)sl->order_list.first; order; order= order->next) + order->item= &order->item_ptr; + + /* + 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*) sl->table_list.first; + tables; + tables= tables->next) + { + /* + Reset old pointers to TABLEs: they are not valid since the tables + were closed in the end of previous prepare or execute call. + */ + tables->table= 0; + tables->table_list= 0; + } + + { + SELECT_LEX_UNIT *unit= sl->master_unit(); + unit->unclean(); + unit->types.empty(); + /* for derived tables & PS (which can't be reset by Item_subquery) */ + unit->reinit_exec_mechanism(); + } + } +} + +/* + Executes previously prepared query. + If there is any parameters, then replace markers with the data supplied + from client, and then execute the query. + SYNOPSYS + mysql_stmt_execute() */ -void mysql_stmt_execute(THD *thd, char *packet) + +void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); +#ifndef EMBEDDED_LIBRARY + uchar *packet_end= (uchar *) packet + packet_length - 1; +#endif Prepared_statement *stmt; - DBUG_ENTER("mysql_stmt_execute"); + + packet+= 9; /* stmt_id + 5 bytes of flags */ - if (!(stmt= find_prepared_statement(thd, stmt_id, "execute"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "execute", SEND_ERROR))) DBUG_VOID_RETURN; + DBUG_PRINT("exec_query:", ("%s", stmt->query)); + /* Check if we got an error when sending long data */ - if (stmt->error_in_prepare) + if (stmt->get_longdata_error) { send_error(thd, stmt->last_errno, stmt->last_error); DBUG_VOID_RETURN; } - stmt->query_id= thd->query_id; thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); - thd->free_list= 0; - - /* - 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); + reset_stmt_for_execute(stmt); - for (SELECT_LEX *sl= stmt->lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) +#ifndef EMBEDDED_LIBRARY + if (stmt->param_count) { - /* - Copy WHERE clause pointers to avoid damaging they by optimisation - */ - if (sl->prep_where) - sl->where= sl->prep_where->copy_andor_structure(thd); - DBUG_ASSERT(sl->join == 0); - ORDER *order; - /* Fix GROUP list */ - for (order=(ORDER *)sl->group_list.first ; order ; order=order->next) - order->item= (Item **)(order+1); - /* Fix ORDER list */ - for (order=(ORDER *)sl->order_list.first ; order ; order=order->next) - order->item= (Item **)(order+1); + uchar *null_array= (uchar *) packet; + if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) || + stmt->set_params(stmt, null_array, (uchar *) packet, packet_end)) + goto set_params_data_err; } - +#else /* - 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. + In embedded library we re-install conversion routines each time + we set params, and also we don't need to parse packet. + So we do it in one function. */ - 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)) - DBUG_VOID_RETURN; -#else - if (stmt->param_count && (*stmt->setup_params_data)(stmt)) - DBUG_VOID_RETURN; + if (stmt->param_count && stmt->set_params_data(stmt)) + goto set_params_data_err; #endif if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); + my_pthread_setprio(pthread_self(),QUERY_PRIOR); /* TODO: Also, have checks on basic executions such as mysql_insert(), mysql_delete(), mysql_update() and mysql_select() to not to have re-check on setup_* and other things .. - */ - thd->protocol= &thd->protocol_prep; // Switch to binary protocol + */ + thd->protocol= &thd->protocol_prep; // Switch to binary protocol mysql_execute_command(thd); - thd->protocol= &thd->protocol_simple; // Use normal protocol + thd->protocol= &thd->protocol_simple; // Use normal protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - free_items(thd->free_list); cleanup_items(stmt->free_list); - free_root(&thd->mem_root, MYF(0)); + close_thread_tables(thd); // to close derived tables + thd->set_statement(&thd->stmt_backup); + DBUG_VOID_RETURN; + +set_params_data_err: thd->set_statement(&thd->stmt_backup); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + send_error(thd); DBUG_VOID_RETURN; } /* - Reset a prepared statement - + Reset a prepared statement, in case there was an error in send_longdata. + Note: we don't send any reply to that command. SYNOPSIS mysql_stmt_reset() thd Thread handle - packet Packet with stmt handle + packet Packet with stmt id DESCRIPTION This function is useful when one gets an error after calling - mysql_stmt_getlongdata() and one wants to reset the handle + mysql_stmt_getlongdata() and wants to reset the handle so that one can call execute again. + See also bug #1664 */ void mysql_stmt_reset(THD *thd, char *packet) { + /* There is always space for 4 bytes in buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_reset"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", DONT_SEND_ERROR))) DBUG_VOID_RETURN; - stmt->error_in_prepare= 0; - Item_param *item= *stmt->param, *end= item + stmt->param_count; + stmt->get_longdata_error= 0; /* Free long data if used */ if (stmt->long_data_used) { + Item_param **item= stmt->param_array; + Item_param **end= item + stmt->param_count; stmt->long_data_used= 0; for (; item < end ; item++) - item->reset(); + (**item).reset(); } DBUG_VOID_RETURN; } /* - Delete a prepared statement from memory + Delete a prepared statement from memory. + Note: we don't send any reply to that command. */ void mysql_stmt_free(THD *thd, char *packet) { + /* There is always space for 4 bytes in packet buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; DBUG_ENTER("mysql_stmt_free"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "close"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "close", DONT_SEND_ERROR))) DBUG_VOID_RETURN; /* Statement map deletes statement on erase */ @@ -1121,7 +1267,7 @@ void mysql_stmt_free(THD *thd, char *packet) /* - Long data in pieces from client + Long data in pieces from client SYNOPSIS mysql_stmt_get_longdata() @@ -1157,14 +1303,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) ulong stmt_id= uint4korr(pos); uint param_number= uint2korr(pos+4); - if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) + if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata", + DONT_SEND_ERROR))) DBUG_VOID_RETURN; #ifndef EMBEDDED_LIBRARY if (param_number >= stmt->param_count) { /* Error will be sent in execute call */ - stmt->error_in_prepare= 1; + stmt->get_longdata_error= 1; stmt->last_errno= ER_WRONG_ARGUMENTS; sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata"); DBUG_VOID_RETURN; @@ -1172,7 +1319,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) pos+= MYSQL_LONG_DATA_HEADER; // Point to data #endif - Item_param *param= *(stmt->param+param_number); + Item_param *param= stmt->param_array[param_number]; #ifndef EMBEDDED_LIBRARY param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1); #else @@ -1186,10 +1333,10 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) Prepared_statement::Prepared_statement(THD *thd_arg) :Statement(thd_arg), thd(thd_arg), - param(0), + param_array(0), param_count(0), last_errno(0), - error_in_prepare(0), + get_longdata_error(0), long_data_used(0), log_full_query(0) { @@ -1198,23 +1345,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg) { log_full_query= 1; #ifndef EMBEDDED_LIBRARY - setup_params= insert_params_withlog; + set_params= insert_params_withlog; #else - setup_params_data= setup_params_data_withlog; + set_params_data= emb_insert_params_withlog; #endif } else #ifndef EMBEDDED_LIBRARY - setup_params= insert_params; // not fully qualified query + set_params= insert_params; #else - setup_params_data= ::setup_params_data; + set_params_data= emb_insert_params; #endif } Prepared_statement::~Prepared_statement() { - my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR)); free_items(free_list); } @@ -1224,4 +1370,3 @@ Statement::Type Prepared_statement::type() const return PREPARED_STATEMENT; } - |