mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
* 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.
This commit is contained in:
@@ -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()
|
|||||||
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
|
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
else if (var_entry->used_query_id == thd->query_id)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
If this variable was already stored in user_var_events by this query
|
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
|
(because it's used in more than one place in the query), don't store
|
||||||
it.
|
it.
|
||||||
*/
|
*/
|
||||||
else if (var_entry->used_query_id == thd->query_id)
|
*out_entry= var_entry;
|
||||||
return;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint size;
|
uint size;
|
||||||
/*
|
/*
|
||||||
@@ -2672,10 +2687,33 @@ void Item_func_get_user_var::fix_length_and_dec()
|
|||||||
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:
|
||||||
|
*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();
|
thd->fatal_error();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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:
|
|||||||
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)
|
|||||||
*client_param->length :
|
*client_param->length :
|
||||||
client_param->buffer_length);
|
client_param->buffer_length);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
res= param->query_val_str(&str);
|
res= param->query_val_str(&str);
|
||||||
if (param->convert_str_value(thd))
|
if (param->convert_str_value(thd))
|
||||||
DBUG_RETURN(1); /* out of memory */
|
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,
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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");
|
||||||
|
if (set_context)
|
||||||
|
{
|
||||||
thd->free_list= NULL;
|
thd->free_list= NULL;
|
||||||
thd->stmt_backup.set_statement(thd);
|
thd->stmt_backup.set_statement(thd);
|
||||||
thd->set_statement(stmt);
|
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user