summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2004-04-13 02:18:09 +0400
committerunknown <sergefp@mysql.com>2004-04-13 02:18:09 +0400
commit7e114f3cd138312b41d20f31113bb6e498925f14 (patch)
tree073a7565e0619d427b5224ddce1fd0b15eb02c06
parent0ba6cb48d84f1ff951d09871a96be6cdef3f2c3c (diff)
parent756223bbda043a20840a1c2236a2f62a35640460 (diff)
downloadmariadb-git-7e114f3cd138312b41d20f31113bb6e498925f14.tar.gz
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
-rw-r--r--mysql-test/r/ps.result86
-rw-r--r--mysql-test/t/ps.test79
-rw-r--r--sql/item.cc11
-rw-r--r--sql/item.h1
-rw-r--r--sql/lex.h2
-rw-r--r--sql/mysql_priv.h4
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/sql_class.cc43
-rw-r--r--sql/sql_class.h29
-rw-r--r--sql/sql_lex.h6
-rw-r--r--sql/sql_parse.cc39
-rw-r--r--sql/sql_prepare.cc260
-rw-r--r--sql/sql_yacc.yy74
13 files changed, 582 insertions, 55 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
new file mode 100644
index 00000000000..14af3c32292
--- /dev/null
+++ b/mysql-test/r/ps.result
@@ -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;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
new file mode 100644
index 00000000000..ab698174161
--- /dev/null
+++ b/mysql-test/t/ps.test
@@ -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;
+
diff --git a/sql/item.cc b/sql/item.cc
index 2584c1cafb3..bb8cf9a33cf 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -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");
- str_value.copy(str,length,default_charset());
+ str_value.copy(str,length,ci);
item_type= STRING_ITEM;
value_is_set= 1;
DBUG_PRINT("info", ("string: %s", str_value.ptr()));
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)
{
@@ -1493,7 +1498,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);
diff --git a/sql/item.h b/sql/item.h
index 0f2927b0d99..25b8bbe06a6 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -413,6 +413,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);
diff --git a/sql/lex.h b/sql/lex.h
index 94ea0295f05..3274d544744 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -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)},
@@ -322,6 +323,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)},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 1175b93b9ba..479b1d29be5 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -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);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5176ee33a17..3db8101366e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4809,6 +4809,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},
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 1b4c8bec416..87b6c49a4b7 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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;
}
+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);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index aa526d5e474..8ccfe3cddd5 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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;
};
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index ea1accabf37..2f1f7e64ac4 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -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
};
@@ -592,6 +593,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)
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1e3beeca8a9..64fa398e5f4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1957,7 +1957,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))))
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 69c5be69210..e578dc988f8 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -99,12 +99,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
@@ -632,6 +635,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:
@@ -828,7 +941,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;
@@ -860,7 +973,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
@@ -874,14 +987,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();
@@ -1159,7 +1274,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;
@@ -1196,7 +1311,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);
@@ -1254,7 +1369,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);
@@ -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
and resultset metadata information back to client (if any),
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
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);
@@ -1320,14 +1445,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= 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))
{
delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(1);
}
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 */
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);
@@ -1356,7 +1493,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))
@@ -1372,6 +1509,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
@@ -1386,7 +1524,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 */
@@ -1439,6 +1577,7 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
}
}
+
/*
Executes previously prepared query.
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()
*/
-
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
{
ulong stmt_id= uint4korr(packet);
@@ -1471,11 +1609,6 @@ 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)
{
@@ -1493,37 +1626,80 @@ 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
-
- 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);
+ 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))
+ 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;
-
-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.
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())
{
log_full_query= 1;
+ set_params_from_vars= insert_params_from_vars_with_log;
#ifndef EMBEDDED_LIBRARY
set_params= insert_params_withlog;
#else
@@ -1672,17 +1849,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
#endif
}
else
+ {
+ set_params_from_vars= insert_params_from_vars;
#ifndef EMBEDDED_LIBRARY
set_params= insert_params;
#else
set_params_data= emb_insert_params;
#endif
+ }
}
Prepared_statement::~Prepared_statement()
{
free_items(free_list);
+ if (name.str)
+ my_free(name.str, MYF(0));
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index b87b0b29677..a25bd3f17bc 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -431,6 +431,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
@@ -723,6 +725,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>
@@ -759,10 +762,12 @@ verb_clause:
| checksum
| commit
| create
+ | deallocate
| delete
| describe
| do
| drop
+ | execute
| flush
| grant
| handler
@@ -774,6 +779,7 @@ verb_clause:
| optimize
| keycache
| preload
+ | prepare
| purge
| rename
| repair
@@ -794,6 +800,72 @@ 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:
@@ -4899,6 +4971,7 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
+ | DEALLOCATE_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
@@ -4996,6 +5069,7 @@ keyword:
| PASSWORD {}
| POINT_SYM {}
| POLYGON {}
+ | PREPARE_SYM {}
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}