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

Manual merge for WL#1622 code, will need fixes

parents 949c01bf 1eb58821
drop table if exists t1,t2;
create table t1
(
a int primary key,
b char(10),
);
insert into t1 values (1,'one');
insert into t1 values (2,'two');
insert into t1 values (3,'three');
insert into t1 values (4,'four');
set @a=2;
prepare stmt1 from 'select * from t1 where a <= ?';
execute stmt1 using @a;
a b
1 one
2 two
set @a=3;
execute stmt1 using @a;
a b
1 one
2 two
3 three
deallocate prepare no_such_statement;
ERROR HY000: Undefined prepared statement
execute stmt1;
ERROR HY000: Wrong arguments to mysql_execute
prepare stmt2 from 'prepare nested_stmt from "select 1"';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '"select 1"' at line 1
prepare stmt2 from 'execute stmt1';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'stmt1' at line 1
prepare stmt2 from 'deallocate prepare z';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'z' at line 1
prepare stmt3 from 'insert into t1 values (?,?)';
set @arg1=5, @arg2='five';
execute stmt3 using @arg1, @arg2;
select * from t1 where a>3;
a b
4 four
5 five
prepare stmt4 from 'update t1 set a=? where b=?';
set @arg1=55, @arg2='five';
execute stmt4 using @arg1, @arg2;
select * from t1 where a>3;
a b
4 four
55 five
prepare stmt4 from 'create table t2 (a int)';
execute stmt4;
prepare stmt4 from 'drop table t2';
execute stmt4;
execute stmt4;
ERROR 42S02: Unknown table 't2'
prepare stmt5 from 'select ? + a from t1';
set @a=1;
execute stmt5 using @a;
? + a
2
3
4
5
56
execute stmt5 using @no_such_var;
? + a
NULL
NULL
NULL
NULL
NULL
set @nullvar=1;
set @nullvar=NULL;
execute stmt5 using @nullvar;
? + a
NULL
NULL
NULL
NULL
NULL
set @nullvar2=NULL;
execute stmt5 using @nullvar2;
? + a
NULL
NULL
NULL
NULL
NULL
prepare stmt6 from 'select 1; select2';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '; select2' at line 1
prepare stmt6 from 'insert into t1 values (5,"five"); select2';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '; select2' at line 1
explain prepare stmt6 from 'insert into t1 values (5,"five"); select2';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'from 'insert into t1 values (5,"five"); select2'' at line 1
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=1;
set @nullvar=NULL;
execute stmt5 using @nullvar;
set @nullvar2=NULL;
execute stmt5 using @nullvar2;
# Check that multiple SQL statements are disabled inside PREPARE
--error 1064
prepare stmt6 from 'select 1; select2';
--error 1064
prepare stmt6 from 'insert into t1 values (5,"five"); select2';
# This shouldn't parse
--error 1064
explain prepare stmt6 from 'insert into t1 values (5,"five"); select2';
drop table t1;
......@@ -644,10 +644,10 @@ void Item_param::set_double(double value)
}
void Item_param::set_value(const char *str, uint length)
void Item_param::set_value(const char *str, uint length, CHARSET_INFO *ci)
{
DBUG_ENTER("Item_param::set_value");
str_value.copy(str,length,default_charset());
str_value.copy(str,length,ci);
item_type= STRING_ITEM;
value_is_set= 1;
maybe_null= 0;
......@@ -655,6 +655,11 @@ void Item_param::set_value(const char *str, uint length)
DBUG_VOID_RETURN;
}
void Item_param::set_value(const char *str, uint length)
{
set_value(str, length, default_charset());
}
void Item_param::set_time(TIME *tm, timestamp_type type)
{
......
......@@ -413,6 +413,7 @@ class Item_param :public Item
void set_int(longlong i);
void set_double(double i);
void set_value(const char *str, uint length);
void set_value(const char *str, uint length, CHARSET_INFO *ci);
void set_long_str(const char *str, ulong length);
void set_long_binary(const char *str, ulong length);
void set_longdata(const char *str, ulong length);
......
......@@ -133,6 +133,7 @@ static SYMBOL symbols[] = {
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
{ "DEFAULT", SYM(DEFAULT)},
......@@ -324,6 +325,7 @@ static SYMBOL symbols[] = {
{ "POINT", SYM(POINT_SYM)},
{ "POLYGON", SYM(POLYGON)},
{ "PRECISION", SYM(PRECISION)},
{ "PREPARE", SYM(PREPARE_SYM)},
{ "PREV", SYM(PREV_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)},
......
......@@ -634,8 +634,10 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length);
int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
LEX_STRING *name=NULL);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
......
......@@ -4822,6 +4822,9 @@ struct show_var_st status_vars[]= {
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
{"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG},
{"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG},
{"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE), SHOW_LONG},
{"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE), SHOW_LONG},
{"Com_dealloc_sql", (char*) (com_stat+(uint) SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG},
{"Connections", (char*) &thread_id, SHOW_LONG_CONST},
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
......
......@@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0));
}
/****************************************************************************
** Thread specific functions
****************************************************************************/
......@@ -1199,6 +1198,7 @@ Statement::Statement(THD *thd)
query_length(0),
free_list(0)
{
name.str= NULL;
init_sql_alloc(&mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
......@@ -1282,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key)
delete (Statement *) key;
}
static byte *get_stmt_name_hash_key(Statement *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->name.length;
return (byte*) entry->name.str;
}
C_MODE_END
Statement_map::Statement_map() :
last_found_statement(0)
{
enum { START_HASH_SIZE = 16 };
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
enum
{
START_STMT_HASH_SIZE = 16,
START_NAME_HASH_SIZE = 16
};
hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0,
get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0));
hash_init(&names_hash, &my_charset_bin, START_NAME_HASH_SIZE, 0, 0,
(hash_get_key) get_stmt_name_hash_key,
NULL,MYF(0));
}
int Statement_map::insert(Statement *statement)
{
int rc= my_hash_insert(&st_hash, (byte *) statement);
if (rc == 0)
last_found_statement= statement;
if (statement->name.str)
{
/*
If there is a statement with the same name, remove it. It is ok to
remove old and fail to insert new one at the same time.
*/
Statement *old_stmt;
if ((old_stmt= find_by_name(&statement->name)))
erase(old_stmt);
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
hash_delete(&st_hash, (byte*)statement);
}
return rc;
}
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
......
......@@ -456,6 +456,7 @@ class Statement
*/
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 @@ class Statement
/*
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 @@ class Statement_map
{
if (statement == last_found_statement)
last_found_statement= 0;
if (statement->name.str)
{
hash_delete(&names_hash, (byte *) statement);
}
hash_delete(&st_hash, (byte *) statement);
}
~Statement_map()
{
hash_free(&st_hash);
hash_free(&names_hash);
}
private:
HASH st_hash;
HASH names_hash;
Statement *last_found_statement;
};
......
......@@ -76,6 +76,7 @@ enum enum_sql_command {
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
/* This should be the last !!! */
SQLCOM_END
};
......@@ -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)
{
......
......@@ -1964,7 +1964,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))))
......
This diff is collapsed.
......@@ -432,6 +432,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MEDIUMTEXT
%token NUMERIC_SYM
%token PRECISION
%token PREPARE_SYM
%token DEALLOCATE_SYM
%token QUICK
%token REAL
%token SIGNED_SYM
......@@ -724,6 +726,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe
prepare execute deallocate
END_OF_INPUT
%type <NONE>
......@@ -760,10 +763,12 @@ verb_clause:
| checksum
| commit
| create
| deallocate
| delete
| describe
| do
| drop
| execute
| flush
| grant
| handler
......@@ -775,6 +780,7 @@ verb_clause:
| optimize
| keycache
| preload
| prepare
| purge
| rename
| repair
......@@ -795,6 +801,71 @@ verb_clause:
| use
;
deallocate:
DEALLOCATE_SYM PREPARE_SYM ident
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
lex->prepared_stmt_name= $3;
};
prepare:
PREPARE_SYM ident FROM TEXT_STRING_sys
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_PREPARE;
lex->prepared_stmt_name= $2;
lex->prepared_stmt_code= $4;
};
execute:
EXECUTE_SYM ident
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_EXECUTE;
lex->prepared_stmt_name= $2;
}
execute_using
{}
;
execute_using:
/* nothing */
| USING execute_var_list
;
execute_var_list:
execute_var_list ',' execute_var_ident
| execute_var_ident
;
execute_var_ident: '@' ident_or_text
{
LEX *lex=Lex;
LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
YYABORT;
}
;
/* help */
help:
......@@ -4900,6 +4971,7 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
| DEALLOCATE_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
......@@ -4998,6 +5070,7 @@ keyword:
| PASSWORD {}
| POINT_SYM {}
| POLYGON {}
| PREPARE_SYM {}
| PREV_SYM {}
| PROCESS {}
| 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