Commit 1d4ee7f8 authored by unknown's avatar unknown

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


mysql-test/r/ps.result:
  Better error message
mysys/my_error.c:
  Comments added
sql/item.cc:
  Moved a chunk of code from sql_prepare.cc to Item_param::set_from_user_var
sql/item.h:
  Moved a chunk of code from sql_prepare.cc to Item_param::set_from_user_var
sql/item_func.cc:
  Code cleanup
sql/mysql_priv.h:
  Code cleanup
sql/sql_class.cc:
  Code cleanup
sql/sql_parse.cc:
  use user_var_entry::val_str in PREPARE stmt FROM @var.
sql/sql_prepare.cc:
  Post-review fixes and code cleanup.
sql/sql_yacc.yy:
  Coding style fixes
parent e9c4cea9
...@@ -23,7 +23,7 @@ a b ...@@ -23,7 +23,7 @@ a b
deallocate prepare no_such_statement; deallocate prepare no_such_statement;
ERROR HY000: Unknown prepared statement handler (no_such_statement) given to DEALLOCATE PREPARE ERROR HY000: Unknown prepared statement handler (no_such_statement) given to DEALLOCATE PREPARE
execute stmt1; execute stmt1;
ERROR HY000: Wrong arguments to mysql_execute ERROR HY000: Wrong arguments to EXECUTE
prepare stmt2 from 'prepare nested_stmt from "select 1"'; 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 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'; prepare stmt2 from 'execute stmt1';
......
...@@ -38,7 +38,7 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; ...@@ -38,7 +38,7 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
"%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored. "%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored.
Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but 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, ...) int my_error(int nr,myf MyFlags, ...)
...@@ -49,7 +49,7 @@ int my_error(int nr,myf MyFlags, ...) ...@@ -49,7 +49,7 @@ int my_error(int nr,myf MyFlags, ...)
reg2 char *endpos; reg2 char *endpos;
char * par; char * par;
char ebuff[ERRMSGSIZE+20]; char ebuff[ERRMSGSIZE+20];
int prec_chars; int prec_chars; /* output precision */
my_bool prec_supplied; my_bool prec_supplied;
DBUG_ENTER("my_error"); DBUG_ENTER("my_error");
LINT_INIT(prec_chars); /* protected by prec_supplied */ LINT_INIT(prec_chars); /* protected by prec_supplied */
...@@ -79,7 +79,8 @@ int my_error(int nr,myf MyFlags, ...) ...@@ -79,7 +79,8 @@ int my_error(int nr,myf MyFlags, ...)
/* /*
Skip size/precision flags to be compatible with printf. Skip size/precision flags to be compatible with printf.
The only size/precision flag supported is "%.*s". 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; prec_supplied= 0;
if (*tpos== '.') if (*tpos== '.')
...@@ -101,45 +102,45 @@ int my_error(int nr,myf MyFlags, ...) ...@@ -101,45 +102,45 @@ int my_error(int nr,myf MyFlags, ...)
*tpos == '-') *tpos == '-')
tpos++; tpos++;
if (*tpos == 'l') /* Skipp 'l' argument */ if (*tpos == 'l') /* Skip 'l' argument */
tpos++; tpos++;
} }
if (*tpos == 's') /* String parameter */ if (*tpos == 's') /* String parameter */
{ {
par = va_arg(ap, char *); par= va_arg(ap, char *);
plen = (uint) strlen(par); plen= (uint) strlen(par);
if (prec_supplied && prec_chars > 0) if (prec_supplied && prec_chars > 0)
plen= min((uint)prec_chars, plen); plen= min((uint)prec_chars, plen);
if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */ if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */
{ {
memcpy(endpos,par, plen); strmake(endpos, par, plen);
endpos += plen; endpos+= plen;
tpos++; tpos++;
olen+=plen-2; olen+= plen-2;
continue; continue;
} }
} }
else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */ else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */
{ {
register int iarg; register int iarg;
iarg = va_arg(ap, int); iarg= va_arg(ap, int);
if (*tpos == 'd') if (*tpos == 'd')
plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos); plen= (uint) (int10_to_str((long) iarg, endpos, -10) - endpos);
else else
plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos); plen= (uint) (int10_to_str((long) (uint) iarg, endpos, 10) - endpos);
if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */ if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */
{ {
endpos+=plen; endpos+= plen;
tpos++; tpos++;
olen+=plen-2; olen+= plen-2;
continue; 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); va_end(ap);
DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags));
} }
......
...@@ -255,7 +255,7 @@ bool Item::get_time(TIME *ltime) ...@@ -255,7 +255,7 @@ bool Item::get_time(TIME *ltime)
return 0; return 0;
} }
CHARSET_INFO * Item::default_charset() CHARSET_INFO *Item::default_charset()
{ {
return current_thd->variables.collation_connection; return current_thd->variables.collation_connection;
} }
...@@ -735,6 +735,70 @@ bool Item_param::set_longdata(const char *str, ulong length) ...@@ -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. Resets parameter after execution.
...@@ -767,8 +831,6 @@ void Item_param::reset() ...@@ -767,8 +831,6 @@ void Item_param::reset()
int Item_param::save_in_field(Field *field, bool no_conversions) int Item_param::save_in_field(Field *field, bool no_conversions)
{ {
DBUG_ASSERT(current_thd->command == COM_EXECUTE);
field->set_notnull(); field->set_notnull();
switch (state) { switch (state) {
......
...@@ -489,6 +489,7 @@ class Item_param :public Item ...@@ -489,6 +489,7 @@ class Item_param :public Item
bool set_str(const char *str, ulong length); bool set_str(const char *str, ulong length);
bool set_longdata(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); 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(); void reset();
/* /*
Assign placeholder value from bind data. Assign placeholder value from bind data.
......
...@@ -2706,10 +2706,10 @@ void Item_func_get_user_var::fix_length_and_dec() ...@@ -2706,10 +2706,10 @@ void Item_func_get_user_var::fix_length_and_dec()
error= get_var_with_binlog(thd, name, &var_entry); error= get_var_with_binlog(thd, name, &var_entry);
if (!var_entry) if (var_entry)
null_value= 1;
else
collation.set(var_entry->collation); collation.set(var_entry->collation);
else
null_value= 1;
if (error) if (error)
thd->fatal_error(); thd->fatal_error();
......
...@@ -348,6 +348,7 @@ inline THD *_current_thd(void) ...@@ -348,6 +348,7 @@ inline THD *_current_thd(void)
#include "field.h" /* Field definitions */ #include "field.h" /* Field definitions */
#include "protocol.h" #include "protocol.h"
#include "sql_udf.h" #include "sql_udf.h"
class user_var_entry;
#include "item.h" #include "item.h"
typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
/* sql_parse.cc */ /* sql_parse.cc */
......
...@@ -1983,12 +1983,9 @@ mysql_execute_command(THD *thd) ...@@ -1983,12 +1983,9 @@ mysql_execute_command(THD *thd)
{ {
/* This is PREPARE stmt FROM @var. */ /* This is PREPARE stmt FROM @var. */
String str; String str;
String *pstr;
CHARSET_INFO *to_cs= thd->variables.collation_connection; CHARSET_INFO *to_cs= thd->variables.collation_connection;
CHARSET_INFO *from_cs;
const char *buf;
uint buf_len;
bool need_conversion; bool need_conversion;
LINT_INIT(from_cs); /* protected by need_conversion */
user_var_entry *entry; user_var_entry *entry;
uint32 unused; uint32 unused;
/* /*
...@@ -2002,53 +1999,29 @@ mysql_execute_command(THD *thd) ...@@ -2002,53 +1999,29 @@ mysql_execute_command(THD *thd)
lex->prepared_stmt_code.length)) lex->prepared_stmt_code.length))
&& entry->value) && entry->value)
{ {
switch (entry->type) String *pstr;
{ my_bool is_var_null;
case REAL_RESULT: pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
str.set(*(double*)entry->value, NOT_FIXED_DEC, to_cs); DBUG_ASSERT(!is_var_null);
buf_len= str.length(); if (!pstr)
buf= str.ptr(); send_error(thd, ER_OUT_OF_RESOURCES);
need_conversion= false; DBUG_ASSERT(pstr == &str);
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);
}
} }
else else
{ str.set("NULL", 4, &my_charset_latin1);
from_cs= &my_charset_bin; need_conversion=
str.set("NULL", 4, from_cs); String::needs_conversion(str.length(), str.charset(), to_cs, &unused);
buf= str.ptr();
buf_len= str.length();
need_conversion= String::needs_conversion(str.length(), from_cs,
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))) if (!(query_str= alloc_root(&thd->mem_root, query_len+1)))
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
if (need_conversion) if (need_conversion)
query_len= copy_and_convert(query_str, query_len, to_cs, buf, buf_len, query_len= copy_and_convert(query_str, query_len, to_cs, str.ptr(),
from_cs); str.length(), str.charset());
else else
memcpy(query_str, buf, query_len); memcpy(query_str, str.ptr(), str.length());
query_str[query_len]= 0; query_str[query_len]= 0;
} }
else else
...@@ -3559,7 +3532,6 @@ purposes internal to the MySQL server", MYF(0)); ...@@ -3559,7 +3532,6 @@ purposes internal to the MySQL server", MYF(0));
*/ */
int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables) int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
{ {
if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
return 1; return 1;
......
...@@ -804,65 +804,20 @@ static bool insert_params_from_vars(Prepared_statement *stmt, ...@@ -804,65 +804,20 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
Item_param **end= begin + stmt->param_count; Item_param **end= begin + stmt->param_count;
user_var_entry *entry; user_var_entry *entry;
LEX_STRING *varname; LEX_STRING *varname;
List_iterator<LEX_STRING> var_it(varnames);
DBUG_ENTER("insert_params_from_vars"); DBUG_ENTER("insert_params_from_vars");
List_iterator<LEX_STRING> var_it(varnames);
for (Item_param **it= begin; it < end; ++it) for (Item_param **it= begin; it < end; ++it)
{ {
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, entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
(byte*) varname->str, (byte*) varname->str,
varname->length)) varname->length);
&& entry->value) if (param->set_from_user_var(stmt->thd, entry) ||
{ param->convert_str_value(stmt->thd))
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))
DBUG_RETURN(1); 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); DBUG_RETURN(0);
} }
...@@ -902,52 +857,9 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -902,52 +857,9 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
if (get_var_with_binlog(stmt->thd, *varname, &entry)) if (get_var_with_binlog(stmt->thd, *varname, &entry))
DBUG_RETURN(1); DBUG_RETURN(1);
DBUG_ASSERT(entry); 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); DBUG_RETURN(1);
}
break;
default:
DBUG_ASSERT(0);
param->set_null();
}
}
else
param->set_null();
/* Insert @'escaped-varname' instead of parameter in the query */ /* Insert @'escaped-varname' instead of parameter in the query */
char *buf, *ptr; char *buf, *ptr;
str.length(0); str.length(0);
...@@ -1837,13 +1749,21 @@ static void reset_stmt_params(Prepared_statement *stmt) ...@@ -1837,13 +1749,21 @@ static void reset_stmt_params(Prepared_statement *stmt)
Executes previously prepared query. Executes previously prepared query.
If there is any parameters, then replace markers with the data supplied If there is any parameters, then replace markers with the data supplied
from client, and then execute the query. from client, and then execute the query.
SYNOPSYS SYNOPSIS
mysql_stmt_execute() 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) void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
{ {
ulong stmt_id= uint4korr(packet); 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 #ifndef EMBEDDED_LIBRARY
uchar *packet_end= (uchar *) packet + packet_length - 1; uchar *packet_end= (uchar *) packet + packet_length - 1;
#endif #endif
...@@ -1865,7 +1785,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1865,7 +1785,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
String expanded_query;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (stmt->param_count) if (stmt->param_count)
{ {
...@@ -1905,6 +1824,10 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1905,6 +1824,10 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
{ {
Prepared_statement *stmt; Prepared_statement *stmt;
/*
Query text for binary log, or empty string if the query is not put into
binary log.
*/
String expanded_query; String expanded_query;
DBUG_ENTER("mysql_sql_stmt_execute"); DBUG_ENTER("mysql_sql_stmt_execute");
...@@ -1918,20 +1841,19 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) ...@@ -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) 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); send_error(thd);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/* Item_param allows setting parameters in COM_EXECUTE only */
thd->command= COM_EXECUTE;
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);
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)) &expanded_query))
{ {
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
send_error(thd); send_error(thd);
} }
execute_stmt(thd, stmt, &expanded_query); execute_stmt(thd, stmt, &expanded_query);
...@@ -1980,10 +1902,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, ...@@ -1980,10 +1902,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR); my_pthread_setprio(pthread_self(), WAIT_PRIOR);
/* /* Free Items that were created during this execution of the PS. */
Free Items that were created during this execution of the PS by query
optimizer.
*/
free_items(thd->free_list); free_items(thd->free_list);
cleanup_items(stmt->free_list); cleanup_items(stmt->free_list);
reset_stmt_params(stmt); reset_stmt_params(stmt);
...@@ -2138,24 +2057,6 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -2138,24 +2057,6 @@ 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()) //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() 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