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 ...@@ -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';
......
...@@ -37,8 +37,8 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; ...@@ -37,8 +37,8 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
The following subset of printf format is supported: The following subset of printf format is supported:
"%[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 */
...@@ -76,10 +76,11 @@ int my_error(int nr,myf MyFlags, ...) ...@@ -76,10 +76,11 @@ int my_error(int nr,myf MyFlags, ...)
} }
else else
{ {
/* /*
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== '.')
...@@ -94,52 +95,52 @@ int my_error(int nr,myf MyFlags, ...) ...@@ -94,52 +95,52 @@ int my_error(int nr,myf MyFlags, ...)
prec_supplied= 1; prec_supplied= 1;
} }
} }
if (!prec_supplied) if (!prec_supplied)
{ {
while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' || while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' ||
*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) {
...@@ -1666,7 +1728,7 @@ bool Item::send(Protocol *protocol, String *buffer) ...@@ -1666,7 +1728,7 @@ bool Item::send(Protocol *protocol, String *buffer)
} }
case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY:
{ {
longlong nr; longlong nr;
nr= val_int(); nr= val_int();
if (!null_value) if (!null_value)
result= protocol->store_tiny(nr); result= protocol->store_tiny(nr);
......
...@@ -489,6 +489,7 @@ public: ...@@ -489,6 +489,7 @@ public:
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.
......
...@@ -2703,17 +2703,17 @@ void Item_func_get_user_var::fix_length_and_dec() ...@@ -2703,17 +2703,17 @@ void Item_func_get_user_var::fix_length_and_dec()
maybe_null=1; maybe_null=1;
decimals=NOT_FIXED_DEC; decimals=NOT_FIXED_DEC;
max_length=MAX_BLOB_WIDTH; max_length=MAX_BLOB_WIDTH;
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();
return; return;
} }
......
...@@ -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 */
......
...@@ -233,7 +233,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), ...@@ -233,7 +233,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
16); 16);
else else
bzero((char*) &user_var_events, sizeof(user_var_events)); bzero((char*) &user_var_events, sizeof(user_var_events));
/* Protocol */ /* Protocol */
protocol= &protocol_simple; // Default protocol protocol= &protocol_simple; // Default protocol
protocol_simple.init(this); protocol_simple.init(this);
......
...@@ -1976,86 +1976,59 @@ mysql_execute_command(THD *thd) ...@@ -1976,86 +1976,59 @@ mysql_execute_command(THD *thd)
break; break;
} }
case SQLCOM_PREPARE: case SQLCOM_PREPARE:
{ {
char *query_str; char *query_str;
uint query_len; uint query_len;
if (lex->prepared_stmt_code_is_varref) if (lex->prepared_stmt_code_is_varref)
{ {
/* 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;
/* /*
Convert @var contents to string in connection character set. Although Convert @var contents to string in connection character set. Although
it is known that int/real/NULL value cannot be a valid query we still it is known that int/real/NULL value cannot be a valid query we still
convert it for error messages to uniform. convert it for error messages to uniform.
*/ */
if ((entry= if ((entry=
(user_var_entry*)hash_search(&thd->user_vars, (user_var_entry*)hash_search(&thd->user_vars,
(byte*)lex->prepared_stmt_code.str, (byte*)lex->prepared_stmt_code.str,
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(); query_len= need_conversion? (str.length() * to_cs->mbmaxlen) :
need_conversion= String::needs_conversion(str.length(), from_cs, str.length();
to_cs, &unused);
}
query_len = need_conversion? (buf_len * to_cs->mbmaxlen) : buf_len;
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
{ {
query_str= lex->prepared_stmt_code.str; query_str= lex->prepared_stmt_code.str;
query_len= lex->prepared_stmt_code.length; query_len= lex->prepared_stmt_code.length;
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n", DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
lex->prepared_stmt_name.length, lex->prepared_stmt_name.length,
lex->prepared_stmt_name.str, lex->prepared_stmt_name.str,
query_len, query_str)); query_len, query_str));
...@@ -2068,7 +2041,7 @@ mysql_execute_command(THD *thd) ...@@ -2068,7 +2041,7 @@ mysql_execute_command(THD *thd)
} }
case SQLCOM_EXECUTE: case SQLCOM_EXECUTE:
{ {
DBUG_PRINT("info", ("EXECUTE: %.*s\n", DBUG_PRINT("info", ("EXECUTE: %.*s\n",
lex->prepared_stmt_name.length, lex->prepared_stmt_name.length,
lex->prepared_stmt_name.str)); lex->prepared_stmt_name.str));
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name); mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
...@@ -3559,7 +3532,6 @@ error: ...@@ -3559,7 +3532,6 @@ error:
*/ */
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;
......
This diff is collapsed.
...@@ -764,12 +764,12 @@ verb_clause: ...@@ -764,12 +764,12 @@ verb_clause:
| checksum | checksum
| commit | commit
| create | create
| deallocate | deallocate
| delete | delete
| describe | describe
| do | do
| drop | drop
| execute | execute
| flush | flush
| grant | grant
| handler | handler
...@@ -781,7 +781,7 @@ verb_clause: ...@@ -781,7 +781,7 @@ verb_clause:
| optimize | optimize
| keycache | keycache
| preload | preload
| prepare | prepare
| purge | purge
| rename | rename
| repair | repair
...@@ -803,7 +803,7 @@ verb_clause: ...@@ -803,7 +803,7 @@ verb_clause:
; ;
deallocate: deallocate:
DEALLOCATE_SYM PREPARE_SYM ident DEALLOCATE_SYM PREPARE_SYM ident
{ {
THD *thd=YYTHD; THD *thd=YYTHD;
LEX *lex= thd->lex; LEX *lex= thd->lex;
...@@ -845,7 +845,7 @@ prepare_src: ...@@ -845,7 +845,7 @@ prepare_src:
lex->prepared_stmt_code= $2; lex->prepared_stmt_code= $2;
lex->prepared_stmt_code_is_varref= true; lex->prepared_stmt_code_is_varref= true;
}; };
execute: execute:
EXECUTE_SYM ident EXECUTE_SYM ident
{ {
......
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