Commit 1e05e6cb authored by sergefp@mysql.com's avatar sergefp@mysql.com

Post review fixes for "SQL Syntax for Prepared Statements".

parent bec20d1f
......@@ -23,7 +23,7 @@ a b
deallocate prepare no_such_statement;
ERROR HY000: Unknown prepared statement handler (no_such_statement) given to DEALLOCATE PREPARE
execute stmt1;
ERROR HY000: Wrong arguments to mysql_execute
ERROR HY000: Wrong arguments to EXECUTE
prepare stmt2 from 'prepare nested_stmt from "select 1"';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '"select 1"' at line 1
prepare stmt2 from 'execute stmt1';
......
......@@ -38,7 +38,7 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
"%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored.
Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but
length value is ignored.
the length value is ignored.
*/
int my_error(int nr,myf MyFlags, ...)
......@@ -49,7 +49,7 @@ int my_error(int nr,myf MyFlags, ...)
reg2 char *endpos;
char * par;
char ebuff[ERRMSGSIZE+20];
int prec_chars;
int prec_chars; /* output precision */
my_bool prec_supplied;
DBUG_ENTER("my_error");
LINT_INIT(prec_chars); /* protected by prec_supplied */
......@@ -79,7 +79,8 @@ int my_error(int nr,myf MyFlags, ...)
/*
Skip size/precision flags to be compatible with printf.
The only size/precision flag supported is "%.*s".
"%.*u" and "%.*d" cause
If "%.*u" or "%.*d" are encountered, the precision number is read
from the variable argument list but its value is ignored.
*/
prec_supplied= 0;
if (*tpos== '.')
......@@ -101,45 +102,45 @@ int my_error(int nr,myf MyFlags, ...)
*tpos == '-')
tpos++;
if (*tpos == 'l') /* Skipp 'l' argument */
if (*tpos == 'l') /* Skip 'l' argument */
tpos++;
}
if (*tpos == 's') /* String parameter */
{
par = va_arg(ap, char *);
plen = (uint) strlen(par);
par= va_arg(ap, char *);
plen= (uint) strlen(par);
if (prec_supplied && prec_chars > 0)
plen= min((uint)prec_chars, plen);
if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */
{
memcpy(endpos,par, plen);
endpos += plen;
strmake(endpos, par, plen);
endpos+= plen;
tpos++;
olen+=plen-2;
olen+= plen-2;
continue;
}
}
else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */
{
register int iarg;
iarg = va_arg(ap, int);
iarg= va_arg(ap, int);
if (*tpos == 'd')
plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos);
else
plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos);
if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */
{
endpos+=plen;
endpos+= plen;
tpos++;
olen+=plen-2;
olen+= plen-2;
continue;
}
}
}
*endpos++='%'; /* % used as % or unknown code */
*endpos++= '%'; /* % used as % or unknown code */
}
*endpos='\0'; /* End of errmessage */
*endpos= '\0'; /* End of errmessage */
va_end(ap);
DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags));
}
......
......@@ -255,7 +255,7 @@ bool Item::get_time(TIME *ltime)
return 0;
}
CHARSET_INFO * Item::default_charset()
CHARSET_INFO *Item::default_charset()
{
return current_thd->variables.collation_connection;
}
......@@ -735,6 +735,70 @@ bool Item_param::set_longdata(const char *str, ulong length)
}
/*
Set parameter value from user variable value.
SYNOPSIS
set_from_user_var
thd Current thread
entry User variable structure (NULL means use NULL value)
RETURN
0 OK
1 Out of memort
*/
bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
{
DBUG_ENTER("Item_param::set_from_user_var");
if (entry && entry->value)
{
item_result_type= entry->type;
switch (entry->type)
{
case REAL_RESULT:
set_double(*(double*)entry->value);
break;
case INT_RESULT:
set_int(*(longlong*)entry->value, 21);
break;
case STRING_RESULT:
{
CHARSET_INFO *fromcs= entry->collation.collation;
CHARSET_INFO *tocs= thd->variables.collation_connection;
uint32 dummy_offset;
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.
*/
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.
*/
item_type= Item::STRING_ITEM;
item_result_type= STRING_RESULT;
if (set_str((const char *)entry->value, entry->length))
DBUG_RETURN(1);
}
break;
default:
DBUG_ASSERT(0);
set_null();
}
}
else
set_null();
DBUG_RETURN(0);
}
/*
Resets parameter after execution.
......@@ -767,8 +831,6 @@ void Item_param::reset()
int Item_param::save_in_field(Field *field, bool no_conversions)
{
DBUG_ASSERT(current_thd->command == COM_EXECUTE);
field->set_notnull();
switch (state) {
......
......@@ -489,6 +489,7 @@ public:
bool set_str(const char *str, ulong length);
bool set_longdata(const char *str, ulong length);
void set_time(TIME *tm, timestamp_type type, uint32 max_length_arg);
bool set_from_user_var(THD *thd, const user_var_entry *entry);
void reset();
/*
Assign placeholder value from bind data.
......
......@@ -2706,10 +2706,10 @@ void Item_func_get_user_var::fix_length_and_dec()
error= get_var_with_binlog(thd, name, &var_entry);
if (!var_entry)
null_value= 1;
else
if (var_entry)
collation.set(var_entry->collation);
else
null_value= 1;
if (error)
thd->fatal_error();
......
......@@ -348,6 +348,7 @@ inline THD *_current_thd(void)
#include "field.h" /* Field definitions */
#include "protocol.h"
#include "sql_udf.h"
class user_var_entry;
#include "item.h"
typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
/* sql_parse.cc */
......
......@@ -1983,12 +1983,9 @@ mysql_execute_command(THD *thd)
{
/* This is PREPARE stmt FROM @var. */
String str;
String *pstr;
CHARSET_INFO *to_cs= thd->variables.collation_connection;
CHARSET_INFO *from_cs;
const char *buf;
uint buf_len;
bool need_conversion;
LINT_INIT(from_cs); /* protected by need_conversion */
user_var_entry *entry;
uint32 unused;
/*
......@@ -2002,53 +1999,29 @@ mysql_execute_command(THD *thd)
lex->prepared_stmt_code.length))
&& entry->value)
{
switch (entry->type)
{
case REAL_RESULT:
str.set(*(double*)entry->value, NOT_FIXED_DEC, to_cs);
buf_len= str.length();
buf= str.ptr();
need_conversion= false;
break;
case INT_RESULT:
str.set(*(longlong*)entry->value, to_cs);
buf_len= str.length();
buf= str.ptr();
need_conversion= false;
break;
case STRING_RESULT:
buf_len= entry->length;
buf= entry->value;
from_cs = entry->collation.collation;
need_conversion= String::needs_conversion(entry->length, from_cs,
to_cs, &unused);
break;
default:
buf= "";
need_conversion= false;
buf_len= 0;
DBUG_ASSERT(0);
}
String *pstr;
my_bool is_var_null;
pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
DBUG_ASSERT(!is_var_null);
if (!pstr)
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_ASSERT(pstr == &str);
}
else
{
from_cs= &my_charset_bin;
str.set("NULL", 4, from_cs);
buf= str.ptr();
buf_len= str.length();
need_conversion= String::needs_conversion(str.length(), from_cs,
to_cs, &unused);
}
str.set("NULL", 4, &my_charset_latin1);
need_conversion=
String::needs_conversion(str.length(), str.charset(), to_cs, &unused);
query_len = need_conversion? (buf_len * to_cs->mbmaxlen) : buf_len;
query_len= need_conversion? (str.length() * to_cs->mbmaxlen) :
str.length();
if (!(query_str= alloc_root(&thd->mem_root, query_len+1)))
send_error(thd, ER_OUT_OF_RESOURCES);
if (need_conversion)
query_len= copy_and_convert(query_str, query_len, to_cs, buf, buf_len,
from_cs);
query_len= copy_and_convert(query_str, query_len, to_cs, str.ptr(),
str.length(), str.charset());
else
memcpy(query_str, buf, query_len);
memcpy(query_str, str.ptr(), str.length());
query_str[query_len]= 0;
}
else
......@@ -3559,7 +3532,6 @@ error:
*/
int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
{
if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
return 1;
......
......@@ -804,65 +804,20 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
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");
List_iterator<LEX_STRING> var_it(varnames);
for (Item_param **it= begin; it < end; ++it)
{
Item_param *param= *it;
varname= var_it++;
if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
(byte*) varname->str,
varname->length))
&& entry->value)
{
param->item_result_type= entry->type;
switch (entry->type)
{
case REAL_RESULT:
param->set_double(*(double*)entry->value);
break;
case INT_RESULT:
param->set_int(*(longlong*)entry->value, 21);
break;
case STRING_RESULT:
{
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))
varname->length);
if (param->set_from_user_var(stmt->thd, entry) ||
param->convert_str_value(stmt->thd))
DBUG_RETURN(1);
}
break;
default:
DBUG_ASSERT(0);
param->set_null();
}
}
else
param->set_null();
if (param->convert_str_value(stmt->thd))
DBUG_RETURN(1); /* out of memory */
}
DBUG_RETURN(0);
}
......@@ -902,52 +857,9 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
if (get_var_with_binlog(stmt->thd, *varname, &entry))
DBUG_RETURN(1);
DBUG_ASSERT(entry);
if (entry->value)
{
param->item_result_type= entry->type;
switch (entry->type)
{
case REAL_RESULT:
param->set_double(*(double*)entry->value);
break;
case INT_RESULT:
param->set_int(*(longlong*)entry->value, 21);
break;
case STRING_RESULT:
{
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))
if (param->set_from_user_var(stmt->thd, entry))
DBUG_RETURN(1);
}
break;
default:
DBUG_ASSERT(0);
param->set_null();
}
}
else
param->set_null();
/* Insert @'escaped-varname' instead of parameter in the query */
char *buf, *ptr;
str.length(0);
......@@ -1837,13 +1749,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
......@@ -1865,7 +1785,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
}
String expanded_query;
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count)
{
......@@ -1905,6 +1824,10 @@ set_params_data_err:
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
{
Prepared_statement *stmt;
/*
Query text for binary log, or empty string if the query is not put into
binary log.
*/
String expanded_query;
DBUG_ENTER("mysql_sql_stmt_execute");
......@@ -1918,20 +1841,19 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
send_error(thd);
DBUG_VOID_RETURN;
}
/* Item_param allows setting parameters in COM_EXECUTE only */
thd->command= COM_EXECUTE;
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,
if (stmt->set_params_from_vars(stmt,
thd->stmt_backup.lex->prepared_stmt_params,
&expanded_query))
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
send_error(thd);
}
execute_stmt(thd, stmt, &expanded_query);
......@@ -1980,10 +1902,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
/*
Free Items that were created during this execution of the PS by query
optimizer.
*/
/* 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);
......@@ -2138,24 +2057,6 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
get_longdata_error(0)
{
*last_error= '\0';
if (mysql_bin_log.is_open()) //psergey-todo: remove this!
{
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
}
}
void Prepared_statement::setup_set_params()
......
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