diff options
author | unknown <sergefp@mysql.com> | 2004-06-07 12:21:26 +0400 |
---|---|---|
committer | unknown <sergefp@mysql.com> | 2004-06-07 12:21:26 +0400 |
commit | 27ad8e4078780e212a5eb7d1987186cd75c5bbe3 (patch) | |
tree | 4ee21efb23af3fdba411054523dabc8d51a75f93 /sql/sql_prepare.cc | |
parent | 938fb67bd2bd139bb02d98d0b0298ab8d4bf901d (diff) | |
parent | 1d4ee7f81c1fff2579c3aab8690f977e99a4840e (diff) | |
download | mariadb-git-27ad8e4078780e212a5eb7d1987186cd75c5bbe3.tar.gz |
Merged
sql/sql_class.h:
Auto merged
sql/sql_prepare.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
mysys/my_error.c:
Hand merged
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r-- | sql/sql_prepare.cc | 393 |
1 files changed, 308 insertions, 85 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 1713c81a096..a15583b17e4 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -91,16 +91,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=false); /****************************************************************************** Implementation @@ -129,7 +135,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; @@ -594,19 +601,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) @@ -627,20 +635,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; @@ -705,7 +711,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; @@ -738,20 +744,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) @@ -770,18 +776,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); } @@ -789,6 +793,106 @@ 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; + const String *res; + 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 @@ -984,7 +1088,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; @@ -1016,7 +1120,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (lex->describe) { - if (send_prep_stmt(stmt, 0)) + if (!text_protocol && send_prep_stmt(stmt, 0)) goto err; } else @@ -1030,14 +1134,16 @@ static int mysql_test_select(Prepared_statement *stmt, goto err_prep; } - if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || + if (!text_protocol) + { + 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(); @@ -1315,7 +1421,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; @@ -1352,7 +1458,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); @@ -1410,7 +1516,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 ? ER_SERVER_SHUTDOWN : 0); @@ -1451,20 +1557,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); @@ -1476,14 +1598,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, (byte*)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); @@ -1500,7 +1634,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); @@ -1512,7 +1646,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)) @@ -1528,10 +1662,12 @@ 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 */ } else { + stmt->setup_set_params(); SELECT_LEX *sl= stmt->lex->all_selects_list; /* Save WHERE clause pointers, because they may be changed during query @@ -1541,8 +1677,10 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { sl->prep_where= sl->where; } + } - DBUG_VOID_RETURN; + + DBUG_RETURN(!stmt); } /* Reinit statement before execution */ @@ -1617,13 +1755,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 @@ -1631,7 +1777,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; @@ -1645,15 +1791,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(stmt); #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 @@ -1662,53 +1806,124 @@ 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 + thd->protocol= &thd->protocol_prep; // Switch to binary protocol + execute_stmt(thd, stmt, &expanded_query); + thd->protocol= &thd->protocol_simple; // Use normal protocol + DBUG_VOID_RETURN; - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); - +set_params_data_err: + reset_stmt_params(stmt); + 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; /* - 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 .. + Query text for binary log, or empty string if the query is not put into + binary log. */ - thd->protocol= &thd->protocol_prep; // Switch to binary protocol + 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); + DBUG_VOID_RETURN; +} + + +/* + 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(); - thd->protocol= &thd->protocol_simple; // Use normal protocol - 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 + close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); - /* - Free Items that were created during this execution of the PS by query - optimizer. - */ - free_items(thd->free_list); - DBUG_VOID_RETURN; - -set_params_data_err: - reset_stmt_params(stmt); - 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. @@ -1848,8 +2063,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 @@ -1857,14 +2078,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); |