diff options
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r-- | sql/sql_prepare.cc | 508 |
1 files changed, 357 insertions, 151 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a8a2f5e8515..d4f69e5dd4f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -92,16 +92,22 @@ public: bool get_longdata_error; #ifndef EMBEDDED_LIBRARY bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, - uchar *read_pos); + uchar *read_pos, String *expanded_query); #else - bool (*set_params_data)(Prepared_statement *st); + bool (*set_params_data)(Prepared_statement *st, String *expanded_query); #endif + bool (*set_params_from_vars)(Prepared_statement *stmt, + List<LEX_STRING>& varnames, + String *expanded_query); public: Prepared_statement(THD *thd_arg); virtual ~Prepared_statement(); + void setup_set_params(); virtual Statement::Type type() const; }; +static void execute_stmt(THD *thd, Prepared_statement *stmt, + String *expanded_query, bool set_context); /****************************************************************************** Implementation @@ -130,7 +136,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where, if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT) { - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where); + char llbuf[22]; + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where); if (se == SEND_ERROR) send_error(thd); return 0; @@ -211,7 +218,13 @@ static ulong get_param_length(uchar **packet, ulong len) if (len < 5) return 0; (*packet)+=9; // Must be 254 when here - /* TODO: why uint4korr here? (should be uint8korr) */ + /* + In our client-server protocol all numbers bigger than 2^24 + stored as 8 bytes with uint8korr. Here we always know that + parameter length is less than 2^4 so don't look at the second + 4 bytes. But still we need to obey the protocol hence 9 in the + assignment above. + */ return (ulong) uint4korr(pos+1); } #else @@ -327,20 +340,19 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) { uchar *to= *pos; TIME tm; - - /* TODO: why length is compared with 8 here? */ - tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; + tm.neg= (bool) to[0]; + day= (uint) sint4korr(to+1); /* Note, that though ranges of hour, minute and second are not checked here we rely on them being < 256: otherwise we'll get buffer overflow in make_{date,time} functions, which are called when time value is converted to string. */ - day= (uint) sint4korr(to+1); tm.hour= (uint) to[5] + day * 24; tm.minute= (uint) to[6]; tm.second= (uint) to[7]; + tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0; if (tm.hour > 838) { /* TODO: add warning 'Data truncated' here */ @@ -349,9 +361,8 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) tm.second= 59; } tm.day= tm.year= tm.month= 0; - tm.neg= (bool)to[0]; - param->set_time(&tm, TIMESTAMP_TIME, + param->set_time(&tm, MYSQL_TIMESTAMP_TIME, MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; @@ -360,14 +371,16 @@ static void set_param_time(Item_param *param, uchar **pos, ulong len) static void set_param_datetime(Item_param *param, uchar **pos, ulong len) { uint length; - + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; - - tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0; - + + tm.neg= 0; + tm.year= (uint) sint2korr(to); + tm.month= (uint) to[2]; + tm.day= (uint) to[3]; /* Note, that though ranges of hour, minute and second are not checked here we rely on them being < 256: otherwise @@ -381,13 +394,10 @@ static void set_param_datetime(Item_param *param, uchar **pos, ulong len) } else tm.hour= tm.minute= tm.second= 0; - - tm.year= (uint) sint2korr(to); - tm.month= (uint) to[2]; - tm.day= (uint) to[3]; - tm.neg= 0; - param->set_time(&tm, TIMESTAMP_DATETIME, + tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0; + + param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME, MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; @@ -414,7 +424,7 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len) tm.second_part= 0; tm.neg= 0; - param->set_time(&tm, TIMESTAMP_DATE, + param->set_time(&tm, MYSQL_TIMESTAMP_DATE, MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; @@ -423,58 +433,25 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len) #else/*!EMBEDDED_LIBRARY*/ void set_param_time(Item_param *param, uchar **pos, ulong len) { - TIME tm; MYSQL_TIME *to= (MYSQL_TIME*)*pos; - - tm.second_part= to->second_part; - - tm.day= to->day; - tm.hour= to->hour; - tm.minute= to->minute; - tm.second= to->second; - - tm.year= tm.month= 0; - tm.neg= to->neg; - param->set_time(&tm, TIMESTAMP_TIME, + param->set_time(to, MYSQL_TIMESTAMP_TIME, MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } void set_param_datetime(Item_param *param, uchar **pos, ulong len) { - TIME tm; MYSQL_TIME *to= (MYSQL_TIME*)*pos; - tm.second_part= to->second_part; - - tm.day= to->day; - tm.hour= to->hour; - tm.minute= to->minute; - tm.second= to->second; - tm.year= to->year; - tm.month= to->month; - tm.neg= 0; - - param->set_time(&tm, TIMESTAMP_DATETIME, + param->set_time(to, MYSQL_TIMESTAMP_DATETIME, MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } void set_param_date(Item_param *param, uchar **pos, ulong len) { - TIME tm; MYSQL_TIME *to= (MYSQL_TIME*)*pos; - - tm.second_part= to->second_part; - tm.day= to->day; - tm.year= to->year; - tm.month= to->month; - tm.neg= 0; - tm.hour= tm.minute= tm.second= 0; - tm.second_part= 0; - tm.neg= 0; - - param->set_time(&tm, TIMESTAMP_DATE, + param->set_time(to, MYSQL_TIMESTAMP_DATE, MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } #endif /*!EMBEDDED_LIBRARY*/ @@ -580,6 +557,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, param->item_result_type= STRING_RESULT; } } + param->param_type= (enum enum_field_types) param_type; } #ifndef EMBEDDED_LIBRARY @@ -589,19 +567,20 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, */ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, - uchar *read_pos, uchar *data_end) + uchar *read_pos, uchar *data_end, + String *query) { THD *thd= stmt->thd; Item_param **begin= stmt->param_array; Item_param **end= begin + stmt->param_count; uint32 length= 0; - String str, query; + String str; const String *res; DBUG_ENTER("insert_params_withlog"); - if (query.copy(stmt->query, stmt->query_length, default_charset_info)) + if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); for (Item_param **it= begin; it < end; ++it) @@ -622,20 +601,18 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, if (param->convert_str_value(thd)) DBUG_RETURN(1); /* out of memory */ - if (query.replace(param->pos_in_query+length, 1, *res)) + 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); } static bool insert_params(Prepared_statement *stmt, uchar *null_array, - uchar *read_pos, uchar *data_end) + uchar *read_pos, uchar *data_end, + String *expanded_query) { Item_param **begin= stmt->param_array; Item_param **end= begin + stmt->param_count; @@ -700,7 +677,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, #else -static bool emb_insert_params(Prepared_statement *stmt) +static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query) { THD *thd= stmt->thd; Item_param **it= stmt->param_array; @@ -733,20 +710,20 @@ static bool emb_insert_params(Prepared_statement *stmt) } -static bool emb_insert_params_withlog(Prepared_statement *stmt) +static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query) { THD *thd= stmt->thd; Item_param **it= stmt->param_array; Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= thd->client_params; - String str, query; + String str; const String *res; uint32 length= 0; DBUG_ENTER("emb_insert_params_withlog"); - if (query.copy(stmt->query, stmt->query_length, default_charset_info)) + if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); for (; it < end; ++it, ++client_param) @@ -765,18 +742,16 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) *client_param->length : client_param->buffer_length); } - res= param->query_val_str(&str); - if (param->convert_str_value(thd)) - DBUG_RETURN(1); /* out of memory */ } - if (query.replace(param->pos_in_query+length, 1, *res)) + res= param->query_val_str(&str); + if (param->convert_str_value(thd)) + DBUG_RETURN(1); /* out of memory */ + + 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); } @@ -784,6 +759,105 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) /* + Set prepared statement parameters from user variables. + SYNOPSIS + insert_params_from_vars() + stmt Statement + varnames List of variables. Caller must ensure that number of variables + in the list is equal to number of statement parameters + query Ignored +*/ + +static bool insert_params_from_vars(Prepared_statement *stmt, + List<LEX_STRING>& varnames, + String *query __attribute__((unused))) +{ + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + user_var_entry *entry; + LEX_STRING *varname; + List_iterator<LEX_STRING> var_it(varnames); + DBUG_ENTER("insert_params_from_vars"); + + for (Item_param **it= begin; it < end; ++it) + { + Item_param *param= *it; + varname= var_it++; + entry= (user_var_entry*)hash_search(&stmt->thd->user_vars, + (byte*) varname->str, + varname->length); + if (param->set_from_user_var(stmt->thd, entry) || + param->convert_str_value(stmt->thd)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +/* + Do the same as insert_params_from_vars but also construct query text for + binary log. + SYNOPSIS + insert_params_from_vars() + stmt Statement + varnames List of variables. Caller must ensure that number of variables + in the list is equal to number of statement parameters + query The query with parameter markers replaced with their values +*/ + +static bool insert_params_from_vars_with_log(Prepared_statement *stmt, + List<LEX_STRING>& varnames, + String *query) +{ + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + user_var_entry *entry; + LEX_STRING *varname; + DBUG_ENTER("insert_params_from_vars"); + + List_iterator<LEX_STRING> var_it(varnames); + String str; + uint32 length= 0; + if (query->copy(stmt->query, stmt->query_length, default_charset_info)) + DBUG_RETURN(1); + + for (Item_param **it= begin; it < end; ++it) + { + Item_param *param= *it; + varname= var_it++; + if (get_var_with_binlog(stmt->thd, *varname, &entry)) + DBUG_RETURN(1); + DBUG_ASSERT(entry); + + if (param->set_from_user_var(stmt->thd, entry)) + DBUG_RETURN(1); + /* Insert @'escaped-varname' instead of parameter in the query */ + char *buf, *ptr; + str.length(0); + if (str.reserve(entry->name.length*2+3)) + DBUG_RETURN(1); + + buf= str.c_ptr_quick(); + ptr= buf; + *ptr++= '@'; + *ptr++= '\''; + ptr+= + escape_string_for_mysql(&my_charset_utf8_general_ci, + ptr, entry->name.str, entry->name.length); + *ptr++= '\''; + str.length(ptr - buf); + + if (param->convert_str_value(stmt->thd)) + DBUG_RETURN(1); /* out of memory */ + + if (query->replace(param->pos_in_query+length, 1, str)) + DBUG_RETURN(1); + length+= str.length()-1; + } + DBUG_RETURN(0); +} + +/* Validate INSERT statement: SYNOPSIS @@ -979,7 +1053,7 @@ static int mysql_test_delete(Prepared_statement *stmt, */ static int mysql_test_select(Prepared_statement *stmt, - TABLE_LIST *tables) + TABLE_LIST *tables, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -1009,30 +1083,32 @@ static int mysql_test_select(Prepared_statement *stmt, goto err; } + thd->used_tables= 0; // Updated by setup_fields + + // JOIN::prepare calls + if (unit->prepare(thd, 0, 0)) + { + send_error(thd); + goto err_prep; + } if (lex->describe) { - if (send_prep_stmt(stmt, 0)) - goto err; + if (!text_protocol && send_prep_stmt(stmt, 0)) + goto err_prep; + unit->cleanup(); } else { - thd->used_tables= 0; // Updated by setup_fields - - // JOIN::prepare calls - if (unit->prepare(thd, 0, 0)) + if (!text_protocol) { - send_error(thd); - goto err_prep; - } - - if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || + if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0) #ifndef EMBEDDED_LIBRARY - || net_flush(&thd->net) + || net_flush(&thd->net) #endif - ) - goto err_prep; - + ) + goto err_prep; + } unit->cleanup(); } thd->free_temporary_memory_pool_for_ps_preparing(); @@ -1199,6 +1275,7 @@ static int mysql_test_create_table(Prepared_statement *stmt, DBUG_ENTER("mysql_test_create_table"); THD *thd= stmt->thd; LEX *lex= stmt->lex; + SELECT_LEX *select_lex= &lex->select_lex; int res= 0; /* Skip first table, which is the table we are creating */ @@ -1207,8 +1284,12 @@ static int mysql_test_create_table(Prepared_statement *stmt, &create_table_local); if (!(res= create_table_precheck(thd, tables, create_table)) && - lex->select_lex.item_list.elements) + select_lex->item_list.elements) + { + select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; res= select_like_statement_test(stmt, tables); + select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE; + } /* put tables back for PS rexecuting */ tables= lex->link_first_table_back(tables, create_table, @@ -1292,7 +1373,11 @@ static int mysql_test_insert_select(Prepared_statement *stmt, (TABLE_LIST *)lex->select_lex.table_list.first; /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) first_local_table->next; - lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; res= select_like_statement_test(stmt, tables); /* revert changes*/ lex->select_lex.table_list.first= (byte*) first_local_table; @@ -1310,7 +1395,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, 0 success 1 error, sent to client */ -static int send_prepare_results(Prepared_statement *stmt) +static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -1347,7 +1432,7 @@ static int send_prepare_results(Prepared_statement *stmt) break; case SQLCOM_SELECT: - if ((res= mysql_test_select(stmt, tables))) + if ((res= mysql_test_select(stmt, tables, text_protocol))) goto error; /* Statement and field info has already been sent */ DBUG_RETURN(0); @@ -1405,7 +1490,7 @@ static int send_prepare_results(Prepared_statement *stmt) goto error; } if (res == 0) - DBUG_RETURN(send_prep_stmt(stmt, 0)); + DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0)); error: if (res < 0) send_error(thd,thd->killed_errno()); @@ -1446,20 +1531,36 @@ static bool init_param_array(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. 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_array, so that a fast and direct - retrieval can be made without going through all field - items. + Given a query string with parameter markers, create a Prepared Statement + from it and send PS info back to the client. + + SYNOPSIS + mysql_stmt_prepare() + packet query to be prepared + packet_length query string length, including ignored trailing NULL or + quote char. + name NULL or statement name. For unnamed statements binary PS + protocol is used, for named statements text protocol is + used. + RETURN + 0 OK, statement prepared successfully + other Error + + NOTES + This function parses the query and sends the total number of parameters + and resultset metadata information back to client (if any), without + executing the query i.e. without any log/disk writes. This allows 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_array, so + that a fast and direct retrieval can be made without going through all + field items. + */ -void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) +int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, + LEX_STRING *name) { LEX *lex; Prepared_statement *stmt= new Prepared_statement(thd); @@ -1471,14 +1572,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) if (stmt == 0) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); + } + + if (name) + { + stmt->name.length= name->length; + if (!(stmt->name.str= memdup_root(&stmt->mem_root, (char*)name->str, + name->length))) + { + delete stmt; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_RETURN(1); + } } if (thd->stmt_map.insert(stmt)) { delete stmt; send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); } thd->stmt_backup.set_statement(thd); @@ -1495,7 +1608,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); } mysql_log.write(thd, COM_PREPARE, "%s", packet); @@ -1507,7 +1620,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) error= yyparse((void *)thd) || thd->is_fatal_error || init_param_array(stmt) || - send_prepare_results(stmt); + send_prepare_results(stmt, test(name)); /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1530,9 +1643,14 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); + stmt= NULL; /* error is sent inside yyparse/send_prepare_results */ } - DBUG_VOID_RETURN; + else + stmt->setup_set_params(); + + DBUG_RETURN(!stmt); + } /* Reinit statement before execution */ @@ -1545,6 +1663,9 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) { if (!sl->first_execution) { + /* remove option which was put by mysql_explain_union() */ + sl->options&= ~SELECT_DESCRIBE; + /* Copy WHERE clause pointers to avoid damaging they by optimisation */ @@ -1609,13 +1730,21 @@ static void reset_stmt_params(Prepared_statement *stmt) Executes previously prepared query. If there is any parameters, then replace markers with the data supplied from client, and then execute the query. - SYNOPSYS + SYNOPSIS mysql_stmt_execute() + thd Current thread + packet Query string + packet_length Query string length, including terminator character. */ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); + /* + Query text for binary log, or empty string if the query is not put into + binary log. + */ + String expanded_query; #ifndef EMBEDDED_LIBRARY uchar *packet_end= (uchar *) packet + packet_length - 1; #endif @@ -1623,7 +1752,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_ENTER("mysql_stmt_execute"); packet+= 9; /* stmt_id + 5 bytes of flags */ - + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute", SEND_ERROR))) DBUG_VOID_RETURN; @@ -1637,15 +1766,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } - thd->stmt_backup.set_statement(thd); - thd->set_statement(stmt); - reset_stmt_for_execute(thd, stmt->lex); #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { uchar *null_array= (uchar *) packet; if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) || - stmt->set_params(stmt, null_array, (uchar *) packet, packet_end)) + stmt->set_params(stmt, null_array, (uchar *) packet, packet_end, + &expanded_query)) goto set_params_data_err; } #else @@ -1654,55 +1781,126 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) we set params, and also we don't need to parse packet. So we do it in one function. */ - if (stmt->param_count && stmt->set_params_data(stmt)) + if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif - - if (!(specialflag & SPECIAL_NO_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->current_arena= stmt; thd->protocol= &thd->protocol_prep; // Switch to binary protocol - mysql_execute_command(thd); - thd->lex->unit.cleanup(); + execute_stmt(thd, stmt, &expanded_query, true); thd->protocol= &thd->protocol_simple; // Use normal protocol thd->current_arena= 0; + DBUG_VOID_RETURN; - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(), WAIT_PRIOR); - - cleanup_items(stmt->free_list); +set_params_data_err: reset_stmt_params(stmt); - close_thread_tables(thd); // to close derived tables - thd->set_statement(&thd->stmt_backup); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); + send_error(thd); + DBUG_VOID_RETURN; +} + + +/* + Execute prepared statement using parameter values from + lex->prepared_stmt_params and send result to the client using text protocol. +*/ + +void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) +{ + Prepared_statement *stmt; /* - Free Items that were created during this execution of the PS by query - optimizer. + Query text for binary log, or empty string if the query is not put into + binary log. */ - free_items(thd->free_list); + String expanded_query; + DBUG_ENTER("mysql_sql_stmt_execute"); + + if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name))) + { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length, + stmt_name->str, "EXECUTE"); + send_error(thd); + DBUG_VOID_RETURN; + } + + if (stmt->param_count != thd->lex->prepared_stmt_params.elements) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); + send_error(thd); + DBUG_VOID_RETURN; + } + + thd->free_list= NULL; + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); + if (stmt->set_params_from_vars(stmt, + thd->stmt_backup.lex->prepared_stmt_params, + &expanded_query)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); + send_error(thd); + } + execute_stmt(thd, stmt, &expanded_query, false); DBUG_VOID_RETURN; +} -set_params_data_err: + +/* + Execute prepared statement. + SYNOPSIS + execute_stmt() + thd Current thread + stmt Statement to execute + expanded_query If binary log is enabled, query string with parameter + placeholders replaced with actual values. Otherwise empty + string. + NOTES + Caller must set parameter values and thd::protocol. + thd->free_list is assumed to be garbage. +*/ + +static void execute_stmt(THD *thd, Prepared_statement *stmt, + String *expanded_query, bool set_context) +{ + DBUG_ENTER("execute_stmt"); + if (set_context) + { + thd->free_list= NULL; + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); + } + reset_stmt_for_execute(stmt); + + if (expanded_query->length() && + alloc_query(thd, (char *)expanded_query->ptr(), + expanded_query->length()+1)) + { + my_error(ER_OUTOFMEMORY, 0, expanded_query->length()); + DBUG_VOID_RETURN; + } + + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); + mysql_execute_command(thd); + thd->lex->unit.cleanup(); + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(), WAIT_PRIOR); + + /* Free Items that were created during this execution of the PS. */ + free_items(thd->free_list); + cleanup_items(stmt->free_list); reset_stmt_params(stmt); + close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); - my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); - send_error(thd); DBUG_VOID_RETURN; } /* - Reset a prepared statement in case there was a recoverable error. + Reset a prepared statement in case there was a recoverable error. SYNOPSIS mysql_stmt_reset() - thd Thread handle - packet Packet with stmt id + thd Thread handle + packet Packet with stmt id DESCRIPTION This function resets statement to the state it was right after prepare. @@ -1842,8 +2040,14 @@ Prepared_statement::Prepared_statement(THD *thd_arg) get_longdata_error(0) { *last_error= '\0'; - if (mysql_bin_log.is_open()) +} + +void Prepared_statement::setup_set_params() +{ + /* Setup binary logging */ + if (mysql_bin_log.is_open() && is_update_query(lex->sql_command)) { + set_params_from_vars= insert_params_from_vars_with_log; #ifndef EMBEDDED_LIBRARY set_params= insert_params_withlog; #else @@ -1851,14 +2055,16 @@ Prepared_statement::Prepared_statement(THD *thd_arg) #endif } else + { + set_params_from_vars= insert_params_from_vars; #ifndef EMBEDDED_LIBRARY set_params= insert_params; #else set_params_data= emb_insert_params; #endif + } } - Prepared_statement::~Prepared_statement() { free_items(free_list); |