mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/dbdata/psergey/mysql-4.1-ps-merge sql/item.cc: Auto merged sql/item.h: Auto merged sql/sql_lex.h: Auto merged
This commit is contained in:
commit
d55809d891
13 changed files with 620 additions and 56 deletions
101
mysql-test/r/ps.result
Normal file
101
mysql-test/r/ps.result
Normal file
|
@ -0,0 +1,101 @@
|
|||
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
|
||||
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
|
||||
drop table t1,t2;
|
102
mysql-test/t/ps.test
Normal file
102
mysql-test/t/ps.test
Normal file
|
@ -0,0 +1,102 @@
|
|||
#
|
||||
# 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 ;
|
||||
|
||||
drop table t1,t2;
|
||||
|
11
sql/item.cc
11
sql/item.cc
|
@ -667,10 +667,10 @@ 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");
|
||||
str_value.copy(str,length,default_charset());
|
||||
str_value.copy(str,length,ci);
|
||||
item_type= STRING_ITEM;
|
||||
value_is_set= 1;
|
||||
maybe_null= 0;
|
||||
|
@ -678,6 +678,11 @@ void Item_param::set_value(const char *str, uint length)
|
|||
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)
|
||||
{
|
||||
|
@ -1558,7 +1563,7 @@ bool Item::send(Protocol *protocol, String *buffer)
|
|||
}
|
||||
case MYSQL_TYPE_TINY:
|
||||
{
|
||||
longlong nr;
|
||||
longlong nr;
|
||||
nr= val_int();
|
||||
if (!null_value)
|
||||
result= protocol->store_tiny(nr);
|
||||
|
|
|
@ -414,6 +414,7 @@ public:
|
|||
void set_int(longlong i);
|
||||
void set_double(double i);
|
||||
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_binary(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_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)},
|
||||
|
@ -324,6 +325,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)},
|
||||
|
|
|
@ -634,8 +634,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);
|
||||
|
|
|
@ -4842,6 +4842,9 @@ 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},
|
||||
|
|
|
@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
|
|||
my_free((char*) entry,MYF(0));
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
** Thread specific functions
|
||||
****************************************************************************/
|
||||
|
@ -160,7 +159,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
|
|||
16);
|
||||
else
|
||||
bzero((char*) &user_var_events, sizeof(user_var_events));
|
||||
|
||||
|
||||
/* Protocol */
|
||||
protocol= &protocol_simple; // Default protocol
|
||||
protocol_simple.init(this);
|
||||
|
@ -1199,6 +1198,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);
|
||||
|
@ -1282,17 +1282,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);
|
||||
|
|
|
@ -456,6 +456,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
|
||||
|
@ -522,8 +523,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
|
||||
|
@ -531,12 +538,14 @@ 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)
|
||||
|
@ -550,15 +559,21 @@ public:
|
|||
{
|
||||
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
|
||||
};
|
||||
|
@ -604,6 +605,11 @@ typedef struct st_lex
|
|||
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
|
||||
bool derived_tables;
|
||||
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() {}
|
||||
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;
|
||||
}
|
||||
|
@ -1971,7 +1970,44 @@ mysql_execute_command(THD *thd)
|
|||
}
|
||||
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:
|
||||
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
|
||||
(res= open_and_lock_tables(thd,tables))))
|
||||
|
|
|
@ -98,12 +98,15 @@ public:
|
|||
#else
|
||||
bool (*set_params_data)(Prepared_statement *st);
|
||||
#endif
|
||||
bool (*set_params_from_vars)(Prepared_statement *stmt,
|
||||
List<LEX_STRING>& varnames);
|
||||
public:
|
||||
Prepared_statement(THD *thd_arg);
|
||||
virtual ~Prepared_statement();
|
||||
virtual Statement::Type type() const;
|
||||
};
|
||||
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt);
|
||||
|
||||
/******************************************************************************
|
||||
Implementation
|
||||
|
@ -639,6 +642,116 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
|
|||
#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:
|
||||
|
||||
|
@ -835,7 +948,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;
|
||||
|
@ -867,7 +980,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
|
||||
|
@ -881,14 +994,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();
|
||||
|
@ -1166,7 +1281,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;
|
||||
|
@ -1203,7 +1318,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);
|
||||
|
@ -1261,7 +1376,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);
|
||||
|
@ -1302,6 +1417,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
|
||||
and resultset metadata information back to client (if any),
|
||||
without executing the query i.e. without any log/disk
|
||||
|
@ -1313,9 +1436,11 @@ static bool init_param_array(Prepared_statement *stmt)
|
|||
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);
|
||||
|
@ -1327,14 +1452,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);
|
||||
|
@ -1351,7 +1488,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);
|
||||
|
@ -1363,7 +1500,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))
|
||||
|
@ -1379,6 +1516,7 @@ 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
|
||||
|
@ -1393,7 +1531,7 @@ 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 */
|
||||
|
@ -1472,7 +1610,6 @@ static void reset_stmt_params(Prepared_statement *stmt)
|
|||
mysql_stmt_execute()
|
||||
*/
|
||||
|
||||
|
||||
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
{
|
||||
ulong stmt_id= uint4korr(packet);
|
||||
|
@ -1496,9 +1633,7 @@ 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)
|
||||
{
|
||||
|
@ -1516,44 +1651,88 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
|||
if (stmt->param_count && stmt->set_params_data(stmt))
|
||||
goto set_params_data_err;
|
||||
#endif
|
||||
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
|
||||
execute_stmt(thd, stmt);
|
||||
thd->protocol= &thd->protocol_simple; // Use normal protocol
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
set_params_data_err:
|
||||
reset_stmt_params(stmt);
|
||||
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.
|
||||
thd->free_list is assumed to be garbage.
|
||||
*/
|
||||
static void execute_stmt(THD *thd, Prepared_statement *stmt)
|
||||
{
|
||||
DBUG_ENTER("execute_stmt");
|
||||
thd->free_list= NULL;
|
||||
thd->stmt_backup.set_statement(thd);
|
||||
thd->set_statement(stmt);
|
||||
reset_stmt_for_execute(stmt);
|
||||
|
||||
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();
|
||||
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:
|
||||
cleanup_items(stmt->free_list);
|
||||
reset_stmt_params(stmt);
|
||||
close_thread_tables(thd); // to close derived tables
|
||||
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 a recoverable error.
|
||||
SYNOPSIS
|
||||
|
@ -1694,6 +1873,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
|
|||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
log_full_query= 1;
|
||||
set_params_from_vars= insert_params_from_vars_with_log;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
set_params= insert_params_withlog;
|
||||
#else
|
||||
|
@ -1701,11 +1881,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -432,6 +432,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
|
||||
|
@ -724,6 +726,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 execute deallocate
|
||||
END_OF_INPUT
|
||||
|
||||
%type <NONE>
|
||||
|
@ -760,10 +763,12 @@ verb_clause:
|
|||
| checksum
|
||||
| commit
|
||||
| create
|
||||
| deallocate
|
||||
| delete
|
||||
| describe
|
||||
| do
|
||||
| drop
|
||||
| execute
|
||||
| flush
|
||||
| grant
|
||||
| handler
|
||||
|
@ -775,6 +780,7 @@ verb_clause:
|
|||
| optimize
|
||||
| keycache
|
||||
| preload
|
||||
| prepare
|
||||
| purge
|
||||
| rename
|
||||
| repair
|
||||
|
@ -795,6 +801,71 @@ 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 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:
|
||||
|
@ -4918,6 +4989,7 @@ keyword:
|
|||
| DATETIME {}
|
||||
| DATE_SYM {}
|
||||
| DAY_SYM {}
|
||||
| DEALLOCATE_SYM {}
|
||||
| DELAY_KEY_WRITE_SYM {}
|
||||
| DES_KEY_FILE {}
|
||||
| DIRECTORY_SYM {}
|
||||
|
@ -5016,6 +5088,7 @@ keyword:
|
|||
| PASSWORD {}
|
||||
| POINT_SYM {}
|
||||
| POLYGON {}
|
||||
| PREPARE_SYM {}
|
||||
| PREV_SYM {}
|
||||
| PROCESS {}
|
||||
| PROCESSLIST_SYM {}
|
||||
|
|
Loading…
Add table
Reference in a new issue