Commit 9626b4d0 authored by unknown's avatar unknown

* New, binlog-aware character sets support in SQL Syntax for Prepared statements.

 * The prepared statement query is put into binary log on execution only if it is an update query.


sql/item_func.cc:
  New, binlog-aware character sets support in SQL Syntax for Prepared statements.
sql/mysql_priv.h:
  New, binlog-aware character sets support in SQL Syntax for Prepared statements.
parent 4cca7db7
...@@ -2583,27 +2583,39 @@ longlong Item_func_get_user_var::val_int() ...@@ -2583,27 +2583,39 @@ longlong Item_func_get_user_var::val_int()
/* /*
Get variable by name and, if necessary, put the record of variable
use into the binary log.
SYNOPSIS
get_var_with_binlog()
thd Current thread
name Variable name
out_entry [out] variable structure or NULL. The pointer is set
regardless of whether function succeeded or not.
When a user variable is invoked from an update query (INSERT, UPDATE etc), When a user variable is invoked from an update query (INSERT, UPDATE etc),
stores this variable and its value in thd->user_var_events, so that it can be stores this variable and its value in thd->user_var_events, so that it can be
written to the binlog (will be written just before the query is written, see written to the binlog (will be written just before the query is written, see
log.cc). log.cc).
RETURN
0 OK
1 Failed to put appropiate record into binary log
*/ */
void Item_func_get_user_var::fix_length_and_dec() int get_var_with_binlog(THD *thd, LEX_STRING &name,
user_var_entry **out_entry)
{ {
THD *thd=current_thd;
BINLOG_USER_VAR_EVENT *user_var_event; BINLOG_USER_VAR_EVENT *user_var_event;
maybe_null=1; user_var_entry *var_entry;
decimals=NOT_FIXED_DEC; var_entry= get_variable(&thd->user_vars, name, 0);
max_length=MAX_BLOB_WIDTH;
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
null_value= 1;
else
collation.set(var_entry->collation);
if (!(opt_bin_log && is_update_query(thd->lex->sql_command))) if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
return; {
*out_entry= var_entry;
return 0;
}
if (!var_entry) if (!var_entry)
{ {
...@@ -2630,13 +2642,16 @@ void Item_func_get_user_var::fix_length_and_dec() ...@@ -2630,13 +2642,16 @@ void Item_func_get_user_var::fix_length_and_dec()
if (!(var_entry= get_variable(&thd->user_vars, name, 0))) if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
goto err; goto err;
} }
/*
If this variable was already stored in user_var_events by this query
(because it's used in more than one place in the query), don't store
it.
*/
else if (var_entry->used_query_id == thd->query_id) else if (var_entry->used_query_id == thd->query_id)
return; {
/*
If this variable was already stored in user_var_events by this query
(because it's used in more than one place in the query), don't store
it.
*/
*out_entry= var_entry;
return 0;
}
uint size; uint size;
/* /*
...@@ -2671,11 +2686,34 @@ void Item_func_get_user_var::fix_length_and_dec() ...@@ -2671,11 +2686,34 @@ void Item_func_get_user_var::fix_length_and_dec()
var_entry->used_query_id= thd->query_id; var_entry->used_query_id= thd->query_id;
if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event)) if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event))
goto err; goto err;
return; *out_entry= var_entry;
return 0;
err: err:
thd->fatal_error(); *out_entry= var_entry;
return 1;
}
void Item_func_get_user_var::fix_length_and_dec()
{
THD *thd=current_thd;
int error;
maybe_null=1;
decimals=NOT_FIXED_DEC;
max_length=MAX_BLOB_WIDTH;
error= get_var_with_binlog(thd, name, &var_entry);
if (!var_entry)
null_value= 1;
else
collation.set(var_entry->collation);
if (error)
thd->fatal_error();
return; return;
} }
......
...@@ -1069,6 +1069,9 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, ...@@ -1069,6 +1069,9 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component); LEX_STRING component);
Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name,
uint length, const char *item_name); uint length, const char *item_name);
/* item_func.cc */
int get_var_with_binlog(THD *thd, LEX_STRING &name,
user_var_entry **out_entry);
/* log.cc */ /* log.cc */
bool flush_error_log(void); bool flush_error_log(void);
......
...@@ -101,11 +101,12 @@ public: ...@@ -101,11 +101,12 @@ public:
public: public:
Prepared_statement(THD *thd_arg); Prepared_statement(THD *thd_arg);
virtual ~Prepared_statement(); virtual ~Prepared_statement();
void setup_set_params();
virtual Statement::Type type() const; virtual Statement::Type type() const;
}; };
static void execute_stmt(THD *thd, Prepared_statement *stmt, static void execute_stmt(THD *thd, Prepared_statement *stmt,
String *expanded_query); String *expanded_query, bool set_context=false);
/****************************************************************************** /******************************************************************************
Implementation Implementation
...@@ -769,12 +770,14 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query) ...@@ -769,12 +770,14 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
*client_param->length : *client_param->length :
client_param->buffer_length); client_param->buffer_length);
} }
res= param->query_val_str(&str);
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
} }
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)) if (query->replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1); DBUG_RETURN(1);
length+= res->length()-1; length+= res->length()-1;
} }
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -820,18 +823,45 @@ static bool insert_params_from_vars(Prepared_statement *stmt, ...@@ -820,18 +823,45 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
param->set_double(*(double*)entry->value); param->set_double(*(double*)entry->value);
break; break;
case INT_RESULT: case INT_RESULT:
param->set_int(*(longlong*)entry->value); param->set_int(*(longlong*)entry->value, 21);
break; break;
case STRING_RESULT: case STRING_RESULT:
param->set_value(entry->value, entry->length, {
entry->collation.collation); CHARSET_INFO *fromcs= entry->collation.collation;
CHARSET_INFO *tocs= stmt->thd->variables.collation_connection;
uint32 dummy_offset;
param->value.cs_info.character_set_client= fromcs;
/*
Setup source and destination character sets so that they
are different only if conversion is necessary: this will
make later checks easier.
*/
param->value.cs_info.final_character_set_of_str_value=
String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
tocs : fromcs;
/*
Exact value of max_length is not known unless data is converted to
charset of connection, so we have to set it later.
*/
param->item_type= Item::STRING_ITEM;
param->item_result_type= STRING_RESULT;
if (param->set_str((const char *)entry->value, entry->length))
DBUG_RETURN(1);
}
break; break;
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
param->set_null();
} }
} }
else else
param->maybe_null= param->null_value= param->value_is_set= 1; param->set_null();
if (param->convert_str_value(stmt->thd))
DBUG_RETURN(1); /* out of memory */
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -869,10 +899,10 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -869,10 +899,10 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
{ {
Item_param *param= *it; Item_param *param= *it;
varname= var_it++; varname= var_it++;
if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars, if (get_var_with_binlog(stmt->thd, *varname, &entry))
(byte*) varname->str, DBUG_RETURN(1);
varname->length)) DBUG_ASSERT(entry);
&& entry->value) if (entry->value)
{ {
param->item_result_type= entry->type; param->item_result_type= entry->type;
switch (entry->type) switch (entry->type)
...@@ -881,26 +911,65 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -881,26 +911,65 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
param->set_double(*(double*)entry->value); param->set_double(*(double*)entry->value);
break; break;
case INT_RESULT: case INT_RESULT:
param->set_int(*(longlong*)entry->value); param->set_int(*(longlong*)entry->value, 21);
break; break;
case STRING_RESULT: case STRING_RESULT:
param->set_value(entry->value, entry->length, {
entry->collation.collation); CHARSET_INFO *fromcs= entry->collation.collation;
CHARSET_INFO *tocs= stmt->thd->variables.collation_connection;
uint32 dummy_offset;
param->value.cs_info.character_set_client= fromcs;
/*
Setup source and destination character sets so that they
are different only if conversion is necessary: this will
make later checks easier.
*/
param->value.cs_info.final_character_set_of_str_value=
String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
tocs : fromcs;
/*
Exact value of max_length is not known unless data is converted to
charset of connection, so we have to set it later.
*/
param->item_type= Item::STRING_ITEM;
param->item_result_type= STRING_RESULT;
if (param->set_str((const char *)entry->value, entry->length))
DBUG_RETURN(1);
}
break; break;
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
param->set_null();
} }
res= param->query_val_str(&str);
} }
else else
{ param->set_null();
param->maybe_null= param->null_value= param->value_is_set= 1;
res= &my_null_string;
}
if (query->replace(param->pos_in_query+length, 1, *res)) /* 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); DBUG_RETURN(1);
length+= res->length()-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); DBUG_RETURN(0);
} }
...@@ -1680,6 +1749,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1680,6 +1749,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
} }
else else
{ {
stmt->setup_set_params();
SELECT_LEX *sl= stmt->lex->all_selects_list; SELECT_LEX *sl= stmt->lex->all_selects_list;
/* /*
Save WHERE clause pointers, because they may be changed during query Save WHERE clause pointers, because they may be changed during query
...@@ -1689,7 +1759,9 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, ...@@ -1689,7 +1759,9 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
{ {
sl->prep_where= sl->where; sl->prep_where= sl->where;
} }
} }
DBUG_RETURN(!stmt); DBUG_RETURN(!stmt);
} }
...@@ -1809,7 +1881,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1809,7 +1881,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
we set params, and also we don't need to parse packet. we set params, and also we don't need to parse packet.
So we do it in one function. 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; goto set_params_data_err;
#endif #endif
thd->protocol= &thd->protocol_prep; // Switch to binary protocol thd->protocol= &thd->protocol_prep; // Switch to binary protocol
...@@ -1853,7 +1925,10 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) ...@@ -1853,7 +1925,10 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
/* Item_param allows setting parameters in COM_EXECUTE only */ /* Item_param allows setting parameters in COM_EXECUTE only */
thd->command= COM_EXECUTE; thd->command= COM_EXECUTE;
if (stmt->set_params_from_vars(stmt, thd->lex->prepared_stmt_params, 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)) &expanded_query))
{ {
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
...@@ -1879,12 +1954,15 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) ...@@ -1879,12 +1954,15 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
*/ */
static void execute_stmt(THD *thd, Prepared_statement *stmt, static void execute_stmt(THD *thd, Prepared_statement *stmt,
String *expanded_query) String *expanded_query, bool set_context)
{ {
DBUG_ENTER("execute_stmt"); DBUG_ENTER("execute_stmt");
thd->free_list= NULL; if (set_context)
thd->stmt_backup.set_statement(thd); {
thd->set_statement(stmt); thd->free_list= NULL;
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
}
reset_stmt_for_execute(stmt); reset_stmt_for_execute(stmt);
if (expanded_query->length() && if (expanded_query->length() &&
...@@ -2060,7 +2138,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -2060,7 +2138,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
get_longdata_error(0) get_longdata_error(0)
{ {
*last_error= '\0'; *last_error= '\0';
if (mysql_bin_log.is_open()) if (mysql_bin_log.is_open()) //psergey-todo: remove this!
{ {
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
...@@ -2080,6 +2158,28 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -2080,6 +2158,28 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
} }
} }
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
set_params_data= emb_insert_params_withlog;
#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() Prepared_statement::~Prepared_statement()
{ {
......
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