Commit a8386d01 authored by sergefp@mysql.com's avatar sergefp@mysql.com

Many files:

  SQL Syntax for Prepared Statements (WL#1622)
ps.test, ps.result:
  new file
parent 3bfbe6e0
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=NULL;
execute stmt5 using @nullvar;
? + a
NULL
NULL
NULL
NULL
NULL
drop table t1;
#
# 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=NULL;
execute stmt5 using @nullvar;
drop table t1;
...@@ -610,16 +610,21 @@ void Item_param::set_double(double value) ...@@ -610,16 +610,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)
{ {
...@@ -1471,7 +1476,7 @@ bool Item::send(Protocol *protocol, String *buffer) ...@@ -1471,7 +1476,7 @@ bool Item::send(Protocol *protocol, String *buffer)
} }
case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY:
{ {
longlong nr; longlong nr;
nr= val_int(); nr= val_int();
if (!null_value) if (!null_value)
result= protocol->store_tiny(nr); result= protocol->store_tiny(nr);
......
...@@ -385,6 +385,7 @@ class Item_param :public Item ...@@ -385,6 +385,7 @@ class Item_param :public Item
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);
......
...@@ -131,6 +131,7 @@ static SYMBOL symbols[] = { ...@@ -131,6 +131,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)},
...@@ -320,6 +321,7 @@ static SYMBOL symbols[] = { ...@@ -320,6 +321,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)},
......
...@@ -604,8 +604,11 @@ int mysqld_show_column_types(THD *thd); ...@@ -604,8 +604,11 @@ 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); class Prepared_statement;
Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
uint packet_length, bool text_protocol=false);
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, Prepared_statement *stmt);
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);
......
...@@ -4805,6 +4805,9 @@ struct show_var_st status_vars[]= { ...@@ -4805,6 +4805,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,6 +78,23 @@ extern "C" void free_user_var(user_var_entry *entry) ...@@ -78,6 +78,23 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0)); my_free((char*) entry,MYF(0));
} }
/****************************************************************************
** SQL syntax names for Prepared Statements
****************************************************************************/
extern "C" byte *get_stmt_key(SQL_PREP_STMT_ENTRY *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->name.length;
return (byte*) entry->name.str;
}
extern "C" void free_sql_stmt(SQL_PREP_STMT_ENTRY *entry)
{
char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
my_free((char*) entry,MYF(0));
}
/**************************************************************************** /****************************************************************************
** Thread specific functions ** Thread specific functions
...@@ -160,7 +177,10 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), ...@@ -160,7 +177,10 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
16); 16);
else else
bzero((char*) &user_var_events, sizeof(user_var_events)); bzero((char*) &user_var_events, sizeof(user_var_events));
hash_init(&sql_prepared_stmts, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_stmt_key,
(hash_free_key) free_sql_stmt,0);
/* Protocol */ /* Protocol */
protocol= &protocol_simple; // Default protocol protocol= &protocol_simple; // Default protocol
protocol_simple.init(this); protocol_simple.init(this);
...@@ -279,6 +299,7 @@ void THD::cleanup(void) ...@@ -279,6 +299,7 @@ void THD::cleanup(void)
my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
delete_dynamic(&user_var_events); delete_dynamic(&user_var_events);
hash_free(&user_vars); hash_free(&user_vars);
hash_free(&sql_prepared_stmts);
if (global_read_lock) if (global_read_lock)
unlock_global_read_lock(this); unlock_global_read_lock(this);
if (ull) if (ull)
......
...@@ -594,6 +594,12 @@ class THD :public ilink, ...@@ -594,6 +594,12 @@ class THD :public ilink,
struct system_variables variables; // Changeable local variables struct system_variables variables; // Changeable local variables
pthread_mutex_t LOCK_delete; // Locked before thd is deleted pthread_mutex_t LOCK_delete; // Locked before thd is deleted
/*
statement_name -> (Statement*) map of statements prepared using SQL syntax.
Hash element is SQL_PREP_STMT_ENTRY.
*/
HASH sql_prepared_stmts;
/* all prepared statements and cursors of this connection */ /* all prepared statements and cursors of this connection */
Statement_map stmt_map; Statement_map stmt_map;
/* /*
...@@ -1269,6 +1275,14 @@ class user_var_entry ...@@ -1269,6 +1275,14 @@ class user_var_entry
DTCollation collation; DTCollation collation;
}; };
class Prepared_statement;
/* Needed by THD::sql_prepared_stmts */
typedef struct st_sql_prep_stmt_entry
{
public:
LEX_STRING name;
Prepared_statement *stmt;
}SQL_PREP_STMT_ENTRY;
/* Class for unique (removing of duplicates) */ /* Class for unique (removing of duplicates) */
......
...@@ -76,6 +76,7 @@ enum enum_sql_command { ...@@ -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
}; };
...@@ -583,6 +584,11 @@ typedef struct st_lex ...@@ -583,6 +584,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)
{ {
......
...@@ -1956,7 +1956,91 @@ mysql_execute_command(THD *thd) ...@@ -1956,7 +1956,91 @@ mysql_execute_command(THD *thd)
} }
break; break;
} }
case SQLCOM_PREPARE:
{
char *stmt_name= lex->prepared_stmt_name.str;
uint name_len= lex->prepared_stmt_name.length;
Prepared_statement *stmt;
SQL_PREP_STMT_ENTRY *entry;
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n", name_len, stmt_name,
lex->prepared_stmt_code.length,
lex->prepared_stmt_code.str));
if ((entry=(SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts,
(byte*)stmt_name, name_len)))
{
/* Free the statement with the same name and reuse hash entry */
thd->stmt_map.erase((Statement*)entry->stmt);
}
else
{
uint size=ALIGN_SIZE(sizeof(SQL_PREP_STMT_ENTRY))+name_len+1;
if (!hash_inited(&thd->sql_prepared_stmts) ||
!(entry= (SQL_PREP_STMT_ENTRY*)my_malloc(size,MYF(MY_WME))))
{
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
entry->name.str= (char*)entry + ALIGN_SIZE(sizeof(SQL_PREP_STMT_ENTRY));
entry->name.length= name_len;
memcpy(entry->name.str, stmt_name, name_len+1);
if (my_hash_insert(&thd->sql_prepared_stmts, (byte*)entry))
{
my_free((char*)entry,MYF(0));
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
}
/* Pretend this is a COM_PREPARE query so parser allows placeholders etc*/
thd->command= COM_PREPARE;
/* 'length+1' is for alloc_query that strips the last character */
stmt= mysql_stmt_prepare(thd, lex->prepared_stmt_code.str,
lex->prepared_stmt_code.length + 1, true);
if (stmt)
{
entry->stmt= stmt;
send_ok(thd, 0L, 0L, "Statement prepared");
}
else
hash_delete(&thd->sql_prepared_stmts, (byte*)entry);
break;
}
case SQLCOM_EXECUTE:
{
char *stmt_name= lex->prepared_stmt_name.str;
uint name_len= lex->prepared_stmt_name.length;
SQL_PREP_STMT_ENTRY *entry;
DBUG_PRINT("info", ("EXECUTE: %.*s\n", name_len, stmt_name));
if (!(entry= (SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts,
(byte*)stmt_name,
name_len)))
{
send_error(thd, ER_UNKNOWN_STMT_HANDLER, "Undefined prepared statement");
lex->prepared_stmt_params.empty();
break;
}
mysql_sql_stmt_execute(thd, entry->stmt);
lex->prepared_stmt_params.empty();
break;
}
case SQLCOM_DEALLOCATE_PREPARE:
{
char *stmt_name= lex->prepared_stmt_name.str;
uint name_len= lex->prepared_stmt_name.length;
SQL_PREP_STMT_ENTRY *entry;
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", name_len, stmt_name));
if (!(entry= (SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts,
(byte*)stmt_name,
name_len)))
{
send_error(thd, ER_UNKNOWN_STMT_HANDLER, "Undefined prepared statement");
break;
}
thd->stmt_map.erase((Statement*)entry->stmt);
hash_delete(&thd->sql_prepared_stmts, (byte*)entry);
send_ok(thd);
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,6 +99,8 @@ class Prepared_statement: public Statement ...@@ -99,6 +99,8 @@ class Prepared_statement: public Statement
#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();
...@@ -623,6 +625,120 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) ...@@ -623,6 +625,120 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
#endif /*!EMBEDDED_LIBRARY*/ #endif /*!EMBEDDED_LIBRARY*/
/*
Set prepared statement parameters from user variables.
Also replace '?' marks with values in thd->query if binary logging is on.
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)))
{
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->item_result_type= INT_RESULT;
param->maybe_null= param->null_value= 1;
param->value_is_set= 0;
}
}
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;
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)))
{
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->item_result_type= INT_RESULT;
param->maybe_null= param->null_value= 1;
param->value_is_set= 0;
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 the following information for INSERT statement: Validate the following information for INSERT statement:
- field existence - field existence
...@@ -780,7 +896,8 @@ static int mysql_test_select_fields(Prepared_statement *stmt, ...@@ -780,7 +896,8 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
Item *having, ORDER *proc, Item *having, ORDER *proc,
ulong select_options, ulong select_options,
SELECT_LEX_UNIT *unit, SELECT_LEX_UNIT *unit,
SELECT_LEX *select_lex) SELECT_LEX *select_lex,
bool text_protocol)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
...@@ -814,7 +931,7 @@ static int mysql_test_select_fields(Prepared_statement *stmt, ...@@ -814,7 +931,7 @@ static int mysql_test_select_fields(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
...@@ -834,14 +951,16 @@ static int mysql_test_select_fields(Prepared_statement *stmt, ...@@ -834,14 +951,16 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
goto err_prep; goto err_prep;
} }
if (send_prep_stmt(stmt, fields.elements) || if (!text_protocol)
thd->protocol_simple.send_fields(&fields, 0) {
if (send_prep_stmt(stmt, fields.elements) ||
thd->protocol_simple.send_fields(&fields, 0)
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
|| net_flush(&thd->net) || net_flush(&thd->net)
#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();
...@@ -865,7 +984,7 @@ static int mysql_test_select_fields(Prepared_statement *stmt, ...@@ -865,7 +984,7 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
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;
...@@ -905,7 +1024,8 @@ static int send_prepare_results(Prepared_statement *stmt) ...@@ -905,7 +1024,8 @@ static int send_prepare_results(Prepared_statement *stmt)
select_lex->having, select_lex->having,
(ORDER*)lex->proc_list.first, (ORDER*)lex->proc_list.first,
select_lex->options | thd->options, select_lex->options | thd->options,
&(lex->unit), select_lex))) &(lex->unit), select_lex,
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);
...@@ -917,7 +1037,7 @@ static int send_prepare_results(Prepared_statement *stmt) ...@@ -917,7 +1037,7 @@ static int send_prepare_results(Prepared_statement *stmt)
*/ */
break; break;
} }
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)
...@@ -970,9 +1090,11 @@ static bool init_param_array(Prepared_statement *stmt) ...@@ -970,9 +1090,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) Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
uint packet_length, bool text_protocol)
{ {
LEX *lex; LEX *lex;
Prepared_statement *stmt= new Prepared_statement(thd); Prepared_statement *stmt= new Prepared_statement(thd);
...@@ -982,14 +1104,14 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -982,14 +1104,14 @@ 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(NULL);
} }
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(NULL);
} }
thd->stmt_backup.set_statement(thd); thd->stmt_backup.set_statement(thd);
...@@ -1006,7 +1128,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -1006,7 +1128,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(NULL);
} }
mysql_log.write(thd, COM_PREPARE, "%s", packet); mysql_log.write(thd, COM_PREPARE, "%s", packet);
...@@ -1018,7 +1140,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -1018,7 +1140,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, text_protocol);
/* 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))
...@@ -1034,6 +1156,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -1034,6 +1156,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
...@@ -1048,7 +1171,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) ...@@ -1048,7 +1171,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 */
...@@ -1109,7 +1232,6 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) ...@@ -1109,7 +1232,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);
...@@ -1181,6 +1303,46 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1181,6 +1303,46 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
} }
/*
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, Prepared_statement *stmt)
{
DBUG_ENTER("mysql_stmt_execute");
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;
}
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
reset_stmt_for_execute(stmt);
thd->command= COM_EXECUTE;
if (stmt->set_params_from_vars(stmt, thd->stmt_backup.lex->
prepared_stmt_params))
{
thd->set_statement(&thd->stmt_backup);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
send_error(thd);
}
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_execute_command(thd);
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
cleanup_items(stmt->free_list);
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 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.
...@@ -1322,6 +1484,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -1322,6 +1484,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
...@@ -1329,11 +1492,14 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -1329,11 +1492,14 @@ 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
}
} }
......
...@@ -430,6 +430,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -430,6 +430,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
...@@ -722,6 +724,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -722,6 +724,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>
...@@ -758,10 +761,12 @@ verb_clause: ...@@ -758,10 +761,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
...@@ -773,6 +778,7 @@ verb_clause: ...@@ -773,6 +778,7 @@ verb_clause:
| optimize | optimize
| keycache | keycache
| preload | preload
| prepare
| purge | purge
| rename | rename
| repair | repair
...@@ -793,6 +799,72 @@ verb_clause: ...@@ -793,6 +799,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:
...@@ -4782,6 +4854,7 @@ keyword: ...@@ -4782,6 +4854,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 {}
...@@ -4879,6 +4952,7 @@ keyword: ...@@ -4879,6 +4952,7 @@ keyword:
| PASSWORD {} | PASSWORD {}
| POINT_SYM {} | POINT_SYM {}
| POLYGON {} | POLYGON {}
| PREPARE_SYM {}
| PREV_SYM {} | PREV_SYM {}
| PROCESS {} | PROCESS {}
| PROCESSLIST_SYM {} | PROCESSLIST_SYM {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment