mirror of
https://github.com/MariaDB/server.git
synced 2025-01-30 18:41:56 +01:00
Merged
sql/item.cc: Auto merged sql/item.h: Auto merged sql/mysql_priv.h: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Manual merge sql/sql_prepare.cc: Manual merge
This commit is contained in:
commit
7e114f3cd1
13 changed files with 582 additions and 55 deletions
86
mysql-test/r/ps.result
Normal file
86
mysql-test/r/ps.result
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
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: Undefined prepared statement
|
||||||
|
execute stmt1;
|
||||||
|
ERROR HY000: Wrong arguments to mysql_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
|
||||||
|
drop table t1;
|
79
mysql-test/t/ps.test
Normal file
79
mysql-test/t/ps.test
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#
|
||||||
|
# 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;
|
||||||
|
|
||||||
|
drop table t1;
|
||||||
|
|
|
@ -635,16 +635,21 @@ void Item_param::set_double(double value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Item_param::set_value(const char *str, uint length)
|
void Item_param::set_value(const char *str, uint length, CHARSET_INFO *ci)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("Item_param::set_value");
|
DBUG_ENTER("Item_param::set_value");
|
||||||
str_value.copy(str,length,default_charset());
|
str_value.copy(str,length,ci);
|
||||||
item_type= STRING_ITEM;
|
item_type= STRING_ITEM;
|
||||||
value_is_set= 1;
|
value_is_set= 1;
|
||||||
DBUG_PRINT("info", ("string: %s", str_value.ptr()));
|
DBUG_PRINT("info", ("string: %s", str_value.ptr()));
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Item_param::set_value(const char *str, uint length)
|
||||||
|
{
|
||||||
|
set_value(str, length, default_charset());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Item_param::set_time(TIME *tm, timestamp_type type)
|
void Item_param::set_time(TIME *tm, timestamp_type type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -413,6 +413,7 @@ public:
|
||||||
void set_int(longlong i);
|
void set_int(longlong i);
|
||||||
void set_double(double i);
|
void set_double(double i);
|
||||||
void set_value(const char *str, uint length);
|
void set_value(const char *str, uint length);
|
||||||
|
void set_value(const char *str, uint length, CHARSET_INFO *ci);
|
||||||
void set_long_str(const char *str, ulong length);
|
void set_long_str(const char *str, ulong length);
|
||||||
void set_long_binary(const char *str, ulong length);
|
void set_long_binary(const char *str, ulong length);
|
||||||
void set_longdata(const char *str, ulong length);
|
void set_longdata(const char *str, ulong length);
|
||||||
|
|
|
@ -133,6 +133,7 @@ static SYMBOL symbols[] = {
|
||||||
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
|
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
|
||||||
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
|
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
|
||||||
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
|
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
|
||||||
|
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
|
||||||
{ "DEC", SYM(DECIMAL_SYM)},
|
{ "DEC", SYM(DECIMAL_SYM)},
|
||||||
{ "DECIMAL", SYM(DECIMAL_SYM)},
|
{ "DECIMAL", SYM(DECIMAL_SYM)},
|
||||||
{ "DEFAULT", SYM(DEFAULT)},
|
{ "DEFAULT", SYM(DEFAULT)},
|
||||||
|
@ -322,6 +323,7 @@ static SYMBOL symbols[] = {
|
||||||
{ "POINT", SYM(POINT_SYM)},
|
{ "POINT", SYM(POINT_SYM)},
|
||||||
{ "POLYGON", SYM(POLYGON)},
|
{ "POLYGON", SYM(POLYGON)},
|
||||||
{ "PRECISION", SYM(PRECISION)},
|
{ "PRECISION", SYM(PRECISION)},
|
||||||
|
{ "PREPARE", SYM(PREPARE_SYM)},
|
||||||
{ "PREV", SYM(PREV_SYM)},
|
{ "PREV", SYM(PREV_SYM)},
|
||||||
{ "PRIMARY", SYM(PRIMARY_SYM)},
|
{ "PRIMARY", SYM(PRIMARY_SYM)},
|
||||||
{ "PRIVILEGES", SYM(PRIVILEGES)},
|
{ "PRIVILEGES", SYM(PRIVILEGES)},
|
||||||
|
|
|
@ -634,8 +634,10 @@ int mysqld_show_column_types(THD *thd);
|
||||||
int mysqld_help (THD *thd, const char *text);
|
int mysqld_help (THD *thd, const char *text);
|
||||||
|
|
||||||
/* sql_prepare.cc */
|
/* 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_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_free(THD *thd, char *packet);
|
||||||
void mysql_stmt_reset(THD *thd, char *packet);
|
void mysql_stmt_reset(THD *thd, char *packet);
|
||||||
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
|
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
|
||||||
|
|
|
@ -4809,6 +4809,9 @@ struct show_var_st status_vars[]= {
|
||||||
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
|
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
|
||||||
{"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),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_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},
|
{"Connections", (char*) &thread_id, SHOW_LONG_CONST},
|
||||||
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
|
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
|
||||||
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
|
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
|
||||||
|
|
|
@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
|
||||||
my_free((char*) entry,MYF(0));
|
my_free((char*) entry,MYF(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** Thread specific functions
|
** Thread specific functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -1199,6 +1198,7 @@ Statement::Statement(THD *thd)
|
||||||
query_length(0),
|
query_length(0),
|
||||||
free_list(0)
|
free_list(0)
|
||||||
{
|
{
|
||||||
|
name.str= NULL;
|
||||||
init_sql_alloc(&mem_root,
|
init_sql_alloc(&mem_root,
|
||||||
thd->variables.query_alloc_block_size,
|
thd->variables.query_alloc_block_size,
|
||||||
thd->variables.query_prealloc_size);
|
thd->variables.query_prealloc_size);
|
||||||
|
@ -1282,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key)
|
||||||
delete (Statement *) key;
|
delete (Statement *) key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
C_MODE_END
|
||||||
|
|
||||||
Statement_map::Statement_map() :
|
Statement_map::Statement_map() :
|
||||||
last_found_statement(0)
|
last_found_statement(0)
|
||||||
{
|
{
|
||||||
enum { START_HASH_SIZE = 16 };
|
enum
|
||||||
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
{
|
||||||
|
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,
|
get_statement_id_as_hash_key,
|
||||||
delete_statement_as_hash_key, MYF(0));
|
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)
|
bool select_dumpvar::send_data(List<Item> &items)
|
||||||
{
|
{
|
||||||
List_iterator_fast<Item_func_set_user_var> li(vars);
|
List_iterator_fast<Item_func_set_user_var> li(vars);
|
||||||
|
|
|
@ -456,6 +456,7 @@ public:
|
||||||
*/
|
*/
|
||||||
bool allow_sum_func;
|
bool allow_sum_func;
|
||||||
|
|
||||||
|
LEX_STRING name; /* name for named prepared statements */
|
||||||
LEX *lex; // parse tree descriptor
|
LEX *lex; // parse tree descriptor
|
||||||
/*
|
/*
|
||||||
Points to the query associated with this statement. It's const, but
|
Points to the query associated with this statement. It's const, but
|
||||||
|
@ -522,8 +523,14 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Used to seek all existing statements in the connection
|
Container for all statements created/used in a connection.
|
||||||
Deletes all statements in destructor.
|
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
|
class Statement_map
|
||||||
|
@ -531,12 +538,14 @@ class Statement_map
|
||||||
public:
|
public:
|
||||||
Statement_map();
|
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);
|
Statement *stmt;
|
||||||
if (rc == 0)
|
stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
|
||||||
last_found_statement= statement;
|
name->length);
|
||||||
return rc;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement *find(ulong id)
|
Statement *find(ulong id)
|
||||||
|
@ -550,15 +559,21 @@ public:
|
||||||
{
|
{
|
||||||
if (statement == last_found_statement)
|
if (statement == last_found_statement)
|
||||||
last_found_statement= 0;
|
last_found_statement= 0;
|
||||||
|
if (statement->name.str)
|
||||||
|
{
|
||||||
|
hash_delete(&names_hash, (byte *) statement);
|
||||||
|
}
|
||||||
hash_delete(&st_hash, (byte *) statement);
|
hash_delete(&st_hash, (byte *) statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Statement_map()
|
~Statement_map()
|
||||||
{
|
{
|
||||||
hash_free(&st_hash);
|
hash_free(&st_hash);
|
||||||
|
hash_free(&names_hash);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
HASH st_hash;
|
HASH st_hash;
|
||||||
|
HASH names_hash;
|
||||||
Statement *last_found_statement;
|
Statement *last_found_statement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ enum enum_sql_command {
|
||||||
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
|
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
|
||||||
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
|
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
|
||||||
|
|
||||||
|
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
|
||||||
/* This should be the last !!! */
|
/* This should be the last !!! */
|
||||||
SQLCOM_END
|
SQLCOM_END
|
||||||
};
|
};
|
||||||
|
@ -592,6 +593,11 @@ typedef struct st_lex
|
||||||
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
|
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
|
||||||
bool derived_tables;
|
bool derived_tables;
|
||||||
bool safe_to_cache_query;
|
bool safe_to_cache_query;
|
||||||
|
/* Prepared statements SQL syntax:*/
|
||||||
|
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
|
||||||
|
LEX_STRING prepared_stmt_code; /* Statement query (in PREPARE )*/
|
||||||
|
/* Names of user variables holding parameters (in EXECUTE) */
|
||||||
|
List<LEX_STRING> prepared_stmt_params;
|
||||||
st_lex() {}
|
st_lex() {}
|
||||||
inline void uncacheable(uint8 cause)
|
inline void uncacheable(uint8 cause)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1957,7 +1957,44 @@ mysql_execute_command(THD *thd)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SQLCOM_PREPARE:
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
|
||||||
|
lex->prepared_stmt_name.length,
|
||||||
|
lex->prepared_stmt_name.str,
|
||||||
|
lex->prepared_stmt_code.length,
|
||||||
|
lex->prepared_stmt_code.str));
|
||||||
|
thd->command= COM_PREPARE;
|
||||||
|
if (!mysql_stmt_prepare(thd, lex->prepared_stmt_code.str,
|
||||||
|
lex->prepared_stmt_code.length + 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
|
||||||
|
send_error(thd,ER_UNKNOWN_STMT_HANDLER,"Undefined prepared statement");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SQLCOM_DO:
|
case SQLCOM_DO:
|
||||||
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
|
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
|
||||||
(res= open_and_lock_tables(thd,tables))))
|
(res= open_and_lock_tables(thd,tables))))
|
||||||
|
|
|
@ -99,12 +99,15 @@ public:
|
||||||
#else
|
#else
|
||||||
bool (*set_params_data)(Prepared_statement *st);
|
bool (*set_params_data)(Prepared_statement *st);
|
||||||
#endif
|
#endif
|
||||||
|
bool (*set_params_from_vars)(Prepared_statement *stmt,
|
||||||
|
List<LEX_STRING>& varnames);
|
||||||
public:
|
public:
|
||||||
Prepared_statement(THD *thd_arg);
|
Prepared_statement(THD *thd_arg);
|
||||||
virtual ~Prepared_statement();
|
virtual ~Prepared_statement();
|
||||||
virtual Statement::Type type() const;
|
virtual Statement::Type type() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void execute_stmt(THD *thd, Prepared_statement *stmt);
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
Implementation
|
Implementation
|
||||||
|
@ -632,6 +635,116 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
||||||
#endif /*!EMBEDDED_LIBRARY*/
|
#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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool insert_params_from_vars(Prepared_statement *stmt,
|
||||||
|
List<LEX_STRING>& varnames)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
for (Item_param **it= begin; it < end; ++it)
|
||||||
|
{
|
||||||
|
Item_param *param= *it;
|
||||||
|
varname= var_it++;
|
||||||
|
if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
|
||||||
|
(byte*) varname->str,
|
||||||
|
varname->length))
|
||||||
|
&& entry->value)
|
||||||
|
{
|
||||||
|
param->item_result_type= entry->type;
|
||||||
|
switch (entry->type)
|
||||||
|
{
|
||||||
|
case REAL_RESULT:
|
||||||
|
param->set_double(*(double*)entry->value);
|
||||||
|
break;
|
||||||
|
case INT_RESULT:
|
||||||
|
param->set_int(*(longlong*)entry->value);
|
||||||
|
break;
|
||||||
|
case STRING_RESULT:
|
||||||
|
param->set_value(entry->value, entry->length,
|
||||||
|
entry->collation.collation);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
param->maybe_null= param->null_value= param->value_is_set= 1;
|
||||||
|
}
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
|
||||||
|
List<LEX_STRING>& varnames)
|
||||||
|
{
|
||||||
|
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, query;
|
||||||
|
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 ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
|
||||||
|
(byte*) varname->str,
|
||||||
|
varname->length))
|
||||||
|
&& entry->value)
|
||||||
|
{
|
||||||
|
param->item_result_type= entry->type;
|
||||||
|
switch (entry->type)
|
||||||
|
{
|
||||||
|
case REAL_RESULT:
|
||||||
|
param->set_double(*(double*)entry->value);
|
||||||
|
break;
|
||||||
|
case INT_RESULT:
|
||||||
|
param->set_int(*(longlong*)entry->value);
|
||||||
|
break;
|
||||||
|
case STRING_RESULT:
|
||||||
|
param->set_value(entry->value, entry->length,
|
||||||
|
entry->collation.collation);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
res= param->query_val_str(&str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
param->maybe_null= param->null_value= param->value_is_set= 1;
|
||||||
|
res= &my_null_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.replace(param->pos_in_query+length, 1, *res))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
length+= res->length()-1;
|
||||||
|
}
|
||||||
|
if (alloc_query(stmt->thd, (char *) query.ptr(), query.length()+1))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Validate INSERT statement:
|
Validate INSERT statement:
|
||||||
|
|
||||||
|
@ -828,7 +941,7 @@ static int mysql_test_delete(Prepared_statement *stmt,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int mysql_test_select(Prepared_statement *stmt,
|
static int mysql_test_select(Prepared_statement *stmt,
|
||||||
TABLE_LIST *tables)
|
TABLE_LIST *tables, bool text_protocol)
|
||||||
{
|
{
|
||||||
THD *thd= stmt->thd;
|
THD *thd= stmt->thd;
|
||||||
LEX *lex= stmt->lex;
|
LEX *lex= stmt->lex;
|
||||||
|
@ -860,7 +973,7 @@ static int mysql_test_select(Prepared_statement *stmt,
|
||||||
|
|
||||||
if (lex->describe)
|
if (lex->describe)
|
||||||
{
|
{
|
||||||
if (send_prep_stmt(stmt, 0))
|
if (!text_protocol && send_prep_stmt(stmt, 0))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -874,6 +987,8 @@ static int mysql_test_select(Prepared_statement *stmt,
|
||||||
goto err_prep;
|
goto err_prep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!text_protocol)
|
||||||
|
{
|
||||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
||||||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
|
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
@ -881,7 +996,7 @@ static int mysql_test_select(Prepared_statement *stmt,
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
goto err_prep;
|
goto err_prep;
|
||||||
|
}
|
||||||
unit->cleanup();
|
unit->cleanup();
|
||||||
}
|
}
|
||||||
thd->free_temporary_memory_pool_for_ps_preparing();
|
thd->free_temporary_memory_pool_for_ps_preparing();
|
||||||
|
@ -1159,7 +1274,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
|
||||||
0 success
|
0 success
|
||||||
1 error, sent to client
|
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;
|
THD *thd= stmt->thd;
|
||||||
LEX *lex= stmt->lex;
|
LEX *lex= stmt->lex;
|
||||||
|
@ -1196,7 +1311,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SQLCOM_SELECT:
|
case SQLCOM_SELECT:
|
||||||
if ((res= mysql_test_select(stmt, tables)))
|
if ((res= mysql_test_select(stmt, tables, text_protocol)))
|
||||||
goto error;
|
goto error;
|
||||||
/* Statement and field info has already been sent */
|
/* Statement and field info has already been sent */
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
|
@ -1254,7 +1369,7 @@ static int send_prepare_results(Prepared_statement *stmt)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
DBUG_RETURN(send_prep_stmt(stmt, 0));
|
DBUG_RETURN(text_protocol?0:send_prep_stmt(stmt, 0));
|
||||||
error:
|
error:
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
||||||
|
@ -1295,6 +1410,14 @@ static bool init_param_array(Prepared_statement *stmt)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
SYNOPSIS
|
||||||
|
mysql_stmt_prepare()
|
||||||
|
packet Prepared query
|
||||||
|
packet_length query length, with ignored trailing NULL or quote char.
|
||||||
|
name NULL or statement name. For unnamed statements binary PS
|
||||||
|
protocol is used, for named statmenents text protocol is
|
||||||
|
used.
|
||||||
|
|
||||||
Parse the query and send the total number of parameters
|
Parse the query and send the total number of parameters
|
||||||
and resultset metadata information back to client (if any),
|
and resultset metadata information back to client (if any),
|
||||||
without executing the query i.e. without any log/disk
|
without executing the query i.e. without any log/disk
|
||||||
|
@ -1306,9 +1429,11 @@ static bool init_param_array(Prepared_statement *stmt)
|
||||||
list in lex->param_array, so that a fast and direct
|
list in lex->param_array, so that a fast and direct
|
||||||
retrieval can be made without going through all field
|
retrieval can be made without going through all field
|
||||||
items.
|
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;
|
LEX *lex;
|
||||||
Prepared_statement *stmt= new Prepared_statement(thd);
|
Prepared_statement *stmt= new Prepared_statement(thd);
|
||||||
|
@ -1320,14 +1445,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||||
if (stmt == 0)
|
if (stmt == 0)
|
||||||
{
|
{
|
||||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
stmt->name.length= name->length;
|
||||||
|
if (!(stmt->name.str= my_memdup((byte*)name->str, name->length,
|
||||||
|
MYF(MY_WME))))
|
||||||
|
{
|
||||||
|
delete stmt;
|
||||||
|
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thd->stmt_map.insert(stmt))
|
if (thd->stmt_map.insert(stmt))
|
||||||
{
|
{
|
||||||
delete stmt;
|
delete stmt;
|
||||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
thd->stmt_backup.set_statement(thd);
|
thd->stmt_backup.set_statement(thd);
|
||||||
|
@ -1344,7 +1481,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||||
/* Statement map deletes statement on erase */
|
/* Statement map deletes statement on erase */
|
||||||
thd->stmt_map.erase(stmt);
|
thd->stmt_map.erase(stmt);
|
||||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
mysql_log.write(thd, COM_PREPARE, "%s", packet);
|
mysql_log.write(thd, COM_PREPARE, "%s", packet);
|
||||||
|
@ -1356,7 +1493,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||||
|
|
||||||
error= yyparse((void *)thd) || thd->is_fatal_error ||
|
error= yyparse((void *)thd) || thd->is_fatal_error ||
|
||||||
init_param_array(stmt) ||
|
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 */
|
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
|
@ -1372,6 +1509,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||||
{
|
{
|
||||||
/* Statement map deletes statement on erase */
|
/* Statement map deletes statement on erase */
|
||||||
thd->stmt_map.erase(stmt);
|
thd->stmt_map.erase(stmt);
|
||||||
|
stmt= NULL;
|
||||||
/* error is sent inside yyparse/send_prepare_results */
|
/* error is sent inside yyparse/send_prepare_results */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1386,7 +1524,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
|
||||||
sl->prep_where= sl->where;
|
sl->prep_where= sl->where;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBUG_VOID_RETURN;
|
DBUG_RETURN(!stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reinit statement before execution */
|
/* Reinit statement before execution */
|
||||||
|
@ -1439,6 +1577,7 @@ static void reset_stmt_for_execute(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
|
||||||
|
@ -1447,7 +1586,6 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
|
||||||
mysql_stmt_execute()
|
mysql_stmt_execute()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -1471,11 +1609,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
thd->stmt_backup.set_statement(thd);
|
|
||||||
thd->set_statement(stmt);
|
|
||||||
|
|
||||||
reset_stmt_for_execute(stmt);
|
|
||||||
|
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
if (stmt->param_count)
|
if (stmt->param_count)
|
||||||
{
|
{
|
||||||
|
@ -1493,21 +1626,69 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||||
if (stmt->param_count && stmt->set_params_data(stmt))
|
if (stmt->param_count && stmt->set_params_data(stmt))
|
||||||
goto set_params_data_err;
|
goto set_params_data_err;
|
||||||
#endif
|
#endif
|
||||||
|
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
|
||||||
|
execute_stmt(thd, stmt);
|
||||||
|
thd->lex->unit.cleanup();
|
||||||
|
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
|
||||||
|
set_params_data_err:
|
||||||
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
|
||||||
|
send_error(thd);
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
DBUG_ENTER("mysql_stmt_execute");
|
||||||
|
|
||||||
|
if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
|
||||||
|
{
|
||||||
|
send_error(thd, ER_UNKNOWN_STMT_HANDLER,
|
||||||
|
"Undefined prepared statement");
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
|
||||||
|
{
|
||||||
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
|
||||||
|
send_error(thd);
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
/* Item_param allows setting parameters in COM_EXECUTE only */
|
||||||
|
thd->command= COM_EXECUTE;
|
||||||
|
|
||||||
|
if (stmt->set_params_from_vars(stmt, thd->lex->prepared_stmt_params))
|
||||||
|
{
|
||||||
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
|
||||||
|
send_error(thd);
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_stmt(thd, stmt);
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Execute prepared statement.
|
||||||
|
Caller must set parameter values and thd::protocol.
|
||||||
|
*/
|
||||||
|
static void execute_stmt(THD *thd, Prepared_statement *stmt)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("execute_stmt");
|
||||||
|
thd->stmt_backup.set_statement(thd);
|
||||||
|
thd->set_statement(stmt);
|
||||||
|
reset_stmt_for_execute(stmt);
|
||||||
|
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
my_pthread_setprio(pthread_self(),QUERY_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);
|
mysql_execute_command(thd);
|
||||||
thd->lex->unit.cleanup();
|
|
||||||
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
|
||||||
|
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||||
|
|
||||||
|
@ -1515,15 +1696,10 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||||
close_thread_tables(thd); // to close derived tables
|
close_thread_tables(thd); // to close derived tables
|
||||||
thd->set_statement(&thd->stmt_backup);
|
thd->set_statement(&thd->stmt_backup);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
|
|
||||||
set_params_data_err:
|
|
||||||
thd->set_statement(&thd->stmt_backup);
|
|
||||||
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
|
|
||||||
send_error(thd);
|
|
||||||
DBUG_VOID_RETURN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reset a prepared statement, in case there was an error in send_longdata.
|
Reset a prepared statement, in case there was an error in send_longdata.
|
||||||
Note: we don't send any reply to that command.
|
Note: we don't send any reply to that command.
|
||||||
|
@ -1665,6 +1841,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
||||||
if (mysql_bin_log.is_open())
|
if (mysql_bin_log.is_open())
|
||||||
{
|
{
|
||||||
log_full_query= 1;
|
log_full_query= 1;
|
||||||
|
set_params_from_vars= insert_params_from_vars_with_log;
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
set_params= insert_params_withlog;
|
set_params= insert_params_withlog;
|
||||||
#else
|
#else
|
||||||
|
@ -1672,17 +1849,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
set_params_from_vars= insert_params_from_vars;
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
set_params= insert_params;
|
set_params= insert_params;
|
||||||
#else
|
#else
|
||||||
set_params_data= emb_insert_params;
|
set_params_data= emb_insert_params;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Prepared_statement::~Prepared_statement()
|
Prepared_statement::~Prepared_statement()
|
||||||
{
|
{
|
||||||
free_items(free_list);
|
free_items(free_list);
|
||||||
|
if (name.str)
|
||||||
|
my_free(name.str, MYF(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -431,6 +431,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||||
%token MEDIUMTEXT
|
%token MEDIUMTEXT
|
||||||
%token NUMERIC_SYM
|
%token NUMERIC_SYM
|
||||||
%token PRECISION
|
%token PRECISION
|
||||||
|
%token PREPARE_SYM
|
||||||
|
%token DEALLOCATE_SYM
|
||||||
%token QUICK
|
%token QUICK
|
||||||
%token REAL
|
%token REAL
|
||||||
%token SIGNED_SYM
|
%token SIGNED_SYM
|
||||||
|
@ -723,6 +725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||||
precision subselect_start opt_and charset
|
precision subselect_start opt_and charset
|
||||||
subselect_end select_var_list select_var_list_init help opt_len
|
subselect_end select_var_list select_var_list_init help opt_len
|
||||||
opt_extended_describe
|
opt_extended_describe
|
||||||
|
prepare execute deallocate
|
||||||
END_OF_INPUT
|
END_OF_INPUT
|
||||||
|
|
||||||
%type <NONE>
|
%type <NONE>
|
||||||
|
@ -759,10 +762,12 @@ verb_clause:
|
||||||
| checksum
|
| checksum
|
||||||
| commit
|
| commit
|
||||||
| create
|
| create
|
||||||
|
| deallocate
|
||||||
| delete
|
| delete
|
||||||
| describe
|
| describe
|
||||||
| do
|
| do
|
||||||
| drop
|
| drop
|
||||||
|
| execute
|
||||||
| flush
|
| flush
|
||||||
| grant
|
| grant
|
||||||
| handler
|
| handler
|
||||||
|
@ -774,6 +779,7 @@ verb_clause:
|
||||||
| optimize
|
| optimize
|
||||||
| keycache
|
| keycache
|
||||||
| preload
|
| preload
|
||||||
|
| prepare
|
||||||
| purge
|
| purge
|
||||||
| rename
|
| rename
|
||||||
| repair
|
| repair
|
||||||
|
@ -794,6 +800,72 @@ verb_clause:
|
||||||
| use
|
| 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 TEXT_STRING_sys
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
lex->prepared_stmt_code= $4;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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 */
|
||||||
|
|
||||||
help:
|
help:
|
||||||
|
@ -4899,6 +4971,7 @@ keyword:
|
||||||
| DATETIME {}
|
| DATETIME {}
|
||||||
| DATE_SYM {}
|
| DATE_SYM {}
|
||||||
| DAY_SYM {}
|
| DAY_SYM {}
|
||||||
|
| DEALLOCATE_SYM {}
|
||||||
| DELAY_KEY_WRITE_SYM {}
|
| DELAY_KEY_WRITE_SYM {}
|
||||||
| DES_KEY_FILE {}
|
| DES_KEY_FILE {}
|
||||||
| DIRECTORY_SYM {}
|
| DIRECTORY_SYM {}
|
||||||
|
@ -4996,6 +5069,7 @@ keyword:
|
||||||
| PASSWORD {}
|
| PASSWORD {}
|
||||||
| POINT_SYM {}
|
| POINT_SYM {}
|
||||||
| POLYGON {}
|
| POLYGON {}
|
||||||
|
| PREPARE_SYM {}
|
||||||
| PREV_SYM {}
|
| PREV_SYM {}
|
||||||
| PROCESS {}
|
| PROCESS {}
|
||||||
| PROCESSLIST_SYM {}
|
| PROCESSLIST_SYM {}
|
||||||
|
|
Loading…
Add table
Reference in a new issue