mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 18:20:07 +01:00
Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/dbdata/psergey/mysql-4.1-ps-merge sql/mysql_priv.h: Auto merged sql/mysqld.cc: Auto merged
This commit is contained in:
commit
ec1c9ef7b6
38 changed files with 1088 additions and 164 deletions
115
mysql-test/r/ps.result
Normal file
115
mysql-test/r/ps.result
Normal file
|
@ -0,0 +1,115 @@
|
|||
drop table if exists t1,t2;
|
||||
create table t1
|
||||
(
|
||||
a int primary key,
|
||||
b char(10)
|
||||
);
|
||||
insert into t1 values (1,'one');
|
||||
insert into t1 values (2,'two');
|
||||
insert into t1 values (3,'three');
|
||||
insert into t1 values (4,'four');
|
||||
set @a=2;
|
||||
prepare stmt1 from 'select * from t1 where a <= ?';
|
||||
execute stmt1 using @a;
|
||||
a b
|
||||
1 one
|
||||
2 two
|
||||
set @a=3;
|
||||
execute stmt1 using @a;
|
||||
a b
|
||||
1 one
|
||||
2 two
|
||||
3 three
|
||||
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 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';
|
||||
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 'stmt1' at line 1
|
||||
prepare stmt2 from 'deallocate prepare z';
|
||||
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 'z' at line 1
|
||||
prepare stmt3 from 'insert into t1 values (?,?)';
|
||||
set @arg1=5, @arg2='five';
|
||||
execute stmt3 using @arg1, @arg2;
|
||||
select * from t1 where a>3;
|
||||
a b
|
||||
4 four
|
||||
5 five
|
||||
prepare stmt4 from 'update t1 set a=? where b=?';
|
||||
set @arg1=55, @arg2='five';
|
||||
execute stmt4 using @arg1, @arg2;
|
||||
select * from t1 where a>3;
|
||||
a b
|
||||
4 four
|
||||
55 five
|
||||
prepare stmt4 from 'create table t2 (a int)';
|
||||
execute stmt4;
|
||||
prepare stmt4 from 'drop table t2';
|
||||
execute stmt4;
|
||||
execute stmt4;
|
||||
ERROR 42S02: Unknown table 't2'
|
||||
prepare stmt5 from 'select ? + a from t1';
|
||||
set @a=1;
|
||||
execute stmt5 using @a;
|
||||
? + a
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
56
|
||||
execute stmt5 using @no_such_var;
|
||||
? + a
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
set @nullvar=1;
|
||||
set @nullvar=NULL;
|
||||
execute stmt5 using @nullvar;
|
||||
? + a
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
set @nullvar2=NULL;
|
||||
execute stmt5 using @nullvar2;
|
||||
? + a
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
prepare stmt6 from 'select 1; select2';
|
||||
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 '; select2' at line 1
|
||||
prepare stmt6 from 'insert into t1 values (5,"five"); select2';
|
||||
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 '; select2' at line 1
|
||||
explain prepare stmt6 from 'insert into t1 values (5,"five"); select2';
|
||||
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 'from 'insert into t1 values (5,"five"); select2'' at line 1
|
||||
create table t2
|
||||
(
|
||||
a int
|
||||
);
|
||||
insert into t2 values (0);
|
||||
set @arg00=NULL ;
|
||||
prepare stmt1 from 'select 1 FROM t2 where a=?' ;
|
||||
execute stmt1 using @arg00 ;
|
||||
1
|
||||
prepare stmt1 from @nosuchvar;
|
||||
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 'NULL' at line 1
|
||||
set @ivar= 1234;
|
||||
prepare stmt1 from @ivar;
|
||||
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 '1234' at line 1
|
||||
set @fvar= 123.4567;
|
||||
prepare stmt1 from @fvar;
|
||||
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 '123.4567' at line 1
|
||||
set @str1 = 'select ?';
|
||||
set @str2 = convert(@str1 using ucs2);
|
||||
prepare stmt1 from @str2;
|
||||
execute stmt1 using @ivar;
|
||||
?
|
||||
1234
|
||||
drop table t1,t2;
|
28
mysql-test/r/rpl_ps.result
Normal file
28
mysql-test/r/rpl_ps.result
Normal file
|
@ -0,0 +1,28 @@
|
|||
stop slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
reset master;
|
||||
reset slave;
|
||||
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
start slave;
|
||||
drop table if exists t1;
|
||||
create table t1(n char(30));
|
||||
prepare stmt1 from 'insert into t1 values (?)';
|
||||
set @var1= "from-master-1";
|
||||
execute stmt1 using @var1;
|
||||
set @var1= "from-master-2-'',";
|
||||
execute stmt1 using @var1;
|
||||
select * from t1;
|
||||
n
|
||||
from-master-1
|
||||
from-master-2-'',
|
||||
set @var2= 'insert into t1 values (concat("from-var-", ?))';
|
||||
prepare stmt2 from @var2;
|
||||
set @var1='from-master-3';
|
||||
execute stmt2 using @var1;
|
||||
select * from t1;
|
||||
n
|
||||
from-master-1
|
||||
from-master-2-'',
|
||||
from-var-from-master-3
|
||||
drop table t1;
|
||||
stop slave;
|
119
mysql-test/t/ps.test
Normal file
119
mysql-test/t/ps.test
Normal file
|
@ -0,0 +1,119 @@
|
|||
#
|
||||
# SQL Syntax for Prepared Statements test
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t1,t2;
|
||||
--enable_warnings
|
||||
|
||||
create table t1
|
||||
(
|
||||
a int primary key,
|
||||
b char(10)
|
||||
);
|
||||
insert into t1 values (1,'one');
|
||||
insert into t1 values (2,'two');
|
||||
insert into t1 values (3,'three');
|
||||
insert into t1 values (4,'four');
|
||||
|
||||
# basic functionality
|
||||
set @a=2;
|
||||
prepare stmt1 from 'select * from t1 where a <= ?';
|
||||
execute stmt1 using @a;
|
||||
set @a=3;
|
||||
execute stmt1 using @a;
|
||||
|
||||
# non-existant statement
|
||||
--error 1243
|
||||
deallocate prepare no_such_statement;
|
||||
|
||||
--error 1210
|
||||
execute stmt1;
|
||||
|
||||
# Nesting ps commands is not allowed:
|
||||
--error 1064
|
||||
prepare stmt2 from 'prepare nested_stmt from "select 1"';
|
||||
|
||||
--error 1064
|
||||
prepare stmt2 from 'execute stmt1';
|
||||
|
||||
--error 1064
|
||||
prepare stmt2 from 'deallocate prepare z';
|
||||
|
||||
# PS insert
|
||||
prepare stmt3 from 'insert into t1 values (?,?)';
|
||||
set @arg1=5, @arg2='five';
|
||||
execute stmt3 using @arg1, @arg2;
|
||||
select * from t1 where a>3;
|
||||
|
||||
# PS update
|
||||
prepare stmt4 from 'update t1 set a=? where b=?';
|
||||
set @arg1=55, @arg2='five';
|
||||
execute stmt4 using @arg1, @arg2;
|
||||
select * from t1 where a>3;
|
||||
|
||||
# PS create/delete
|
||||
prepare stmt4 from 'create table t2 (a int)';
|
||||
execute stmt4;
|
||||
prepare stmt4 from 'drop table t2';
|
||||
execute stmt4;
|
||||
|
||||
# Do something that will cause error
|
||||
--error 1051
|
||||
execute stmt4;
|
||||
|
||||
# placeholders in result field names.
|
||||
prepare stmt5 from 'select ? + a from t1';
|
||||
set @a=1;
|
||||
execute stmt5 using @a;
|
||||
|
||||
execute stmt5 using @no_such_var;
|
||||
|
||||
set @nullvar=1;
|
||||
set @nullvar=NULL;
|
||||
execute stmt5 using @nullvar;
|
||||
|
||||
set @nullvar2=NULL;
|
||||
execute stmt5 using @nullvar2;
|
||||
|
||||
# Check that multiple SQL statements are disabled inside PREPARE
|
||||
--error 1064
|
||||
prepare stmt6 from 'select 1; select2';
|
||||
|
||||
--error 1064
|
||||
prepare stmt6 from 'insert into t1 values (5,"five"); select2';
|
||||
|
||||
# This shouldn't parse
|
||||
--error 1064
|
||||
explain prepare stmt6 from 'insert into t1 values (5,"five"); select2';
|
||||
|
||||
create table t2
|
||||
(
|
||||
a int
|
||||
);
|
||||
|
||||
insert into t2 values (0);
|
||||
|
||||
# parameter is NULL
|
||||
set @arg00=NULL ;
|
||||
prepare stmt1 from 'select 1 FROM t2 where a=?' ;
|
||||
execute stmt1 using @arg00 ;
|
||||
|
||||
# prepare using variables:
|
||||
--error 1064
|
||||
prepare stmt1 from @nosuchvar;
|
||||
|
||||
set @ivar= 1234;
|
||||
--error 1064
|
||||
prepare stmt1 from @ivar;
|
||||
|
||||
set @fvar= 123.4567;
|
||||
--error 1064
|
||||
prepare stmt1 from @fvar;
|
||||
|
||||
set @str1 = 'select ?';
|
||||
set @str2 = convert(@str1 using ucs2);
|
||||
prepare stmt1 from @str2;
|
||||
execute stmt1 using @ivar;
|
||||
|
||||
drop table t1,t2;
|
||||
|
43
mysql-test/t/rpl_ps.test
Normal file
43
mysql-test/t/rpl_ps.test
Normal file
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# Test of replicating user variables
|
||||
#
|
||||
source include/master-slave.inc;
|
||||
|
||||
#save_master_pos;
|
||||
#connection slave;
|
||||
#sync_with_master;
|
||||
#reset master;
|
||||
#connection master;
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
--enable_warnings
|
||||
|
||||
create table t1(n char(30));
|
||||
|
||||
prepare stmt1 from 'insert into t1 values (?)';
|
||||
set @var1= "from-master-1";
|
||||
execute stmt1 using @var1;
|
||||
set @var1= "from-master-2-'',";
|
||||
execute stmt1 using @var1;
|
||||
select * from t1;
|
||||
|
||||
set @var2= 'insert into t1 values (concat("from-var-", ?))';
|
||||
prepare stmt2 from @var2;
|
||||
set @var1='from-master-3';
|
||||
execute stmt2 using @var1;
|
||||
|
||||
save_master_pos;
|
||||
connection slave;
|
||||
sync_with_master;
|
||||
select * from t1;
|
||||
|
||||
connection master;
|
||||
|
||||
drop table t1;
|
||||
|
||||
save_master_pos;
|
||||
connection slave;
|
||||
sync_with_master;
|
||||
stop slave;
|
||||
|
|
@ -33,6 +33,12 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
|
|||
nr Errno
|
||||
MyFlags Flags
|
||||
... variable list
|
||||
NOTE
|
||||
The following subset of printf format is supported:
|
||||
"%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored.
|
||||
|
||||
Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but
|
||||
the length value is ignored.
|
||||
*/
|
||||
|
||||
int my_error(int nr,myf MyFlags, ...)
|
||||
|
@ -43,7 +49,10 @@ int my_error(int nr,myf MyFlags, ...)
|
|||
reg2 char *endpos;
|
||||
char * par;
|
||||
char ebuff[ERRMSGSIZE+20];
|
||||
int prec_chars; /* output precision */
|
||||
my_bool prec_supplied;
|
||||
DBUG_ENTER("my_error");
|
||||
LINT_INIT(prec_chars); /* protected by prec_supplied */
|
||||
|
||||
va_start(ap,MyFlags);
|
||||
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
|
||||
|
@ -59,7 +68,6 @@ int my_error(int nr,myf MyFlags, ...)
|
|||
if (tpos[0] != '%')
|
||||
{
|
||||
*endpos++= *tpos++; /* Copy ordinary char */
|
||||
olen++;
|
||||
continue;
|
||||
}
|
||||
if (*++tpos == '%') /* test if %% */
|
||||
|
@ -68,43 +76,71 @@ int my_error(int nr,myf MyFlags, ...)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Skip if max size is used (to be compatible with printf) */
|
||||
while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' || *tpos == '-')
|
||||
tpos++;
|
||||
if (*tpos == 'l') /* Skip 'l' argument */
|
||||
tpos++;
|
||||
/*
|
||||
Skip size/precision flags to be compatible with printf.
|
||||
The only size/precision flag supported is "%.*s".
|
||||
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== '.')
|
||||
{
|
||||
tpos++;
|
||||
olen--;
|
||||
if (*tpos == '*')
|
||||
{
|
||||
tpos++;
|
||||
olen--;
|
||||
prec_chars= va_arg(ap, int); /* get length parameter */
|
||||
prec_supplied= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prec_supplied)
|
||||
{
|
||||
while (my_isdigit(&my_charset_latin1, *tpos) || *tpos == '.' ||
|
||||
*tpos == '-')
|
||||
tpos++;
|
||||
|
||||
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 */
|
||||
{
|
||||
endpos=strmov(endpos,par);
|
||||
tpos++;
|
||||
olen+=plen-2;
|
||||
continue;
|
||||
strmake(endpos, par, plen);
|
||||
endpos+= plen;
|
||||
tpos++;
|
||||
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));
|
||||
}
|
||||
|
|
68
sql/item.cc
68
sql/item.cc
|
@ -255,7 +255,7 @@ bool Item::get_time(TIME *ltime)
|
|||
return 0;
|
||||
}
|
||||
|
||||
CHARSET_INFO * Item::default_charset() const
|
||||
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) {
|
||||
|
|
|
@ -245,7 +245,7 @@ public:
|
|||
virtual Item *real_item() { return this; }
|
||||
virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
|
||||
|
||||
CHARSET_INFO *default_charset() const;
|
||||
static CHARSET_INFO *default_charset();
|
||||
virtual CHARSET_INFO *compare_collation() { return NULL; }
|
||||
|
||||
virtual bool walk(Item_processor processor, byte *arg)
|
||||
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
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
|
||||
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;
|
||||
maybe_null=1;
|
||||
decimals=NOT_FIXED_DEC;
|
||||
max_length=MAX_BLOB_WIDTH;
|
||||
|
||||
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
|
||||
null_value= 1;
|
||||
else
|
||||
collation.set(var_entry->collation);
|
||||
user_var_entry *var_entry;
|
||||
var_entry= get_variable(&thd->user_vars, name, 0);
|
||||
|
||||
if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
|
||||
return;
|
||||
{
|
||||
*out_entry= var_entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)))
|
||||
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)
|
||||
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;
|
||||
/*
|
||||
|
@ -2671,11 +2686,34 @@ void Item_func_get_user_var::fix_length_and_dec()
|
|||
var_entry->used_query_id= thd->query_id;
|
||||
if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
*out_entry= var_entry;
|
||||
return 0;
|
||||
|
||||
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)
|
||||
collation.set(var_entry->collation);
|
||||
else
|
||||
null_value= 1;
|
||||
|
||||
if (error)
|
||||
thd->fatal_error();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ static SYMBOL symbols[] = {
|
|||
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
|
||||
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
|
||||
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
|
||||
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
|
||||
{ "DEC", SYM(DECIMAL_SYM)},
|
||||
{ "DECIMAL", SYM(DECIMAL_SYM)},
|
||||
{ "DEFAULT", SYM(DEFAULT)},
|
||||
|
@ -325,6 +326,7 @@ static SYMBOL symbols[] = {
|
|||
{ "POINT", SYM(POINT_SYM)},
|
||||
{ "POLYGON", SYM(POLYGON)},
|
||||
{ "PRECISION", SYM(PRECISION)},
|
||||
{ "PREPARE", SYM(PREPARE_SYM)},
|
||||
{ "PREV", SYM(PREV_SYM)},
|
||||
{ "PRIMARY", SYM(PRIMARY_SYM)},
|
||||
{ "PRIVILEGES", SYM(PRIVILEGES)},
|
||||
|
|
|
@ -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 */
|
||||
|
@ -639,8 +640,10 @@ int mysqld_show_column_types(THD *thd);
|
|||
int mysqld_help (THD *thd, const char *text);
|
||||
|
||||
/* sql_prepare.cc */
|
||||
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length);
|
||||
int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
|
||||
LEX_STRING *name=NULL);
|
||||
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
|
||||
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
|
||||
void mysql_stmt_free(THD *thd, char *packet);
|
||||
void mysql_stmt_reset(THD *thd, char *packet);
|
||||
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
|
||||
|
@ -1069,6 +1072,9 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
|
|||
LEX_STRING component);
|
||||
Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_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 */
|
||||
bool flush_error_log(void);
|
||||
|
||||
|
|
|
@ -5023,6 +5023,12 @@ struct show_var_st status_vars[]= {
|
|||
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
|
||||
{"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG},
|
||||
{"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG},
|
||||
{"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE),
|
||||
SHOW_LONG},
|
||||
{"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE),
|
||||
SHOW_LONG},
|
||||
{"Com_dealloc_sql", (char*) (com_stat+(uint)
|
||||
SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG},
|
||||
{"Connections", (char*) &thread_id, SHOW_LONG_CONST},
|
||||
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
|
||||
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
|
||||
|
|
|
@ -255,7 +255,7 @@ character-set=latin2
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -257,7 +257,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -251,7 +251,7 @@ character-set=latin7
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -258,7 +258,7 @@ character-set=latin1
|
|||
"Schlüssel- und Tabellenverweis passen nicht zusammen",
|
||||
"Operand solle %d Spalte(n) enthalten",
|
||||
"Unterabfrage lieferte mehr als einen Datensatz zurück",
|
||||
"Unbekannter Prepared-Statement-Handler (%ld) für %s angegeben",
|
||||
"Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben",
|
||||
"Die Hilfe-Datenbank ist beschädigt oder existiert nicht",
|
||||
"Zyklischer Verweis in Unterabfragen",
|
||||
"Spalte '%s' wird von %s nach %s umgewandelt",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=greek
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=latin2
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=ujis
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=euckr
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=latin1
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -250,7 +250,7 @@ character-set=latin2
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -247,7 +247,7 @@ character-set=latin1
|
|||
"Referência da chave e referência da tabela não coincidem",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subconsulta retorna mais que 1 registro",
|
||||
"Desconhecido manipulador de declaração preparado (%ld) determinado para %s",
|
||||
"Desconhecido manipulador de declaração preparado (%.*s) determinado para %s",
|
||||
"Banco de dado de ajuda corrupto ou não existente",
|
||||
"Referência cíclica em subconsultas",
|
||||
"Convertendo coluna '%s' de %s para %s",
|
||||
|
|
|
@ -250,7 +250,7 @@ character-set=latin2
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=koi8r
|
|||
"Key reference and table reference doesn't match",
|
||||
"Операнд должен содержать %d колонок",
|
||||
"Подзапрос возвращает более одной записи",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Циклическая ссылка на подзапрос",
|
||||
"Преобразование поля '%s' из %s в %s",
|
||||
|
|
|
@ -254,7 +254,7 @@ character-set=latin2
|
|||
"Key reference and table reference doesn't match",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returns more than 1 row",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"Cyclic reference on subqueries",
|
||||
"Converting column '%s' from %s to %s",
|
||||
|
|
|
@ -248,7 +248,7 @@ character-set=latin1
|
|||
"Referencia de llave y referencia de tabla no coinciden",
|
||||
"Operando debe tener %d columna(s)",
|
||||
"Subconsulta retorna mas que 1 línea",
|
||||
"Desconocido preparado comando handler (%ld) dado para %s",
|
||||
"Desconocido preparado comando handler (%.*s) dado para %s",
|
||||
"Base de datos Help está corrupto o no existe",
|
||||
"Cíclica referencia en subconsultas",
|
||||
"Convirtiendo columna '%s' de %s para %s",
|
||||
|
|
|
@ -246,7 +246,7 @@ character-set=latin1
|
|||
"Nyckelreferensen och tabellreferensen stämmer inte överens",
|
||||
"Operand should contain %d column(s)",
|
||||
"Subquery returnerade mer än 1 rad",
|
||||
"Okänd PREPARED STATEMENT id (%ld) var given till %s",
|
||||
"Okänd PREPARED STATEMENT id (%.*s) var given till %s",
|
||||
"Hjälpdatabasen finns inte eller är skadad",
|
||||
"Cyklisk referens i subqueries",
|
||||
"Konvertar kolumn '%s' från %s till %s",
|
||||
|
|
|
@ -251,7 +251,7 @@ character-set=koi8u
|
|||
"Key reference and table reference doesn't match",
|
||||
"ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×",
|
||||
"ð¦ÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ¦ÌØÛ ÎiÖ 1 ÚÁÐÉÓ",
|
||||
"Unknown prepared statement handler (%ld) given to %s",
|
||||
"Unknown prepared statement handler (%.*s) given to %s",
|
||||
"Help database is corrupt or does not exist",
|
||||
"ãÉË̦ÞÎÅ ÐÏÓÉÌÁÎÎÑ ÎÁ ЦÄÚÁÐÉÔ",
|
||||
"ðÅÒÅÔ×ÏÒÅÎÎÑ ÓÔÏ×ÂÃÁ '%s' Ú %s Õ %s",
|
||||
|
|
|
@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
|
|||
my_free((char*) entry,MYF(0));
|
||||
}
|
||||
|
||||
|
||||
bool key_part_spec::operator==(const key_part_spec& other) const
|
||||
{
|
||||
return length == other.length && !strcmp(field_name, other.field_name);
|
||||
|
@ -1302,6 +1301,7 @@ Statement::Statement(THD *thd)
|
|||
query_length(0),
|
||||
free_list(0)
|
||||
{
|
||||
name.str= NULL;
|
||||
init_sql_alloc(&mem_root,
|
||||
thd->variables.query_alloc_block_size,
|
||||
thd->variables.query_prealloc_size);
|
||||
|
@ -1385,17 +1385,52 @@ static void delete_statement_as_hash_key(void *key)
|
|||
delete (Statement *) key;
|
||||
}
|
||||
|
||||
static byte *get_stmt_name_hash_key(Statement *entry, uint *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length=(uint) entry->name.length;
|
||||
return (byte*) entry->name.str;
|
||||
}
|
||||
|
||||
C_MODE_END
|
||||
|
||||
Statement_map::Statement_map() :
|
||||
last_found_statement(0)
|
||||
{
|
||||
enum { START_HASH_SIZE = 16 };
|
||||
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||
enum
|
||||
{
|
||||
START_STMT_HASH_SIZE = 16,
|
||||
START_NAME_HASH_SIZE = 16
|
||||
};
|
||||
hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0,
|
||||
get_statement_id_as_hash_key,
|
||||
delete_statement_as_hash_key, MYF(0));
|
||||
hash_init(&names_hash, &my_charset_bin, START_NAME_HASH_SIZE, 0, 0,
|
||||
(hash_get_key) get_stmt_name_hash_key,
|
||||
NULL,MYF(0));
|
||||
}
|
||||
|
||||
int Statement_map::insert(Statement *statement)
|
||||
{
|
||||
int rc= my_hash_insert(&st_hash, (byte *) statement);
|
||||
if (rc == 0)
|
||||
last_found_statement= statement;
|
||||
if (statement->name.str)
|
||||
{
|
||||
/*
|
||||
If there is a statement with the same name, remove it. It is ok to
|
||||
remove old and fail to insert new one at the same time.
|
||||
*/
|
||||
Statement *old_stmt;
|
||||
if ((old_stmt= find_by_name(&statement->name)))
|
||||
erase(old_stmt);
|
||||
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
|
||||
hash_delete(&st_hash, (byte*)statement);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
bool select_dumpvar::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<Item_func_set_user_var> li(vars);
|
||||
|
|
|
@ -461,6 +461,7 @@ public:
|
|||
*/
|
||||
bool allow_sum_func;
|
||||
|
||||
LEX_STRING name; /* name for named prepared statements */
|
||||
LEX *lex; // parse tree descriptor
|
||||
/*
|
||||
Points to the query associated with this statement. It's const, but
|
||||
|
@ -527,8 +528,14 @@ public:
|
|||
|
||||
|
||||
/*
|
||||
Used to seek all existing statements in the connection
|
||||
Deletes all statements in destructor.
|
||||
Container for all statements created/used in a connection.
|
||||
Statements in Statement_map have unique Statement::id (guaranteed by id
|
||||
assignment in Statement::Statement)
|
||||
Non-empty statement names are unique too: attempt to insert a new statement
|
||||
with duplicate name causes older statement to be deleted
|
||||
|
||||
Statements are auto-deleted when they are removed from the map and when the
|
||||
map is deleted.
|
||||
*/
|
||||
|
||||
class Statement_map
|
||||
|
@ -536,34 +543,47 @@ class Statement_map
|
|||
public:
|
||||
Statement_map();
|
||||
|
||||
int insert(Statement *statement)
|
||||
int insert(Statement *statement);
|
||||
|
||||
Statement *find_by_name(LEX_STRING *name)
|
||||
{
|
||||
int rc= my_hash_insert(&st_hash, (byte *) statement);
|
||||
if (rc == 0)
|
||||
last_found_statement= statement;
|
||||
return rc;
|
||||
Statement *stmt;
|
||||
stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
|
||||
name->length);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
Statement *find(ulong id)
|
||||
{
|
||||
if (last_found_statement == 0 || id != last_found_statement->id)
|
||||
last_found_statement= (Statement *) hash_search(&st_hash, (byte *) &id,
|
||||
sizeof(id));
|
||||
{
|
||||
Statement *stmt;
|
||||
stmt= (Statement *) hash_search(&st_hash, (byte *) &id, sizeof(id));
|
||||
if (stmt->name.str)
|
||||
return NULL;
|
||||
last_found_statement= stmt;
|
||||
}
|
||||
return last_found_statement;
|
||||
}
|
||||
void erase(Statement *statement)
|
||||
{
|
||||
if (statement == last_found_statement)
|
||||
last_found_statement= 0;
|
||||
if (statement->name.str)
|
||||
{
|
||||
hash_delete(&names_hash, (byte *) statement);
|
||||
}
|
||||
hash_delete(&st_hash, (byte *) statement);
|
||||
}
|
||||
|
||||
~Statement_map()
|
||||
{
|
||||
hash_free(&st_hash);
|
||||
hash_free(&names_hash);
|
||||
}
|
||||
private:
|
||||
HASH st_hash;
|
||||
HASH names_hash;
|
||||
Statement *last_found_statement;
|
||||
};
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ enum enum_sql_command {
|
|||
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
|
||||
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
|
||||
|
||||
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
|
||||
/* This should be the last !!! */
|
||||
SQLCOM_END
|
||||
};
|
||||
|
@ -616,6 +617,17 @@ typedef struct st_lex
|
|||
bool derived_tables;
|
||||
bool safe_to_cache_query;
|
||||
ALTER_INFO alter_info;
|
||||
/* Prepared statements SQL syntax:*/
|
||||
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
|
||||
/*
|
||||
Prepared statement query text or name of variable that holds the
|
||||
prepared statement (in PREPARE ... queries)
|
||||
*/
|
||||
LEX_STRING prepared_stmt_code;
|
||||
/* If true, prepared_stmt_code is a name of variable that holds the query */
|
||||
bool prepared_stmt_code_is_varref;
|
||||
/* Names of user variables holding parameters (in EXECUTE) */
|
||||
List<LEX_STRING> prepared_stmt_params;
|
||||
st_lex() {}
|
||||
inline void uncacheable(uint8 cause)
|
||||
{
|
||||
|
|
|
@ -1424,7 +1424,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
}
|
||||
case COM_EXECUTE:
|
||||
{
|
||||
thd->free_list= NULL;
|
||||
mysql_stmt_execute(thd, packet, packet_length);
|
||||
break;
|
||||
}
|
||||
|
@ -1976,7 +1975,99 @@ mysql_execute_command(THD *thd)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SQLCOM_PREPARE:
|
||||
{
|
||||
char *query_str;
|
||||
uint query_len;
|
||||
if (lex->prepared_stmt_code_is_varref)
|
||||
{
|
||||
/* This is PREPARE stmt FROM @var. */
|
||||
String str;
|
||||
String *pstr;
|
||||
CHARSET_INFO *to_cs= thd->variables.collation_connection;
|
||||
bool need_conversion;
|
||||
user_var_entry *entry;
|
||||
uint32 unused;
|
||||
/*
|
||||
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
|
||||
convert it for error messages to uniform.
|
||||
*/
|
||||
if ((entry=
|
||||
(user_var_entry*)hash_search(&thd->user_vars,
|
||||
(byte*)lex->prepared_stmt_code.str,
|
||||
lex->prepared_stmt_code.length))
|
||||
&& entry->value)
|
||||
{
|
||||
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
|
||||
str.set("NULL", 4, &my_charset_latin1);
|
||||
need_conversion=
|
||||
String::needs_conversion(str.length(), str.charset(), to_cs, &unused);
|
||||
|
||||
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, str.ptr(),
|
||||
str.length(), str.charset());
|
||||
else
|
||||
memcpy(query_str, str.ptr(), str.length());
|
||||
query_str[query_len]= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
query_str= lex->prepared_stmt_code.str;
|
||||
query_len= lex->prepared_stmt_code.length;
|
||||
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str,
|
||||
query_len, query_str));
|
||||
}
|
||||
thd->command= COM_PREPARE;
|
||||
if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
|
||||
&lex->prepared_stmt_name))
|
||||
send_ok(thd, 0L, 0L, "Statement prepared");
|
||||
break;
|
||||
}
|
||||
case SQLCOM_EXECUTE:
|
||||
{
|
||||
DBUG_PRINT("info", ("EXECUTE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
|
||||
lex->prepared_stmt_params.empty();
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DEALLOCATE_PREPARE:
|
||||
{
|
||||
Statement* stmt;
|
||||
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
|
||||
{
|
||||
thd->stmt_map.erase(stmt);
|
||||
send_ok(thd);
|
||||
}
|
||||
else
|
||||
{
|
||||
res= -1;
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
|
||||
lex->prepared_stmt_name.length, lex->prepared_stmt_name.str,
|
||||
"DEALLOCATE PREPARE");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DO:
|
||||
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
|
||||
(res= open_and_lock_tables(thd,tables))))
|
||||
|
@ -3441,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;
|
||||
|
|
|
@ -91,16 +91,22 @@ public:
|
|||
bool get_longdata_error;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
|
||||
uchar *read_pos);
|
||||
uchar *read_pos, String *expanded_query);
|
||||
#else
|
||||
bool (*set_params_data)(Prepared_statement *st);
|
||||
bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
|
||||
#endif
|
||||
bool (*set_params_from_vars)(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *expanded_query);
|
||||
public:
|
||||
Prepared_statement(THD *thd_arg);
|
||||
virtual ~Prepared_statement();
|
||||
void setup_set_params();
|
||||
virtual Statement::Type type() const;
|
||||
};
|
||||
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
||||
String *expanded_query, bool set_context=false);
|
||||
|
||||
/******************************************************************************
|
||||
Implementation
|
||||
|
@ -129,7 +135,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where,
|
|||
|
||||
if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
|
||||
char llbuf[22];
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where);
|
||||
if (se == SEND_ERROR)
|
||||
send_error(thd);
|
||||
return 0;
|
||||
|
@ -594,19 +601,20 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
|
|||
*/
|
||||
|
||||
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
|
||||
uchar *read_pos, uchar *data_end)
|
||||
uchar *read_pos, uchar *data_end,
|
||||
String *query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
uint32 length= 0;
|
||||
|
||||
String str, query;
|
||||
String str;
|
||||
const String *res;
|
||||
|
||||
DBUG_ENTER("insert_params_withlog");
|
||||
|
||||
if (query.copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
|
@ -627,20 +635,18 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
|
|||
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);
|
||||
|
||||
length+= res->length()-1;
|
||||
}
|
||||
if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
|
||||
uchar *read_pos, uchar *data_end)
|
||||
uchar *read_pos, uchar *data_end,
|
||||
String *expanded_query)
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
|
@ -705,7 +711,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
|
|||
|
||||
#else
|
||||
|
||||
static bool emb_insert_params(Prepared_statement *stmt)
|
||||
static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **it= stmt->param_array;
|
||||
|
@ -738,20 +744,20 @@ static bool emb_insert_params(Prepared_statement *stmt)
|
|||
}
|
||||
|
||||
|
||||
static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
||||
static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
Item_param **it= stmt->param_array;
|
||||
Item_param **end= it + stmt->param_count;
|
||||
MYSQL_BIND *client_param= thd->client_params;
|
||||
|
||||
String str, query;
|
||||
String str;
|
||||
const String *res;
|
||||
uint32 length= 0;
|
||||
|
||||
DBUG_ENTER("emb_insert_params_withlog");
|
||||
|
||||
if (query.copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (; it < end; ++it, ++client_param)
|
||||
|
@ -770,24 +776,122 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
|||
*client_param->length :
|
||||
client_param->buffer_length);
|
||||
}
|
||||
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))
|
||||
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))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
length+= res->length()-1;
|
||||
}
|
||||
|
||||
if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
#endif /*!EMBEDDED_LIBRARY*/
|
||||
|
||||
|
||||
/*
|
||||
Set prepared statement parameters from user variables.
|
||||
SYNOPSIS
|
||||
insert_params_from_vars()
|
||||
stmt Statement
|
||||
varnames List of variables. Caller must ensure that number of variables
|
||||
in the list is equal to number of statement parameters
|
||||
query Ignored
|
||||
*/
|
||||
|
||||
static bool insert_params_from_vars(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *query __attribute__((unused)))
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
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");
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
varname= var_it++;
|
||||
entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
|
||||
(byte*) varname->str,
|
||||
varname->length);
|
||||
if (param->set_from_user_var(stmt->thd, entry) ||
|
||||
param->convert_str_value(stmt->thd))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Do the same as insert_params_from_vars but also construct query text for
|
||||
binary log.
|
||||
SYNOPSIS
|
||||
insert_params_from_vars()
|
||||
stmt Statement
|
||||
varnames List of variables. Caller must ensure that number of variables
|
||||
in the list is equal to number of statement parameters
|
||||
query The query with parameter markers replaced with their values
|
||||
*/
|
||||
|
||||
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames,
|
||||
String *query)
|
||||
{
|
||||
Item_param **begin= stmt->param_array;
|
||||
Item_param **end= begin + stmt->param_count;
|
||||
user_var_entry *entry;
|
||||
LEX_STRING *varname;
|
||||
DBUG_ENTER("insert_params_from_vars");
|
||||
|
||||
List_iterator<LEX_STRING> var_it(varnames);
|
||||
String str;
|
||||
const String *res;
|
||||
uint32 length= 0;
|
||||
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
for (Item_param **it= begin; it < end; ++it)
|
||||
{
|
||||
Item_param *param= *it;
|
||||
varname= var_it++;
|
||||
if (get_var_with_binlog(stmt->thd, *varname, &entry))
|
||||
DBUG_RETURN(1);
|
||||
DBUG_ASSERT(entry);
|
||||
|
||||
if (param->set_from_user_var(stmt->thd, entry))
|
||||
DBUG_RETURN(1);
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
Validate INSERT statement:
|
||||
|
||||
|
@ -984,7 +1088,7 @@ static int mysql_test_delete(Prepared_statement *stmt,
|
|||
*/
|
||||
|
||||
static int mysql_test_select(Prepared_statement *stmt,
|
||||
TABLE_LIST *tables)
|
||||
TABLE_LIST *tables, bool text_protocol)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
LEX *lex= stmt->lex;
|
||||
|
@ -1016,7 +1120,7 @@ static int mysql_test_select(Prepared_statement *stmt,
|
|||
|
||||
if (lex->describe)
|
||||
{
|
||||
if (send_prep_stmt(stmt, 0))
|
||||
if (!text_protocol && send_prep_stmt(stmt, 0))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
|
@ -1030,14 +1134,16 @@ static int mysql_test_select(Prepared_statement *stmt,
|
|||
goto err_prep;
|
||||
}
|
||||
|
||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
||||
if (!text_protocol)
|
||||
{
|
||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
||||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
|| net_flush(&thd->net)
|
||||
|| net_flush(&thd->net)
|
||||
#endif
|
||||
)
|
||||
goto err_prep;
|
||||
|
||||
)
|
||||
goto err_prep;
|
||||
}
|
||||
unit->cleanup();
|
||||
}
|
||||
thd->free_temporary_memory_pool_for_ps_preparing();
|
||||
|
@ -1315,7 +1421,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
|
|||
0 success
|
||||
1 error, sent to client
|
||||
*/
|
||||
static int send_prepare_results(Prepared_statement *stmt)
|
||||
static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
|
||||
{
|
||||
THD *thd= stmt->thd;
|
||||
LEX *lex= stmt->lex;
|
||||
|
@ -1352,7 +1458,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
|||
break;
|
||||
|
||||
case SQLCOM_SELECT:
|
||||
if ((res= mysql_test_select(stmt, tables)))
|
||||
if ((res= mysql_test_select(stmt, tables, text_protocol)))
|
||||
goto error;
|
||||
/* Statement and field info has already been sent */
|
||||
DBUG_RETURN(0);
|
||||
|
@ -1410,7 +1516,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
|||
goto error;
|
||||
}
|
||||
if (res == 0)
|
||||
DBUG_RETURN(send_prep_stmt(stmt, 0));
|
||||
DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0));
|
||||
error:
|
||||
if (res < 0)
|
||||
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
||||
|
@ -1451,20 +1557,36 @@ static bool init_param_array(Prepared_statement *stmt)
|
|||
|
||||
|
||||
/*
|
||||
Parse the query and send the total number of parameters
|
||||
and resultset metadata information back to client (if any),
|
||||
without executing the query i.e. without any log/disk
|
||||
writes. This will allow the queries to be re-executed
|
||||
without re-parsing during execute.
|
||||
Given a query string with parameter markers, create a Prepared Statement
|
||||
from it and send PS info back to the client.
|
||||
|
||||
SYNOPSIS
|
||||
mysql_stmt_prepare()
|
||||
packet query to be prepared
|
||||
packet_length query string length, including ignored trailing NULL or
|
||||
quote char.
|
||||
name NULL or statement name. For unnamed statements binary PS
|
||||
protocol is used, for named statements text protocol is
|
||||
used.
|
||||
RETURN
|
||||
0 OK, statement prepared successfully
|
||||
other Error
|
||||
|
||||
NOTES
|
||||
This function parses the query and sends the total number of parameters
|
||||
and resultset metadata information back to client (if any), without
|
||||
executing the query i.e. without any log/disk writes. This allows the
|
||||
queries to be re-executed without re-parsing during execute.
|
||||
|
||||
If parameter markers are found in the query, then store
|
||||
the information using Item_param along with maintaining a
|
||||
list in lex->param_array, so that a fast and direct
|
||||
retrieval can be made without going through all field
|
||||
items.
|
||||
If parameter markers are found in the query, then store the information
|
||||
using Item_param along with maintaining a list in lex->param_array, so
|
||||
that a fast and direct retrieval can be made without going through all
|
||||
field items.
|
||||
|
||||
*/
|
||||
|
||||
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||
int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
|
||||
LEX_STRING *name)
|
||||
{
|
||||
LEX *lex;
|
||||
Prepared_statement *stmt= new Prepared_statement(thd);
|
||||
|
@ -1476,14 +1598,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
|||
if (stmt == 0)
|
||||
{
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
stmt->name.length= name->length;
|
||||
if (!(stmt->name.str= memdup_root(&stmt->mem_root, (byte*)name->str,
|
||||
name->length)))
|
||||
{
|
||||
delete stmt;
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (thd->stmt_map.insert(stmt))
|
||||
{
|
||||
delete stmt;
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
|
@ -1500,7 +1634,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
|||
/* Statement map deletes statement on erase */
|
||||
thd->stmt_map.erase(stmt);
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
DBUG_VOID_RETURN;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
mysql_log.write(thd, COM_PREPARE, "%s", packet);
|
||||
|
@ -1512,7 +1646,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
|||
|
||||
error= yyparse((void *)thd) || thd->is_fatal_error ||
|
||||
init_param_array(stmt) ||
|
||||
send_prepare_results(stmt);
|
||||
send_prepare_results(stmt, test(name));
|
||||
|
||||
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
|
@ -1528,10 +1662,12 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
|||
{
|
||||
/* Statement map deletes statement on erase */
|
||||
thd->stmt_map.erase(stmt);
|
||||
stmt= NULL;
|
||||
/* error is sent inside yyparse/send_prepare_results */
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt->setup_set_params();
|
||||
SELECT_LEX *sl= stmt->lex->all_selects_list;
|
||||
/*
|
||||
Save WHERE clause pointers, because they may be changed during query
|
||||
|
@ -1541,8 +1677,10 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
|||
{
|
||||
sl->prep_where= sl->where;
|
||||
}
|
||||
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
DBUG_RETURN(!stmt);
|
||||
}
|
||||
|
||||
/* Reinit statement before execution */
|
||||
|
@ -1617,13 +1755,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
|
||||
|
@ -1631,7 +1777,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
DBUG_ENTER("mysql_stmt_execute");
|
||||
|
||||
packet+= 9; /* stmt_id + 5 bytes of flags */
|
||||
|
||||
|
||||
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute",
|
||||
SEND_ERROR)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -1645,15 +1791,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
reset_stmt_for_execute(stmt);
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (stmt->param_count)
|
||||
{
|
||||
uchar *null_array= (uchar *) packet;
|
||||
if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
|
||||
stmt->set_params(stmt, null_array, (uchar *) packet, packet_end))
|
||||
stmt->set_params(stmt, null_array, (uchar *) packet, packet_end,
|
||||
&expanded_query))
|
||||
goto set_params_data_err;
|
||||
}
|
||||
#else
|
||||
|
@ -1662,41 +1806,16 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
we set params, and also we don't need to parse packet.
|
||||
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;
|
||||
#endif
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
/*
|
||||
TODO:
|
||||
Also, have checks on basic executions such as mysql_insert(),
|
||||
mysql_delete(), mysql_update() and mysql_select() to not to
|
||||
have re-check on setup_* and other things ..
|
||||
*/
|
||||
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
|
||||
mysql_execute_command(thd);
|
||||
thd->lex->unit.cleanup();
|
||||
execute_stmt(thd, stmt, &expanded_query);
|
||||
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||
|
||||
cleanup_items(stmt->free_list);
|
||||
reset_stmt_params(stmt);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
/*
|
||||
Free Items that were created during this execution of the PS by query
|
||||
optimizer.
|
||||
*/
|
||||
free_items(thd->free_list);
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
set_params_data_err:
|
||||
reset_stmt_params(stmt);
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -1704,11 +1823,107 @@ set_params_data_err:
|
|||
|
||||
|
||||
/*
|
||||
Reset a prepared statement in case there was a recoverable error.
|
||||
Execute prepared statement using parameter values from
|
||||
lex->prepared_stmt_params and send result to the client using text protocol.
|
||||
*/
|
||||
|
||||
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");
|
||||
|
||||
if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length,
|
||||
stmt_name->str, "EXECUTE");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
|
||||
{
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
|
||||
send_error(thd);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
|
||||
send_error(thd);
|
||||
}
|
||||
execute_stmt(thd, stmt, &expanded_query);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Execute prepared statement.
|
||||
SYNOPSIS
|
||||
execute_stmt()
|
||||
thd Current thread
|
||||
stmt Statement to execute
|
||||
expanded_query If binary log is enabled, query string with parameter
|
||||
placeholders replaced with actual values. Otherwise empty
|
||||
string.
|
||||
NOTES
|
||||
Caller must set parameter values and thd::protocol.
|
||||
thd->free_list is assumed to be garbage.
|
||||
*/
|
||||
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt,
|
||||
String *expanded_query, bool set_context)
|
||||
{
|
||||
DBUG_ENTER("execute_stmt");
|
||||
if (set_context)
|
||||
{
|
||||
thd->free_list= NULL;
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
}
|
||||
reset_stmt_for_execute(stmt);
|
||||
|
||||
if (expanded_query->length() &&
|
||||
alloc_query(thd, (char *)expanded_query->ptr(),
|
||||
expanded_query->length()+1))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
mysql_execute_command(thd);
|
||||
thd->lex->unit.cleanup();
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||
|
||||
/* 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);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
thd->set_statement(&thd->stmt_backup);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Reset a prepared statement in case there was a recoverable error.
|
||||
SYNOPSIS
|
||||
mysql_stmt_reset()
|
||||
thd Thread handle
|
||||
packet Packet with stmt id
|
||||
thd Thread handle
|
||||
packet Packet with stmt id
|
||||
|
||||
DESCRIPTION
|
||||
This function resets statement to the state it was right after prepare.
|
||||
|
@ -1848,8 +2063,14 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
|||
get_longdata_error(0)
|
||||
{
|
||||
*last_error= '\0';
|
||||
if (mysql_bin_log.is_open())
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1857,14 +2078,16 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
|||
#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()
|
||||
{
|
||||
free_items(free_list);
|
||||
|
|
|
@ -433,6 +433,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
%token MEDIUMTEXT
|
||||
%token NUMERIC_SYM
|
||||
%token PRECISION
|
||||
%token PREPARE_SYM
|
||||
%token DEALLOCATE_SYM
|
||||
%token QUICK
|
||||
%token REAL
|
||||
%token SIGNED_SYM
|
||||
|
@ -725,6 +727,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
precision subselect_start opt_and charset
|
||||
subselect_end select_var_list select_var_list_init help opt_len
|
||||
opt_extended_describe
|
||||
prepare prepare_src execute deallocate
|
||||
END_OF_INPUT
|
||||
|
||||
%type <NONE>
|
||||
|
@ -761,10 +764,12 @@ verb_clause:
|
|||
| checksum
|
||||
| commit
|
||||
| create
|
||||
| deallocate
|
||||
| delete
|
||||
| describe
|
||||
| do
|
||||
| drop
|
||||
| execute
|
||||
| flush
|
||||
| grant
|
||||
| handler
|
||||
|
@ -776,6 +781,7 @@ verb_clause:
|
|||
| optimize
|
||||
| keycache
|
||||
| preload
|
||||
| prepare
|
||||
| purge
|
||||
| rename
|
||||
| repair
|
||||
|
@ -796,6 +802,86 @@ verb_clause:
|
|||
| use
|
||||
;
|
||||
|
||||
deallocate:
|
||||
DEALLOCATE_SYM PREPARE_SYM ident
|
||||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_PREPARE)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
|
||||
lex->prepared_stmt_name= $3;
|
||||
};
|
||||
|
||||
prepare:
|
||||
PREPARE_SYM ident FROM prepare_src
|
||||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_PREPARE)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_PREPARE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
};
|
||||
|
||||
prepare_src:
|
||||
TEXT_STRING_sys
|
||||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
lex->prepared_stmt_code= $1;
|
||||
lex->prepared_stmt_code_is_varref= false;
|
||||
}
|
||||
| '@' ident_or_text
|
||||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
lex->prepared_stmt_code= $2;
|
||||
lex->prepared_stmt_code_is_varref= true;
|
||||
};
|
||||
|
||||
execute:
|
||||
EXECUTE_SYM ident
|
||||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_PREPARE)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_EXECUTE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
}
|
||||
execute_using
|
||||
{}
|
||||
;
|
||||
|
||||
execute_using:
|
||||
/* nothing */
|
||||
| USING execute_var_list
|
||||
;
|
||||
|
||||
execute_var_list:
|
||||
execute_var_list ',' execute_var_ident
|
||||
| execute_var_ident
|
||||
;
|
||||
|
||||
execute_var_ident: '@' ident_or_text
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
|
||||
if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
|
||||
YYABORT;
|
||||
}
|
||||
;
|
||||
|
||||
/* help */
|
||||
|
||||
help:
|
||||
|
@ -4912,6 +4998,7 @@ keyword:
|
|||
| DATETIME {}
|
||||
| DATE_SYM {}
|
||||
| DAY_SYM {}
|
||||
| DEALLOCATE_SYM {}
|
||||
| DELAY_KEY_WRITE_SYM {}
|
||||
| DES_KEY_FILE {}
|
||||
| DIRECTORY_SYM {}
|
||||
|
@ -5010,6 +5097,7 @@ keyword:
|
|||
| PASSWORD {}
|
||||
| POINT_SYM {}
|
||||
| POLYGON {}
|
||||
| PREPARE_SYM {}
|
||||
| PREV_SYM {}
|
||||
| PROCESS {}
|
||||
| PROCESSLIST_SYM {}
|
||||
|
|
Loading…
Add table
Reference in a new issue