Commit 013f0f6c authored by Sergei Golubchik's avatar Sergei Golubchik

cleanup: query rewrites for Item_param and Item_splocal

Fix query rewrites in PS code - it was memcpy-ing the same query tail
many times. Instead use the same logic as in SP code, copy query pieces
into the destination buffer.

Extract this logic into a separate class Rewritable_query_parameter
with Item_param and Item_splocal inheriting from it.

Create a helper class Copy_query_with_rewrite that incapsulates
the query rewriting logic, use it in SP and PS.
parent d7c1e0eb
...@@ -1579,9 +1579,8 @@ Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, ...@@ -1579,9 +1579,8 @@ Item_splocal::Item_splocal(const LEX_STRING &sp_var_name,
enum_field_types sp_var_type, enum_field_types sp_var_type,
uint pos_in_q, uint len_in_q) uint pos_in_q, uint len_in_q)
:Item_sp_variable(sp_var_name.str, sp_var_name.length), :Item_sp_variable(sp_var_name.str, sp_var_name.length),
m_var_idx(sp_var_idx), Rewritable_query_parameter(pos_in_q, len_in_q),
limit_clause_param(FALSE), m_var_idx(sp_var_idx)
pos_in_query(pos_in_q), len_in_query(len_in_q)
{ {
maybe_null= TRUE; maybe_null= TRUE;
...@@ -3230,14 +3229,13 @@ default_set_param_func(Item_param *param, ...@@ -3230,14 +3229,13 @@ default_set_param_func(Item_param *param,
Item_param::Item_param(uint pos_in_query_arg) : Item_param::Item_param(uint pos_in_query_arg) :
Rewritable_query_parameter(pos_in_query_arg, 1),
state(NO_VALUE), inout(IN_PARAM), state(NO_VALUE), inout(IN_PARAM),
item_result_type(STRING_RESULT), item_result_type(STRING_RESULT),
/* Don't pretend to be a literal unless value for this item is set. */ /* Don't pretend to be a literal unless value for this item is set. */
item_type(PARAM_ITEM), item_type(PARAM_ITEM),
param_type(MYSQL_TYPE_VARCHAR), param_type(MYSQL_TYPE_VARCHAR),
pos_in_query(pos_in_query_arg),
set_param_func(default_set_param_func), set_param_func(default_set_param_func),
limit_clause_param(FALSE),
m_out_param_info(NULL) m_out_param_info(NULL)
{ {
name= (char*) "?"; name= (char*) "?";
...@@ -4102,6 +4100,13 @@ void Item_param::make_field(Send_field *field) ...@@ -4102,6 +4100,13 @@ void Item_param::make_field(Send_field *field)
field->type= m_out_param_info->type; field->type= m_out_param_info->type;
} }
bool Item_param::append_for_log(THD *thd, String *str)
{
StringBuffer<STRING_BUFFER_USUAL_SIZE> buf;
const String *val= query_val_str(thd, &buf);
return str->append(*val);
}
/**************************************************************************** /****************************************************************************
Item_copy Item_copy
****************************************************************************/ ****************************************************************************/
......
...@@ -528,6 +528,70 @@ class Settable_routine_parameter ...@@ -528,6 +528,70 @@ class Settable_routine_parameter
{ return NULL; } { return NULL; }
}; };
/**
This is used for items in the query that needs to be rewritten
before binlogging
At the moment this applies to Item_param and Item_splocal
*/
class Rewritable_query_parameter
{
public:
/*
Offset inside the query text.
Value of 0 means that this object doesn't have to be replaced
(for example SP variables in control statements)
*/
uint pos_in_query;
/*
Byte length of parameter name in the statement. This is not
Item::name_length because name_length contains byte length of UTF8-encoded
name, but the query string is in the client charset.
*/
uint len_in_query;
bool limit_clause_param;
Rewritable_query_parameter(uint pos_in_q= 0, uint len_in_q= 0)
: pos_in_query(pos_in_q), len_in_query(len_in_q),
limit_clause_param(false)
{ }
virtual ~Rewritable_query_parameter() { }
virtual bool append_for_log(THD *thd, String *str) = 0;
};
class Copy_query_with_rewrite
{
THD *thd;
const char *src;
size_t src_len, from;
String *dst;
bool copy_up_to(size_t bytes)
{
DBUG_ASSERT(bytes >= from);
return dst->append(src + from, bytes - from);
}
public:
Copy_query_with_rewrite(THD *t, const char *s, size_t l, String *d)
:thd(t), src(s), src_len(l), from(0), dst(d) { }
bool append(Rewritable_query_parameter *p)
{
if (copy_up_to(p->pos_in_query) || p->append_for_log(thd, dst))
return true;
from= p->pos_in_query + p->len_in_query;
return false;
}
bool finalize()
{ return copy_up_to(src_len); }
};
struct st_dyncall_create_def struct st_dyncall_create_def
{ {
...@@ -1433,6 +1497,8 @@ class Item { ...@@ -1433,6 +1497,8 @@ class Item {
} }
virtual Item_splocal *get_item_splocal() { return 0; } virtual Item_splocal *get_item_splocal() { return 0; }
virtual Rewritable_query_parameter *get_rewritable_query_parameter()
{ return 0; }
/* /*
Return Settable_routine_parameter interface of the Item. Return 0 Return Settable_routine_parameter interface of the Item. Return 0
...@@ -1691,7 +1757,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str) ...@@ -1691,7 +1757,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str)
*****************************************************************************/ *****************************************************************************/
class Item_splocal :public Item_sp_variable, class Item_splocal :public Item_sp_variable,
private Settable_routine_parameter private Settable_routine_parameter,
public Rewritable_query_parameter
{ {
uint m_var_idx; uint m_var_idx;
...@@ -1699,33 +1766,6 @@ class Item_splocal :public Item_sp_variable, ...@@ -1699,33 +1766,6 @@ class Item_splocal :public Item_sp_variable,
Item_result m_result_type; Item_result m_result_type;
enum_field_types m_field_type; enum_field_types m_field_type;
public: public:
/*
If this variable is a parameter in LIMIT clause.
Used only during NAME_CONST substitution, to not append
NAME_CONST to the resulting query and thus not break
the slave.
*/
bool limit_clause_param;
/*
Position of this reference to SP variable in the statement (the
statement itself is in sp_instr_stmt::m_query).
This is valid only for references to SP variables in statements,
excluding DECLARE CURSOR statement. It is used to replace references to SP
variables with NAME_CONST calls when putting statements into the binary
log.
Value of 0 means that this object doesn't corresponding to reference to
SP variable in query text.
*/
uint pos_in_query;
/*
Byte length of SP variable name in the statement (see pos_in_query).
The value of this field may differ from the name_length value because
name_length contains byte length of UTF8-encoded item name, but
the query string (see sp_instr_stmt::m_query) is currently stored with
a charset from the SET NAMES statement.
*/
uint len_in_query;
Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx, Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx,
enum_field_types sp_var_type, enum_field_types sp_var_type,
uint pos_in_q= 0, uint len_in_q= 0); uint pos_in_q= 0, uint len_in_q= 0);
...@@ -1751,10 +1791,13 @@ class Item_splocal :public Item_sp_variable, ...@@ -1751,10 +1791,13 @@ class Item_splocal :public Item_sp_variable,
public: public:
Item_splocal *get_item_splocal() { return this; } Item_splocal *get_item_splocal() { return this; }
Rewritable_query_parameter *get_rewritable_query_parameter()
{ return this; }
Settable_routine_parameter *get_settable_routine_parameter() Settable_routine_parameter *get_settable_routine_parameter()
{ { return this; }
return this;
} bool append_for_log(THD *thd, String *str);
}; };
/***************************************************************************** /*****************************************************************************
...@@ -2229,7 +2272,8 @@ class Item_null_result :public Item_null ...@@ -2229,7 +2272,8 @@ class Item_null_result :public Item_null
/* Item represents one placeholder ('?') of prepared statement */ /* Item represents one placeholder ('?') of prepared statement */
class Item_param :public Item, class Item_param :public Item,
private Settable_routine_parameter private Settable_routine_parameter,
public Rewritable_query_parameter
{ {
char cnvbuf[MAX_FIELD_WIDTH]; char cnvbuf[MAX_FIELD_WIDTH];
String cnvstr; String cnvstr;
...@@ -2294,11 +2338,6 @@ class Item_param :public Item, ...@@ -2294,11 +2338,6 @@ class Item_param :public Item,
supply for this placeholder in mysql_stmt_execute. supply for this placeholder in mysql_stmt_execute.
*/ */
enum enum_field_types param_type; enum enum_field_types param_type;
/*
Offset of placeholder inside statement text. Used to create
no-placeholders version of this statement for the binary log.
*/
uint pos_in_query;
Item_param(uint pos_in_query_arg); Item_param(uint pos_in_query_arg);
...@@ -2364,14 +2403,14 @@ class Item_param :public Item, ...@@ -2364,14 +2403,14 @@ class Item_param :public Item,
Otherwise return FALSE. Otherwise return FALSE.
*/ */
bool eq(const Item *item, bool binary_cmp) const; bool eq(const Item *item, bool binary_cmp) const;
/** Item is a argument to a limit clause. */
bool limit_clause_param;
void set_param_type_and_swap_value(Item_param *from); void set_param_type_and_swap_value(Item_param *from);
Rewritable_query_parameter *get_rewritable_query_parameter()
{ return this; }
Settable_routine_parameter *get_settable_routine_parameter() Settable_routine_parameter *get_settable_routine_parameter()
{ { return this; }
return this;
} bool append_for_log(THD *thd, String *str);
private: private:
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it); virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
......
...@@ -149,13 +149,9 @@ sp_get_item_value(THD *thd, Item *item, String *str) ...@@ -149,13 +149,9 @@ sp_get_item_value(THD *thd, Item *item, String *str)
return NULL; return NULL;
{ {
char buf_holder[STRING_BUFFER_USUAL_SIZE]; StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
String buf(buf_holder, sizeof(buf_holder), result->charset());
CHARSET_INFO *cs= thd->variables.character_set_client; CHARSET_INFO *cs= thd->variables.character_set_client;
/* We must reset length of the buffer, because of String specificity. */
buf.length(0);
buf.append('_'); buf.append('_');
buf.append(result->charset()->csname); buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous) if (cs->escape_with_backslash_is_dangerous)
...@@ -178,6 +174,28 @@ sp_get_item_value(THD *thd, Item *item, String *str) ...@@ -178,6 +174,28 @@ sp_get_item_value(THD *thd, Item *item, String *str)
} }
bool Item_splocal::append_for_log(THD *thd, String *str)
{
if (fix_fields(thd, NULL))
return true;
if (limit_clause_param)
return str->append_ulonglong(val_uint());
if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
str->append(&m_name) ||
str->append(STRING_WITH_LEN("',")))
return true;
StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
String *str_value= sp_get_item_value(thd, this_item(), &str_value_holder);
if (str_value)
return str->append(*str_value) || str->append(')');
else
return str->append(STRING_WITH_LEN("NULL)"));
}
/** /**
Returns a combination of: Returns a combination of:
- sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might - sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
...@@ -979,19 +997,16 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) ...@@ -979,19 +997,16 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
DBUG_ENTER("subst_spvars"); DBUG_ENTER("subst_spvars");
Dynamic_array<Item_splocal*> sp_vars_uses; Dynamic_array<Item_splocal*> sp_vars_uses;
char *pbuf, *cur, buffer[512]; char *pbuf;
String qbuf(buffer, sizeof(buffer), &my_charset_bin); StringBuffer<512> qbuf;
int prev_pos, res, buf_len; Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
/* Find all instances of Item_splocal used in this statement */ /* Find all instances of Item_splocal used in this statement */
for (Item *item= instr->free_list; item; item= item->next) for (Item *item= instr->free_list; item; item= item->next)
{ {
Item_splocal *item_spl= item->get_item_splocal(); Item_splocal *item_spl= item->get_item_splocal();
if (item_spl) if (item_spl && item_spl->pos_in_query)
{ sp_vars_uses.append(item_spl);
if (item_spl->pos_in_query)
sp_vars_uses.append(item_spl);
}
} }
if (!sp_vars_uses.elements()) if (!sp_vars_uses.elements())
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
...@@ -999,64 +1014,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) ...@@ -999,64 +1014,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
/* Sort SP var refs by their occurences in the query */ /* Sort SP var refs by their occurences in the query */
sp_vars_uses.sort(cmp_splocal_locations); sp_vars_uses.sort(cmp_splocal_locations);
/* thd->query_name_consts= sp_vars_uses.elements();
Construct a statement string where SP local var refs are replaced
with "NAME_CONST(name, value)"
*/
qbuf.length(0);
cur= query_str->str;
prev_pos= res= 0;
thd->query_name_consts= 0;
for (Item_splocal **splocal= sp_vars_uses.front(); for (Item_splocal **splocal= sp_vars_uses.front();
splocal <= sp_vars_uses.back(); splocal++) splocal <= sp_vars_uses.back(); splocal++)
{ {
Item *val; if (acc.append(*splocal))
DBUG_RETURN(TRUE);
char str_buffer[STRING_BUFFER_USUAL_SIZE];
String str_value_holder(str_buffer, sizeof(str_buffer),
&my_charset_latin1);
String *str_value;
/* append the text between sp ref occurences */
res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos);
prev_pos= (*splocal)->pos_in_query + (*splocal)->len_in_query;
res|= (*splocal)->fix_fields(thd, (Item **) splocal);
if (res)
break;
if ((*splocal)->limit_clause_param)
{
res|= qbuf.append_ulonglong((*splocal)->val_uint());
if (res)
break;
continue;
}
/* append the spvar substitute */
res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('"));
res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length);
res|= qbuf.append(STRING_WITH_LEN("',"));
if (res)
break;
val= (*splocal)->this_item();
DBUG_PRINT("info", ("print 0x%lx", (long) val));
str_value= sp_get_item_value(thd, val, &str_value_holder);
if (str_value)
res|= qbuf.append(*str_value);
else
res|= qbuf.append(STRING_WITH_LEN("NULL"));
res|= qbuf.append(')');
if (res)
break;
thd->query_name_consts++;
} }
if (res || if (acc.finalize())
qbuf.append(cur + prev_pos, query_str->length - prev_pos))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* /*
...@@ -1071,8 +1037,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) ...@@ -1071,8 +1037,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
<db_name> Name of current database <db_name> Name of current database
<flags> Flags struct <flags> Flags struct
*/ */
buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE + thd->db_length + int buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE + 1); thd->db_length + QUERY_CACHE_FLAGS_SIZE + 1);
if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len))) if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
{ {
char *ptr= pbuf + qbuf.length(); char *ptr= pbuf + qbuf.length();
......
...@@ -873,14 +873,9 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array, ...@@ -873,14 +873,9 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
THD *thd= stmt->thd; THD *thd= stmt->thd;
Item_param **begin= stmt->param_array; Item_param **begin= stmt->param_array;
Item_param **end= begin + stmt->param_count; Item_param **end= begin + stmt->param_count;
uint32 length= 0; Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
String str;
const String *res;
DBUG_ENTER("insert_params_with_log"); DBUG_ENTER("insert_params_with_log");
if (query->copy(stmt->query(), stmt->query_length(), default_charset_info))
DBUG_RETURN(1);
for (Item_param **it= begin; it < end; ++it) for (Item_param **it= begin; it < end; ++it)
{ {
Item_param *param= *it; Item_param *param= *it;
...@@ -913,15 +908,16 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array, ...@@ -913,15 +908,16 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
*/ */
else if (! is_param_long_data_type(param)) else if (! is_param_long_data_type(param))
DBUG_RETURN(1); DBUG_RETURN(1);
res= param->query_val_str(thd, &str);
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
if (query->replace(param->pos_in_query+length, 1, *res)) if (acc.append(param))
DBUG_RETURN(1); DBUG_RETURN(1);
length+= res->length()-1; if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
} }
if (acc.finalize())
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -1050,23 +1046,15 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query) ...@@ -1050,23 +1046,15 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
} }
static bool emb_insert_params_with_log(Prepared_statement *stmt, static bool emb_insert_params_with_log(Prepared_statement *stmt, String *query)
String *query)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
Item_param **it= stmt->param_array; Item_param **it= stmt->param_array;
Item_param **end= it + stmt->param_count; Item_param **end= it + stmt->param_count;
MYSQL_BIND *client_param= thd->client_params; MYSQL_BIND *client_param= thd->client_params;
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
String str;
const String *res;
uint32 length= 0;
DBUG_ENTER("emb_insert_params_with_log"); DBUG_ENTER("emb_insert_params_with_log");
if (query->copy(stmt->query(), stmt->query_length(), default_charset_info))
DBUG_RETURN(1);
for (; it < end; ++it, ++client_param) for (; it < end; ++it, ++client_param)
{ {
Item_param *param= *it; Item_param *param= *it;
...@@ -1087,15 +1075,15 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt, ...@@ -1087,15 +1075,15 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt,
DBUG_RETURN(1); DBUG_RETURN(1);
} }
} }
res= param->query_val_str(thd, &str); if (acc.append(param))
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); DBUG_RETURN(1);
length+= res->length()-1; if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
} }
if (acc.finalize())
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -1232,16 +1220,11 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -1232,16 +1220,11 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
user_var_entry *entry; user_var_entry *entry;
LEX_STRING *varname; LEX_STRING *varname;
List_iterator<LEX_STRING> var_it(varnames); List_iterator<LEX_STRING> var_it(varnames);
String buf;
const String *val;
uint32 length= 0;
THD *thd= stmt->thd; THD *thd= stmt->thd;
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
DBUG_ENTER("insert_params_from_vars_with_log"); DBUG_ENTER("insert_params_from_vars_with_log");
if (query->copy(stmt->query(), stmt->query_length(), default_charset_info))
DBUG_RETURN(1);
for (Item_param **it= begin; it < end; ++it) for (Item_param **it= begin; it < end; ++it)
{ {
Item_param *param= *it; Item_param *param= *it;
...@@ -1257,15 +1240,16 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -1257,15 +1240,16 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
setup_one_conversion_function(thd, param, param->param_type); setup_one_conversion_function(thd, param, param->param_type);
if (param->set_from_user_var(thd, entry)) if (param->set_from_user_var(thd, entry))
DBUG_RETURN(1); DBUG_RETURN(1);
val= param->query_val_str(thd, &buf);
if (param->convert_str_value(thd)) if (acc.append(param))
DBUG_RETURN(1); /* out of memory */ DBUG_RETURN(1);
if (query->replace(param->pos_in_query+length, 1, *val)) if (param->convert_str_value(thd))
DBUG_RETURN(1); DBUG_RETURN(1);
length+= val->length()-1;
} }
if (acc.finalize())
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -3244,9 +3228,15 @@ void Prepared_statement::setup_set_params() ...@@ -3244,9 +3228,15 @@ void Prepared_statement::setup_set_params()
Decide if we have to expand the query (because we must write it to logs or Decide if we have to expand the query (because we must write it to logs or
because we want to look it up in the query cache) or not. because we want to look it up in the query cache) or not.
*/ */
if ((mysql_bin_log.is_open() && is_update_query(lex->sql_command)) || bool replace_params_with_values= false;
opt_log || thd->variables.sql_log_slow || // binlog
query_cache_is_cacheable_query(lex)) replace_params_with_values|= mysql_bin_log.is_open() && is_update_query(lex->sql_command);
// general or slow log
replace_params_with_values|= opt_log || thd->variables.sql_log_slow;
// query cache
replace_params_with_values|= query_cache_is_cacheable_query(lex);
if (replace_params_with_values)
{ {
set_params_from_vars= insert_params_from_vars_with_log; set_params_from_vars= insert_params_from_vars_with_log;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment