summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2004-04-05 19:43:37 +0400
committerunknown <sergefp@mysql.com>2004-04-05 19:43:37 +0400
commitef8a5401f146e0aa17d9cc342c52a82e16e7c6ef (patch)
tree617aa81bcde9afbb78f590039c16af502a91bb52
parentb26165d3a2eeb748b9bd5573f9bac7b73d5f9a37 (diff)
downloadmariadb-git-ef8a5401f146e0aa17d9cc342c52a82e16e7c6ef.tar.gz
Many files:
SQL Syntax for Prepared Statements (WL#1622) ps.test, ps.result: new file sql/item.cc: SQL Syntax for Prepared Statements (WL#1622) sql/item.h: SQL Syntax for Prepared Statements (WL#1622) sql/lex.h: SQL Syntax for Prepared Statements (WL#1622) sql/mysql_priv.h: SQL Syntax for Prepared Statements (WL#1622) sql/mysqld.cc: SQL Syntax for Prepared Statements (WL#1622) sql/sql_class.cc: SQL Syntax for Prepared Statements (WL#1622) sql/sql_class.h: SQL Syntax for Prepared Statements (WL#1622) sql/sql_lex.h: SQL Syntax for Prepared Statements (WL#1622) sql/sql_parse.cc: SQL Syntax for Prepared Statements (WL#1622) sql/sql_prepare.cc: SQL Syntax for Prepared Statements (WL#1622) sql/sql_yacc.yy: SQL Syntax for Prepared Statements (WL#1622)
-rw-r--r--mysql-test/r/ps.result77
-rw-r--r--mysql-test/t/ps.test75
-rw-r--r--sql/item.cc11
-rw-r--r--sql/item.h1
-rw-r--r--sql/lex.h2
-rw-r--r--sql/mysql_priv.h5
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/sql_class.cc23
-rw-r--r--sql/sql_class.h14
-rw-r--r--sql/sql_lex.h6
-rw-r--r--sql/sql_parse.cc86
-rw-r--r--sql/sql_prepare.cc202
-rw-r--r--sql/sql_yacc.yy74
13 files changed, 555 insertions, 24 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
new file mode 100644
index 00000000000..234c4af56f4
--- /dev/null
+++ b/mysql-test/r/ps.result
@@ -0,0 +1,77 @@
+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;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
new file mode 100644
index 00000000000..a97de1a0de7
--- /dev/null
+++ b/mysql-test/t/ps.test
@@ -0,0 +1,75 @@
+#
+# 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;
+
diff --git a/sql/item.cc b/sql/item.cc
index 48e35f06ec3..eacee9b4653 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -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");
- 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)
{
@@ -1471,7 +1476,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 dffa93eaac8..eea8bc011f4 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -385,6 +385,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 3b32d2bcd3b..589579eda51 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -131,6 +131,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)},
@@ -320,6 +321,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 bd919d12348..7845d3199f4 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -604,8 +604,11 @@ 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);
+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_sql_stmt_execute(THD *thd, Prepared_statement *stmt);
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 d602c44c8f9..38e493ce5d6 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4805,6 +4805,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..49fa0455a30 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -78,6 +78,23 @@ extern "C" void free_user_var(user_var_entry *entry)
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
@@ -160,7 +177,10 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
16);
else
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_simple; // Default protocol
protocol_simple.init(this);
@@ -279,6 +299,7 @@ void THD::cleanup(void)
my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
delete_dynamic(&user_var_events);
hash_free(&user_vars);
+ hash_free(&sql_prepared_stmts);
if (global_read_lock)
unlock_global_read_lock(this);
if (ull)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6815d0ae43c..22cb1197b21 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -594,6 +594,12 @@ public:
struct system_variables variables; // Changeable local variables
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 */
Statement_map stmt_map;
/*
@@ -1269,6 +1275,14 @@ class user_var_entry
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) */
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b9d85a23011..b1dd0355d62 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
};
@@ -583,6 +584,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 68ef195cdc4..f2c36eb5513 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1956,7 +1956,91 @@ mysql_execute_command(THD *thd)
}
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:
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 0285c1eec2f..655285d263c 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -99,6 +99,8 @@ 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();
@@ -623,6 +625,120 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
#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:
- field existence
@@ -780,7 +896,8 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
Item *having, ORDER *proc,
ulong select_options,
SELECT_LEX_UNIT *unit,
- SELECT_LEX *select_lex)
+ SELECT_LEX *select_lex,
+ bool text_protocol)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
@@ -814,7 +931,7 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
if (lex->describe)
{
- if (send_prep_stmt(stmt, 0))
+ if (!text_protocol && send_prep_stmt(stmt, 0))
goto err;
}
else
@@ -834,14 +951,16 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
goto err_prep;
}
- if (send_prep_stmt(stmt, fields.elements) ||
- thd->protocol_simple.send_fields(&fields, 0)
+ if (!text_protocol)
+ {
+ if (send_prep_stmt(stmt, fields.elements) ||
+ thd->protocol_simple.send_fields(&fields, 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();
@@ -865,7 +984,7 @@ err:
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;
@@ -905,7 +1024,8 @@ static int send_prepare_results(Prepared_statement *stmt)
select_lex->having,
(ORDER*)lex->proc_list.first,
select_lex->options | thd->options,
- &(lex->unit), select_lex)))
+ &(lex->unit), select_lex,
+ text_protocol)))
goto error;
/* Statement and field info has already been sent */
DBUG_RETURN(0);
@@ -917,7 +1037,7 @@ static int send_prepare_results(Prepared_statement *stmt)
*/
break;
}
- DBUG_RETURN(send_prep_stmt(stmt, 0));
+ DBUG_RETURN(text_protocol? 0: send_prep_stmt(stmt, 0));
error:
if (res < 0)
@@ -970,9 +1090,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)
+Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
+ uint packet_length, bool text_protocol)
{
LEX *lex;
Prepared_statement *stmt= new Prepared_statement(thd);
@@ -982,14 +1104,14 @@ 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(NULL);
}
if (thd->stmt_map.insert(stmt))
{
delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(NULL);
}
thd->stmt_backup.set_statement(thd);
@@ -1006,7 +1128,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(NULL);
}
mysql_log.write(thd, COM_PREPARE, "%s", packet);
@@ -1018,7 +1140,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, text_protocol);
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR))
@@ -1034,6 +1156,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
@@ -1048,7 +1171,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 */
@@ -1109,7 +1232,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);
@@ -1182,6 +1304,46 @@ set_params_data_err:
/*
+ 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.
Note: we don't send any reply to that command.
SYNOPSIS
@@ -1322,6 +1484,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
@@ -1329,11 +1492,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
+ }
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 90dc209f0bc..9e9b698f0b4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -430,6 +430,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
@@ -722,6 +724,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>
@@ -758,10 +761,12 @@ verb_clause:
| checksum
| commit
| create
+ | deallocate
| delete
| describe
| do
| drop
+ | execute
| flush
| grant
| handler
@@ -773,6 +778,7 @@ verb_clause:
| optimize
| keycache
| preload
+ | prepare
| purge
| rename
| repair
@@ -793,6 +799,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:
@@ -4782,6 +4854,7 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
+ | DEALLOCATE_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
@@ -4879,6 +4952,7 @@ keyword:
| PASSWORD {}
| POINT_SYM {}
| POLYGON {}
+ | PREPARE_SYM {}
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}