mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
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.
This commit is contained in:
15
sql/item.cc
15
sql/item.cc
@ -1579,9 +1579,8 @@ Item_splocal::Item_splocal(const LEX_STRING &sp_var_name,
|
||||
enum_field_types sp_var_type,
|
||||
uint pos_in_q, uint len_in_q)
|
||||
:Item_sp_variable(sp_var_name.str, sp_var_name.length),
|
||||
m_var_idx(sp_var_idx),
|
||||
limit_clause_param(FALSE),
|
||||
pos_in_query(pos_in_q), len_in_query(len_in_q)
|
||||
Rewritable_query_parameter(pos_in_q, len_in_q),
|
||||
m_var_idx(sp_var_idx)
|
||||
{
|
||||
maybe_null= TRUE;
|
||||
|
||||
@ -3230,14 +3229,13 @@ default_set_param_func(Item_param *param,
|
||||
|
||||
|
||||
Item_param::Item_param(uint pos_in_query_arg) :
|
||||
Rewritable_query_parameter(pos_in_query_arg, 1),
|
||||
state(NO_VALUE), inout(IN_PARAM),
|
||||
item_result_type(STRING_RESULT),
|
||||
/* Don't pretend to be a literal unless value for this item is set. */
|
||||
item_type(PARAM_ITEM),
|
||||
param_type(MYSQL_TYPE_VARCHAR),
|
||||
pos_in_query(pos_in_query_arg),
|
||||
set_param_func(default_set_param_func),
|
||||
limit_clause_param(FALSE),
|
||||
m_out_param_info(NULL)
|
||||
{
|
||||
name= (char*) "?";
|
||||
@ -4102,6 +4100,13 @@ void Item_param::make_field(Send_field *field)
|
||||
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
|
||||
****************************************************************************/
|
||||
|
123
sql/item.h
123
sql/item.h
@ -528,6 +528,70 @@ public:
|
||||
{ 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
|
||||
{
|
||||
@ -1433,6 +1497,8 @@ public:
|
||||
}
|
||||
|
||||
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
|
||||
@ -1691,7 +1757,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str)
|
||||
*****************************************************************************/
|
||||
|
||||
class Item_splocal :public Item_sp_variable,
|
||||
private Settable_routine_parameter
|
||||
private Settable_routine_parameter,
|
||||
public Rewritable_query_parameter
|
||||
{
|
||||
uint m_var_idx;
|
||||
|
||||
@ -1699,33 +1766,6 @@ class Item_splocal :public Item_sp_variable,
|
||||
Item_result m_result_type;
|
||||
enum_field_types m_field_type;
|
||||
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,
|
||||
enum_field_types sp_var_type,
|
||||
uint pos_in_q= 0, uint len_in_q= 0);
|
||||
@ -1751,10 +1791,13 @@ private:
|
||||
public:
|
||||
Item_splocal *get_item_splocal() { return this; }
|
||||
|
||||
Rewritable_query_parameter *get_rewritable_query_parameter()
|
||||
{ return this; }
|
||||
|
||||
Settable_routine_parameter *get_settable_routine_parameter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
{ return this; }
|
||||
|
||||
bool append_for_log(THD *thd, String *str);
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
@ -2229,7 +2272,8 @@ public:
|
||||
/* Item represents one placeholder ('?') of prepared statement */
|
||||
|
||||
class Item_param :public Item,
|
||||
private Settable_routine_parameter
|
||||
private Settable_routine_parameter,
|
||||
public Rewritable_query_parameter
|
||||
{
|
||||
char cnvbuf[MAX_FIELD_WIDTH];
|
||||
String cnvstr;
|
||||
@ -2294,11 +2338,6 @@ public:
|
||||
supply for this placeholder in mysql_stmt_execute.
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -2364,14 +2403,14 @@ public:
|
||||
Otherwise return FALSE.
|
||||
*/
|
||||
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);
|
||||
|
||||
Rewritable_query_parameter *get_rewritable_query_parameter()
|
||||
{ return this; }
|
||||
Settable_routine_parameter *get_settable_routine_parameter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
{ return this; }
|
||||
|
||||
bool append_for_log(THD *thd, String *str);
|
||||
|
||||
private:
|
||||
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
|
||||
|
100
sql/sp_head.cc
100
sql/sp_head.cc
@ -149,13 +149,9 @@ sp_get_item_value(THD *thd, Item *item, String *str)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
char buf_holder[STRING_BUFFER_USUAL_SIZE];
|
||||
String buf(buf_holder, sizeof(buf_holder), result->charset());
|
||||
StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset());
|
||||
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(result->charset()->csname);
|
||||
if (cs->escape_with_backslash_is_dangerous)
|
||||
@ -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:
|
||||
- sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
|
||||
@ -979,84 +997,32 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
|
||||
DBUG_ENTER("subst_spvars");
|
||||
|
||||
Dynamic_array<Item_splocal*> sp_vars_uses;
|
||||
char *pbuf, *cur, buffer[512];
|
||||
String qbuf(buffer, sizeof(buffer), &my_charset_bin);
|
||||
int prev_pos, res, buf_len;
|
||||
char *pbuf;
|
||||
StringBuffer<512> qbuf;
|
||||
Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
|
||||
|
||||
/* Find all instances of Item_splocal used in this statement */
|
||||
for (Item *item= instr->free_list; item; item= item->next)
|
||||
{
|
||||
Item_splocal *item_spl= item->get_item_splocal();
|
||||
if (item_spl)
|
||||
{
|
||||
if (item_spl->pos_in_query)
|
||||
if (item_spl && item_spl->pos_in_query)
|
||||
sp_vars_uses.append(item_spl);
|
||||
}
|
||||
}
|
||||
if (!sp_vars_uses.elements())
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
/* Sort SP var refs by their occurences in the query */
|
||||
sp_vars_uses.sort(cmp_splocal_locations);
|
||||
|
||||
/*
|
||||
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;
|
||||
thd->query_name_consts= sp_vars_uses.elements();
|
||||
|
||||
for (Item_splocal **splocal= sp_vars_uses.front();
|
||||
splocal <= sp_vars_uses.back(); splocal++)
|
||||
{
|
||||
Item *val;
|
||||
|
||||
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;
|
||||
if (acc.append(*splocal))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* 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 ||
|
||||
qbuf.append(cur + prev_pos, query_str->length - prev_pos))
|
||||
if (acc.finalize())
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/*
|
||||
@ -1071,8 +1037,8 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
|
||||
<db_name> Name of current database
|
||||
<flags> Flags struct
|
||||
*/
|
||||
buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE + thd->db_length +
|
||||
QUERY_CACHE_FLAGS_SIZE + 1);
|
||||
int buf_len= (qbuf.length() + 1 + QUERY_CACHE_DB_LENGTH_SIZE +
|
||||
thd->db_length + QUERY_CACHE_FLAGS_SIZE + 1);
|
||||
if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
|
||||
{
|
||||
char *ptr= pbuf + qbuf.length();
|
||||
|
@ -873,14 +873,9 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
uint32 length= 0;
|
||||
String str;
|
||||
const String *res;
|
||||
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
|
||||
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)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
@ -913,15 +908,16 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
|
||||
*/
|
||||
else if (! is_param_long_data_type(param))
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
String *query)
|
||||
static bool emb_insert_params_with_log(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;
|
||||
const String *res;
|
||||
uint32 length= 0;
|
||||
|
||||
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
|
||||
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)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
@ -1087,15 +1075,15 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt,
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1232,16 +1220,11 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
||||
user_var_entry *entry;
|
||||
LEX_STRING *varname;
|
||||
List_iterator<LEX_STRING> var_it(varnames);
|
||||
String buf;
|
||||
const String *val;
|
||||
uint32 length= 0;
|
||||
THD *thd= stmt->thd;
|
||||
Copy_query_with_rewrite acc(thd, stmt->query(), stmt->query_length(), query);
|
||||
|
||||
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)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
@ -1257,15 +1240,16 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
||||
setup_one_conversion_function(thd, param, param->param_type);
|
||||
if (param->set_from_user_var(thd, entry))
|
||||
DBUG_RETURN(1);
|
||||
val= param->query_val_str(thd, &buf);
|
||||
|
||||
if (acc.append(param))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (param->convert_str_value(thd))
|
||||
DBUG_RETURN(1); /* out of memory */
|
||||
|
||||
if (query->replace(param->pos_in_query+length, 1, *val))
|
||||
DBUG_RETURN(1);
|
||||
length+= val->length()-1;
|
||||
}
|
||||
if (acc.finalize())
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
@ -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
|
||||
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)) ||
|
||||
opt_log || thd->variables.sql_log_slow ||
|
||||
query_cache_is_cacheable_query(lex))
|
||||
bool replace_params_with_values= false;
|
||||
// binlog
|
||||
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;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
|
Reference in New Issue
Block a user