diff options
author | unknown <sergefp@mysql.com> | 2004-04-05 19:43:37 +0400 |
---|---|---|
committer | unknown <sergefp@mysql.com> | 2004-04-05 19:43:37 +0400 |
commit | ef8a5401f146e0aa17d9cc342c52a82e16e7c6ef (patch) | |
tree | 617aa81bcde9afbb78f590039c16af502a91bb52 | |
parent | b26165d3a2eeb748b9bd5573f9bac7b73d5f9a37 (diff) | |
download | mariadb-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.result | 77 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 75 | ||||
-rw-r--r-- | sql/item.cc | 11 | ||||
-rw-r--r-- | sql/item.h | 1 | ||||
-rw-r--r-- | sql/lex.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/mysqld.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.cc | 23 | ||||
-rw-r--r-- | sql/sql_class.h | 14 | ||||
-rw-r--r-- | sql/sql_lex.h | 6 | ||||
-rw-r--r-- | sql/sql_parse.cc | 86 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 202 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 74 |
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 {} |