Commit 8cc8b0ea authored by konstantin@oak.local's avatar konstantin@oak.local

Desperate attempt to push part of prepared statements cleanup which was

reviewed in Saint-Petersbourg (including post-review fixes).
parent 0a27eef8
...@@ -714,6 +714,7 @@ extern void my_free_lock(byte *ptr,myf flags); ...@@ -714,6 +714,7 @@ extern void my_free_lock(byte *ptr,myf flags);
#define my_free_lock(A,B) my_free((A),(B)) #define my_free_lock(A,B) my_free((A),(B))
#endif #endif
#define alloc_root_inited(A) ((A)->min_malloc != 0) #define alloc_root_inited(A) ((A)->min_malloc != 0)
#define clear_alloc_root(A) bzero((void *) (A), sizeof(MEM_ROOT))
extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size, extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
uint pre_alloc_size); uint pre_alloc_size);
extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size); extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size);
......
...@@ -375,9 +375,9 @@ class Item_param :public Item ...@@ -375,9 +375,9 @@ class Item_param :public Item
bool get_time(TIME *tm); bool get_time(TIME *tm);
void reset() {} void reset() {}
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
void (*setup_param_func)(Item_param *param, uchar **pos); void (*set_param_func)(Item_param *param, uchar **pos);
#else #else
void (*setup_param_func)(Item_param *param, uchar **pos, ulong data_len); void (*set_param_func)(Item_param *param, uchar **pos, ulong data_len);
#endif #endif
enum Item_result result_type () const enum Item_result result_type () const
{ return item_result_type; } { return item_result_type; }
......
...@@ -620,14 +620,13 @@ int mysqld_show_column_types(THD *thd); ...@@ -620,14 +620,13 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text); int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */ /* sql_prepare.cc */
bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length);
void mysql_stmt_execute(THD *thd, char *packet); void mysql_stmt_execute(THD *thd, char *packet);
void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
List<Item> &values, ulong counter); List<Item> &values, ulong counter);
void setup_param_functions(Item_param *param, uchar param_type);
/* sql_error.cc */ /* sql_error.cc */
MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code,
......
...@@ -178,8 +178,3 @@ char *net_store_data(char *to,const char *from, uint length); ...@@ -178,8 +178,3 @@ char *net_store_data(char *to,const char *from, uint length);
char *net_store_data(char *to,int32 from); char *net_store_data(char *to,int32 from);
char *net_store_data(char *to,longlong from); char *net_store_data(char *to,longlong from);
#ifdef EMBEDDED_LIBRARY
bool setup_params_data(struct st_prep_stmt *stmt);
bool setup_params_data_withlog(struct st_prep_stmt *stmt);
#endif
...@@ -145,8 +145,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), ...@@ -145,8 +145,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
init(); init();
/* Initialize sub structures */ /* Initialize sub structures */
bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); clear_alloc_root(&transaction.mem_root);
bzero((char*) &warn_root,sizeof(warn_root));
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0; user_connect=(USER_CONN *)0;
hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0, hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
...@@ -331,7 +330,7 @@ THD::~THD() ...@@ -331,7 +330,7 @@ THD::~THD()
dbug_sentry = THD_SENTRY_GONE; dbug_sentry = THD_SENTRY_GONE;
#endif #endif
/* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */ /* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */
init_alloc_root(&stmt_backup.mem_root, 0, 0); clear_alloc_root(&stmt_backup.mem_root);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -1185,10 +1184,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) ...@@ -1185,10 +1184,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
Statement::Statement(THD *thd) Statement::Statement(THD *thd)
:id(++thd->statement_id_counter), :id(++thd->statement_id_counter),
query_id(thd->query_id),
set_query_id(1), set_query_id(1),
allow_sum_func(0), allow_sum_func(0),
command(thd->command),
lex(&main_lex), lex(&main_lex),
query(0), query(0),
query_length(0), query_length(0),
...@@ -1207,10 +1204,8 @@ Statement::Statement(THD *thd) ...@@ -1207,10 +1204,8 @@ Statement::Statement(THD *thd)
Statement::Statement() Statement::Statement()
:id(0), :id(0),
query_id(0), /* initialized later */
set_query_id(1), set_query_id(1),
allow_sum_func(0), /* initialized later */ allow_sum_func(0), /* initialized later */
command(COM_SLEEP), /* initialized later */
lex(&main_lex), lex(&main_lex),
query(0), /* these two are set */ query(0), /* these two are set */
query_length(0), /* in alloc_query() */ query_length(0), /* in alloc_query() */
...@@ -1229,15 +1224,11 @@ Statement::Type Statement::type() const ...@@ -1229,15 +1224,11 @@ Statement::Type Statement::type() const
void Statement::set_statement(Statement *stmt) void Statement::set_statement(Statement *stmt)
{ {
id= stmt->id; id= stmt->id;
query_id= stmt->query_id;
set_query_id= stmt->set_query_id; set_query_id= stmt->set_query_id;
allow_sum_func= stmt->allow_sum_func; allow_sum_func= stmt->allow_sum_func;
command= stmt->command;
lex= stmt->lex; lex= stmt->lex;
query= stmt->query; query= stmt->query;
query_length= stmt->query_length; query_length= stmt->query_length;
free_list= stmt->free_list;
mem_root= stmt->mem_root;
} }
......
...@@ -434,15 +434,6 @@ class Statement ...@@ -434,15 +434,6 @@ class Statement
*/ */
ulong id; ulong id;
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
ID is automatically generated from mutex-protected counter.
It's used in handler code for various purposes: to check which columns
from table are necessary for this select, to check if it's necessary to
update auto-updatable fields (like auto_increment and timestamp).
*/
ulong query_id;
/* /*
- if set_query_id=1, we set field->query_id for all fields. In that case - if set_query_id=1, we set field->query_id for all fields. In that case
field list can not contain duplicates. field list can not contain duplicates.
...@@ -461,11 +452,6 @@ class Statement ...@@ -461,11 +452,6 @@ class Statement
See item_sum.cc for details. See item_sum.cc for details.
*/ */
bool allow_sum_func; bool allow_sum_func;
/*
Type of current query: COM_PREPARE, COM_QUERY, etc. Set from
first byte of the packet in do_command()
*/
enum enum_server_command command;
LEX *lex; // parse tree descriptor LEX *lex; // parse tree descriptor
/* /*
...@@ -676,6 +662,11 @@ class THD :public ilink, ...@@ -676,6 +662,11 @@ class THD :public ilink,
uint dbug_sentry; // watch out for memory corruption uint dbug_sentry; // watch out for memory corruption
#endif #endif
struct st_my_thread_var *mysys_var; struct st_my_thread_var *mysys_var;
/*
Type of current query: COM_PREPARE, COM_QUERY, etc. Set from
first byte of the packet in do_command()
*/
enum enum_server_command command;
uint32 server_id; uint32 server_id;
uint32 file_id; // for LOAD DATA INFILE uint32 file_id; // for LOAD DATA INFILE
/* /*
...@@ -751,6 +742,15 @@ class THD :public ilink, ...@@ -751,6 +742,15 @@ class THD :public ilink,
List <MYSQL_ERROR> warn_list; List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count; uint total_warn_count;
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
ID is automatically generated from mutex-protected counter.
It's used in handler code for various purposes: to check which columns
from table are necessary for this select, to check if it's necessary to
update auto-updatable fields (like auto_increment and timestamp).
*/
ulong query_id;
ulong warn_id, version, options, thread_id, col_access; ulong warn_id, version, options, thread_id, col_access;
/* Statement id is thread-wide. This counter is used to generate ids */ /* Statement id is thread-wide. This counter is used to generate ids */
......
...@@ -552,7 +552,7 @@ typedef struct st_lex ...@@ -552,7 +552,7 @@ typedef struct st_lex
List<Item> *insert_list,field_list,value_list; List<Item> *insert_list,field_list,value_list;
List<List_item> many_values; List<List_item> many_values;
List<set_var_base> var_list; List<set_var_base> var_list;
List<Item> param_list; List<Item_param> param_list;
SQL_LIST proc_list, auxilliary_table_list, save_list; SQL_LIST proc_list, auxilliary_table_list, save_list;
TYPELIB *interval; TYPELIB *interval;
create_field *last_field; create_field *last_field;
...@@ -577,7 +577,6 @@ typedef struct st_lex ...@@ -577,7 +577,6 @@ typedef struct st_lex
uint uint_geom_type; uint uint_geom_type;
uint grant, grant_tot_col, which_columns; uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option; uint fk_delete_opt, fk_update_opt, fk_match_option;
uint param_count;
uint slave_thd_opt; uint slave_thd_opt;
uint8 describe; uint8 describe;
bool drop_if_exists, drop_temporary, local_file; bool drop_if_exists, drop_temporary, local_file;
......
...@@ -1232,10 +1232,34 @@ bool do_command(THD *thd) ...@@ -1232,10 +1232,34 @@ bool do_command(THD *thd)
command_name[command])); command_name[command]));
} }
net->read_timeout=old_timeout; // restore it net->read_timeout=old_timeout; // restore it
/*
packet_length contains length of data, as it was stored in packet
header. In case of malformed header, packet_length can be zero.
If packet_length is not zero, my_net_read ensures that this number
of bytes was actually read from network. Additionally my_net_read
sets packet[packet_length]= 0 (thus if packet_length == 0,
command == packet[0] == COM_SLEEP).
In dispatch_command packet[packet_length] points beyond the end of packet.
*/
DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
} }
#endif /* EMBEDDED_LIBRARY */ #endif /* EMBEDDED_LIBRARY */
/*
Perform one connection-level (COM_XXXX) command.
SYNOPSIS
dispatch_command()
thd connection handle
command type of command to perform
packet data for the command, packet is always null-terminated
packet_length length of packet + 1 (to show that data is
null-terminated) except for COM_SLEEP, where it
can be zero.
RETURN VALUE
0 ok
1 request of thread shutdown, i. e. if command is
COM_QUIT/COM_SHUTDOWN
*/
bool dispatch_command(enum enum_server_command command, THD *thd, bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length) char* packet, uint packet_length)
......
...@@ -86,16 +86,17 @@ class Prepared_statement: public Statement ...@@ -86,16 +86,17 @@ class Prepared_statement: public Statement
{ {
public: public:
THD *thd; THD *thd;
Item_param **param; /* array of all placeholders */ Item_param **param_array;
uint param_count; uint param_count;
uint last_errno; uint last_errno;
char last_error[MYSQL_ERRMSG_SIZE]; char last_error[MYSQL_ERRMSG_SIZE];
bool error_in_prepare, long_data_used; bool get_longdata_error;
bool long_data_used;
bool log_full_query; bool log_full_query;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
bool (*setup_params)(Prepared_statement *st, uchar *pos, uchar *read_pos); bool (*set_params)(Prepared_statement *st, uchar *pos, uchar *read_pos);
#else #else
bool (*setup_params_data)(Prepared_statement *st); bool (*set_params_data)(Prepared_statement *st);
#endif #endif
public: public:
Prepared_statement(THD *thd_arg); Prepared_statement(THD *thd_arg);
...@@ -117,13 +118,14 @@ inline bool is_param_null(const uchar *pos, ulong param_no) ...@@ -117,13 +118,14 @@ inline bool is_param_null(const uchar *pos, ulong param_no)
enum { STMT_QUERY_LOG_LENGTH= 8192 }; enum { STMT_QUERY_LOG_LENGTH= 8192 };
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
#define SETUP_PARAM_FUNCTION(fn_name) \ #define SET_PARAM_FUNCTION(fn_name) \
static void fn_name(Item_param *param, uchar **pos, ulong data_len) static void fn_name(Item_param *param, uchar **pos, ulong data_len)
#else #else
#define SETUP_PARAM_FUNCTION(fn_name) \ #define SET_PARAM_FUNCTION(fn_name) \
static void fn_name(Item_param *param, uchar **pos) static void fn_name(Item_param *param, uchar **pos)
#endif #endif
enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR };
/* /*
Seek prepared statement in statement map by id: returns zero if statement Seek prepared statement in statement map by id: returns zero if statement
...@@ -131,13 +133,15 @@ static void fn_name(Item_param *param, uchar **pos) ...@@ -131,13 +133,15 @@ static void fn_name(Item_param *param, uchar **pos)
*/ */
static Prepared_statement * static Prepared_statement *
find_prepared_statement(THD *thd, ulong id, const char *where) find_prepared_statement(THD *thd, ulong id, const char *where,
enum enum_send_error se)
{ {
Statement *stmt= thd->stmt_map.find(id); Statement *stmt= thd->stmt_map.find(id);
if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT) if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
{ {
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where); my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
if (se == SEND_ERROR)
send_error(thd); send_error(thd);
return 0; return 0;
} }
...@@ -154,11 +158,11 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) ...@@ -154,11 +158,11 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{ {
NET *net= &stmt->thd->net; NET *net= &stmt->thd->net;
char buff[9]; char buff[9];
buff[0]= 0; buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id); int4store(buff+1, stmt->id);
int2store(buff+5, columns); int2store(buff+5, columns);
int2store(buff+7, stmt->param_count); int2store(buff+7, stmt->param_count);
/* This should be fixed to work with prepared statements */ /* TODO: send types of placeholders here */
return (my_net_write(net, buff, sizeof(buff)) || net_flush(net)); return (my_net_write(net, buff, sizeof(buff)) || net_flush(net));
} }
#else #else
...@@ -177,8 +181,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, ...@@ -177,8 +181,8 @@ static bool send_prep_stmt(Prepared_statement *stmt,
/* /*
Read the length of the parameter data and retun back to Read the length of the parameter data and return back to
caller by positing the pointer to param data caller by positing the pointer to param data.
*/ */
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
...@@ -208,49 +212,49 @@ static ulong get_param_length(uchar **packet) ...@@ -208,49 +212,49 @@ static ulong get_param_length(uchar **packet)
#endif /*!EMBEDDED_LIBRARY*/ #endif /*!EMBEDDED_LIBRARY*/
/* /*
Setup param conversion routines Data conversion routines
SYNOPSIS
setup_param_xx() set_param_xx()
param Parameter Item param parameter item
pos Input data buffer pos input data buffer
len length of data in the buffer
All these functions reads the data from pos and sets up that data All these functions read the data from pos, convert it to requested type
through 'param' and advances the buffer position to predifined and assign to param; pos is advanced to predefined length.
length position.
Make a note that the NULL handling is examined at first execution Make a note that the NULL handling is examined at first execution
(i.e. when input types altered) and for all subsequent executions (i.e. when input types altered) and for all subsequent executions
we don't read any values for this. we don't read any values for this.
RETURN VALUES RETURN VALUE
none
*/ */
SETUP_PARAM_FUNCTION(setup_param_tiny) SET_PARAM_FUNCTION(set_param_tiny)
{ {
param->set_int((longlong)(**pos)); param->set_int((longlong)(**pos));
*pos+= 1; *pos+= 1;
} }
SETUP_PARAM_FUNCTION(setup_param_short) SET_PARAM_FUNCTION(set_param_short)
{ {
param->set_int((longlong)sint2korr(*pos)); param->set_int((longlong)sint2korr(*pos));
*pos+= 2; *pos+= 2;
} }
SETUP_PARAM_FUNCTION(setup_param_int32) SET_PARAM_FUNCTION(set_param_int32)
{ {
param->set_int((longlong)sint4korr(*pos)); param->set_int((longlong)sint4korr(*pos));
*pos+= 4; *pos+= 4;
} }
SETUP_PARAM_FUNCTION(setup_param_int64) SET_PARAM_FUNCTION(set_param_int64)
{ {
param->set_int((longlong)sint8korr(*pos)); param->set_int((longlong)sint8korr(*pos));
*pos+= 8; *pos+= 8;
} }
SETUP_PARAM_FUNCTION(setup_param_float) SET_PARAM_FUNCTION(set_param_float)
{ {
float data; float data;
float4get(data,*pos); float4get(data,*pos);
...@@ -258,7 +262,7 @@ SETUP_PARAM_FUNCTION(setup_param_float) ...@@ -258,7 +262,7 @@ SETUP_PARAM_FUNCTION(setup_param_float)
*pos+= 4; *pos+= 4;
} }
SETUP_PARAM_FUNCTION(setup_param_double) SET_PARAM_FUNCTION(set_param_double)
{ {
double data; double data;
float8get(data,*pos); float8get(data,*pos);
...@@ -266,7 +270,7 @@ SETUP_PARAM_FUNCTION(setup_param_double) ...@@ -266,7 +270,7 @@ SETUP_PARAM_FUNCTION(setup_param_double)
*pos+= 8; *pos+= 8;
} }
SETUP_PARAM_FUNCTION(setup_param_time) SET_PARAM_FUNCTION(set_param_time)
{ {
ulong length; ulong length;
...@@ -290,11 +294,11 @@ SETUP_PARAM_FUNCTION(setup_param_time) ...@@ -290,11 +294,11 @@ SETUP_PARAM_FUNCTION(setup_param_time)
*pos+= length; *pos+= length;
} }
SETUP_PARAM_FUNCTION(setup_param_datetime) SET_PARAM_FUNCTION(set_param_datetime)
{ {
uint length= get_param_length(pos); uint length;
if (length) if ((length= get_param_length(pos)))
{ {
uchar *to= *pos; uchar *to= *pos;
TIME tm; TIME tm;
...@@ -320,7 +324,7 @@ SETUP_PARAM_FUNCTION(setup_param_datetime) ...@@ -320,7 +324,7 @@ SETUP_PARAM_FUNCTION(setup_param_datetime)
*pos+= length; *pos+= length;
} }
SETUP_PARAM_FUNCTION(setup_param_date) SET_PARAM_FUNCTION(set_param_date)
{ {
ulong length; ulong length;
...@@ -342,55 +346,55 @@ SETUP_PARAM_FUNCTION(setup_param_date) ...@@ -342,55 +346,55 @@ SETUP_PARAM_FUNCTION(setup_param_date)
*pos+= length; *pos+= length;
} }
SETUP_PARAM_FUNCTION(setup_param_str) SET_PARAM_FUNCTION(set_param_str)
{ {
ulong len= get_param_length(pos); ulong len= get_param_length(pos);
param->set_value((const char *)*pos, len); param->set_value((const char *)*pos, len);
*pos+= len; *pos+= len;
} }
void setup_param_functions(Item_param *param, uchar param_type) static void setup_one_conversion_function(Item_param *param, uchar param_type)
{ {
switch (param_type) { switch (param_type) {
case FIELD_TYPE_TINY: case FIELD_TYPE_TINY:
param->setup_param_func= setup_param_tiny; param->set_param_func= set_param_tiny;
param->item_result_type= INT_RESULT; param->item_result_type= INT_RESULT;
break; break;
case FIELD_TYPE_SHORT: case FIELD_TYPE_SHORT:
param->setup_param_func= setup_param_short; param->set_param_func= set_param_short;
param->item_result_type= INT_RESULT; param->item_result_type= INT_RESULT;
break; break;
case FIELD_TYPE_LONG: case FIELD_TYPE_LONG:
param->setup_param_func= setup_param_int32; param->set_param_func= set_param_int32;
param->item_result_type= INT_RESULT; param->item_result_type= INT_RESULT;
break; break;
case FIELD_TYPE_LONGLONG: case FIELD_TYPE_LONGLONG:
param->setup_param_func= setup_param_int64; param->set_param_func= set_param_int64;
param->item_result_type= INT_RESULT; param->item_result_type= INT_RESULT;
break; break;
case FIELD_TYPE_FLOAT: case FIELD_TYPE_FLOAT:
param->setup_param_func= setup_param_float; param->set_param_func= set_param_float;
param->item_result_type= REAL_RESULT; param->item_result_type= REAL_RESULT;
break; break;
case FIELD_TYPE_DOUBLE: case FIELD_TYPE_DOUBLE:
param->setup_param_func= setup_param_double; param->set_param_func= set_param_double;
param->item_result_type= REAL_RESULT; param->item_result_type= REAL_RESULT;
break; break;
case FIELD_TYPE_TIME: case FIELD_TYPE_TIME:
param->setup_param_func= setup_param_time; param->set_param_func= set_param_time;
param->item_result_type= STRING_RESULT; param->item_result_type= STRING_RESULT;
break; break;
case FIELD_TYPE_DATE: case FIELD_TYPE_DATE:
param->setup_param_func= setup_param_date; param->set_param_func= set_param_date;
param->item_result_type= STRING_RESULT; param->item_result_type= STRING_RESULT;
break; break;
case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP:
param->setup_param_func= setup_param_datetime; param->set_param_func= set_param_datetime;
param->item_result_type= STRING_RESULT; param->item_result_type= STRING_RESULT;
break; break;
default: default:
param->setup_param_func= setup_param_str; param->set_param_func= set_param_str;
param->item_result_type= STRING_RESULT; param->item_result_type= STRING_RESULT;
} }
} }
...@@ -405,9 +409,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, ...@@ -405,9 +409,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
uchar *read_pos) uchar *read_pos)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
List<Item> &params= stmt->lex->param_list; Item_param **begin= stmt->param_array;
List_iterator<Item> param_iterator(params); Item_param **end= begin + stmt->param_count;
Item_param *param; uint32 length= 0;
String str, query; String str, query;
const String *res; const String *res;
...@@ -417,16 +421,14 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, ...@@ -417,16 +421,14 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
if (query.copy(stmt->query, stmt->query_length, default_charset_info)) if (query.copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1); DBUG_RETURN(1);
ulong param_no= 0; for (Item_param **it= begin; it < end; ++it)
uint32 length= 0;
while ((param= (Item_param *)param_iterator++))
{ {
Item_param *param= *it;
if (param->long_data_supplied) if (param->long_data_supplied)
res= param->query_val_str(&str); res= param->query_val_str(&str);
else else
{ {
if (is_param_null(pos,param_no)) if (is_param_null(pos, it - begin))
{ {
param->maybe_null= param->null_value= 1; param->maybe_null= param->null_value= 1;
res= &my_null_string; res= &my_null_string;
...@@ -434,7 +436,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, ...@@ -434,7 +436,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
else else
{ {
param->maybe_null= param->null_value= 0; param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&read_pos); param->set_param_func(param, &read_pos);
res= param->query_val_str(&str); res= param->query_val_str(&str);
} }
} }
...@@ -442,7 +444,6 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, ...@@ -442,7 +444,6 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
DBUG_RETURN(1); DBUG_RETURN(1);
length+= res->length()-1; length+= res->length()-1;
param_no++;
} }
if (alloc_query(thd, (char *)query.ptr(), query.length()+1)) if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -454,41 +455,35 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, ...@@ -454,41 +455,35 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
static bool insert_params(Prepared_statement *stmt, uchar *pos, static bool insert_params(Prepared_statement *stmt, uchar *pos,
uchar *read_pos) uchar *read_pos)
{ {
List<Item> &params= stmt->lex->param_list; Item_param **begin= stmt->param_array;
List_iterator<Item> param_iterator(params); Item_param **end= begin + stmt->param_count;
Item_param *param;
ulong param_no= 0;
DBUG_ENTER("insert_params"); DBUG_ENTER("insert_params");
while ((param= (Item_param *)param_iterator++)) for (Item_param **it= begin; it < end; ++it)
{ {
Item_param *param= *it;
if (!param->long_data_supplied) if (!param->long_data_supplied)
{ {
if (is_param_null(pos,param_no)) if (is_param_null(pos, it - begin))
param->maybe_null= param->null_value= 1; param->maybe_null= param->null_value= 1;
else else
{ {
param->maybe_null= param->null_value= 0; param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&read_pos); param->set_param_func(param, &read_pos);
} }
} }
param_no++;
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
static bool setup_params_data(Prepared_statement *stmt) static bool setup_conversion_functions(Prepared_statement *stmt,
uchar **data)
{ {
List<Item> &params= stmt->lex->param_list; /* skip null bits */
List_iterator<Item> param_iterator(params); uchar *read_pos= *data + (stmt->param_count+7) / 8;
Item_param *param;
uchar *pos= (uchar*) stmt->thd->net.read_pos + 1 +
MYSQL_STMT_HEADER; //skip header
uchar *read_pos= pos+(stmt->param_count+7) / 8; //skip null bits
DBUG_ENTER("setup_params_data"); DBUG_ENTER("setup_conversion_functions");
if (*read_pos++) //types supplied / first execute if (*read_pos++) //types supplied / first execute
{ {
...@@ -496,31 +491,32 @@ static bool setup_params_data(Prepared_statement *stmt) ...@@ -496,31 +491,32 @@ static bool setup_params_data(Prepared_statement *stmt)
First execute or types altered by the client, setup the First execute or types altered by the client, setup the
conversion routines for all parameters (one time) conversion routines for all parameters (one time)
*/ */
while ((param= (Item_param *)param_iterator++)) Item_param **it= stmt->param_array;
Item_param **end= it + stmt->param_count;
for (; it < end; ++it)
{ {
setup_param_functions(param,*read_pos); setup_one_conversion_function(*it, *read_pos);
read_pos+= 2; read_pos+= 2;
} }
param_iterator.rewind();
} }
stmt->setup_params(stmt,pos,read_pos); *data= read_pos;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
#else #else
bool setup_params_data(Prepared_statement *stmt) static bool emb_insert_params(Prepared_statement *stmt)
{ {
List<Item> &params= stmt->lex->param_list; Item_param **it= stmt->param_array;
List_iterator<Item> param_iterator(params); Item_param **end= it + stmt->param_count;
Item_param *param;
MYSQL_BIND *client_param= stmt->thd->client_params; MYSQL_BIND *client_param= stmt->thd->client_params;
DBUG_ENTER("setup_params_data"); DBUG_ENTER("emb_insert_params");
for (;(param= (Item_param *)param_iterator++); client_param++) for (; it < end; ++it, ++client_param)
{ {
setup_param_functions(param, client_param->buffer_type); Item_param *param= *it;
setup_one_conversion_function(param, client_param->buffer_type);
if (!param->long_data_supplied) if (!param->long_data_supplied)
{ {
if (*client_param->is_null) if (*client_param->is_null)
...@@ -529,7 +525,7 @@ bool setup_params_data(Prepared_statement *stmt) ...@@ -529,7 +525,7 @@ bool setup_params_data(Prepared_statement *stmt)
{ {
uchar *buff= (uchar*)client_param->buffer; uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0; param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&buff, param->set_param_func(param, &buff,
client_param->length ? client_param->length ?
*client_param->length : *client_param->length :
client_param->buffer_length); client_param->buffer_length);
...@@ -539,27 +535,27 @@ bool setup_params_data(Prepared_statement *stmt) ...@@ -539,27 +535,27 @@ bool setup_params_data(Prepared_statement *stmt)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
bool setup_params_data_withlog(Prepared_statement *stmt)
static bool emb_insert_params_withlog(Prepared_statement *stmt)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
List<Item> &params= stmt->lex->param_list; Item_param **it= stmt->param_array;
List_iterator<Item> param_iterator(params); Item_param **end= it + stmt->param_count;
Item_param *param;
MYSQL_BIND *client_param= thd->client_params; MYSQL_BIND *client_param= thd->client_params;
String str, query; String str, query;
const String *res; const String *res;
uint32 length= 0;
DBUG_ENTER("setup_params_data_withlog"); DBUG_ENTER("emb_insert_params_withlog");
if (query.copy(stmt->query, stmt->query_length, default_charset_info)) if (query.copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1); DBUG_RETURN(1);
uint32 length= 0; for (; it < end; ++it, ++client_param)
for (;(param= (Item_param *)param_iterator++); client_param++)
{ {
setup_param_functions(param, client_param->buffer_type); Item_param *param= *it;
setup_one_conversion_function(param, client_param->buffer_type);
if (param->long_data_supplied) if (param->long_data_supplied)
res= param->query_val_str(&str); res= param->query_val_str(&str);
else else
...@@ -573,7 +569,7 @@ bool setup_params_data_withlog(Prepared_statement *stmt) ...@@ -573,7 +569,7 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
{ {
uchar *buff= (uchar*)client_param->buffer; uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0; param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&buff, param->set_param_func(param, &buff,
client_param->length ? client_param->length ?
*client_param->length : *client_param->length :
client_param->buffer_length); client_param->buffer_length);
...@@ -582,7 +578,6 @@ bool setup_params_data_withlog(Prepared_statement *stmt) ...@@ -582,7 +578,6 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
} }
if (query.replace(param->pos_in_query+length, 1, *res)) if (query.replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1); DBUG_RETURN(1);
length+= res->length()-1; length+= res->length()-1;
} }
...@@ -596,11 +591,17 @@ bool setup_params_data_withlog(Prepared_statement *stmt) ...@@ -596,11 +591,17 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
/* /*
Validate the following information for INSERT statement: Validate the following information for INSERT statement:
- field existance - field existence
- fields count - fields count
SYNOPSIS
mysql_test_insert_fields()
RETURN VALUE
0 ok
1 error, sent to the client
-1 error, not sent to client
*/ */
static bool mysql_test_insert_fields(Prepared_statement *stmt, static int mysql_test_insert_fields(Prepared_statement *stmt,
TABLE_LIST *table_list, TABLE_LIST *table_list,
List<Item> &fields, List<Item> &fields,
List<List_item> &values_list) List<List_item> &values_list)
...@@ -630,7 +631,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -630,7 +631,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
if (open_and_lock_tables(thd, table_list)) if (open_and_lock_tables(thd, table_list))
{ {
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1); DBUG_RETURN(-1);
} }
table= table_list->table; table= table_list->table;
...@@ -643,7 +644,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -643,7 +644,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
if (check_insert_fields(thd,table,fields,*values,1)) if (check_insert_fields(thd,table,fields,*values,1))
{ {
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1); DBUG_RETURN(-1);
} }
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
...@@ -658,7 +659,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -658,7 +659,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
ER(ER_WRONG_VALUE_COUNT_ON_ROW), ER(ER_WRONG_VALUE_COUNT_ON_ROW),
MYF(0), counter); MYF(0), counter);
DBUG_RETURN(1); DBUG_RETURN(-1);
} }
} }
} }
...@@ -666,22 +667,23 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -666,22 +667,23 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
{ {
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
} }
if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/* /*
Validate the following information Validate the following information:
UPDATE - set and where clause DELETE - where clause UPDATE - set and where clause
DELETE - where clause
And send update-set clause column list fields info SYNOPSIS
back to client. For DELETE, just validate where clause mysql_test_upd_fields()
and return no fields information back to client. RETURN VALUE
0 success
1 error, sent to client
-1 error, not sent to client
*/ */
static bool mysql_test_upd_fields(Prepared_statement *stmt, static int mysql_test_upd_fields(Prepared_statement *stmt,
TABLE_LIST *table_list, TABLE_LIST *table_list,
List<Item> &fields, List<Item> &values, List<Item> &fields, List<Item> &values,
COND *conds) COND *conds)
...@@ -711,32 +713,32 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt, ...@@ -711,32 +713,32 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt,
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
/* /* TODO: here we should send types of placeholders to the client. */
Currently return only column list info only, and we are not
sending any info on where clause.
*/
if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
err: err:
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1); DBUG_RETURN(-1);
} }
/* /*
Validate the following information: Validate the following information:
SELECT - column list SELECT - column list
- where clause - where clause
- order clause - order clause
- having clause - having clause
- group by clause - group by clause
- if no column spec i.e. '*', then setup all fields - if no column spec i.e. '*', then setup all fields
In case of success, if this query is not EXPLAIN, send column list info
And send column list fields info back to client. back to client.
SYNOPSIS
mysql_test_select_fields()
RETURN VALUE
0 success
1 error, sent to client
-1 error, not sent to client
*/ */
static bool mysql_test_select_fields(Prepared_statement *stmt, static int mysql_test_select_fields(Prepared_statement *stmt,
TABLE_LIST *tables, TABLE_LIST *tables,
uint wild_num, uint wild_num,
List<Item> &fields, COND *conds, List<Item> &fields, COND *conds,
...@@ -748,7 +750,6 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, ...@@ -748,7 +750,6 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
select_result *result= lex->result;
DBUG_ENTER("mysql_test_select_fields"); DBUG_ENTER("mysql_test_select_fields");
...@@ -772,7 +773,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, ...@@ -772,7 +773,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
*/ */
thd->allocate_temporary_memory_pool_for_ps_preparing(); thd->allocate_temporary_memory_pool_for_ps_preparing();
if (open_and_lock_tables(thd, tables)) if (open_and_lock_tables(thd, tables))
{
send_error(thd);
goto err; goto err;
}
if (lex->describe) if (lex->describe)
{ {
...@@ -781,6 +785,7 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, ...@@ -781,6 +785,7 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
} }
else else
{ {
select_result *result= lex->result;
if (!result && !(result= new select_send())) if (!result && !(result= new select_send()))
{ {
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
...@@ -790,7 +795,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, ...@@ -790,7 +795,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
thd->used_tables= 0; // Updated by setup_fields thd->used_tables= 0; // Updated by setup_fields
if (unit->prepare(thd, result, 0)) if (unit->prepare(thd, result, 0))
{
send_error(thd);
goto err_prep; goto err_prep;
}
if (send_prep_stmt(stmt, fields.elements) || if (send_prep_stmt(stmt, fields.elements) ||
thd->protocol_simple.send_fields(&fields, 0) thd->protocol_simple.send_fields(&fields, 0)
...@@ -815,46 +823,45 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, ...@@ -815,46 +823,45 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
/* /*
Send the prepare query results back to client Send the prepare query results back to client
SYNOPSIS
send_prepare_results()
stmt prepared statement
RETURN VALUE
0 success
1 error, sent to client
*/ */
static bool send_prepare_results(Prepared_statement *stmt) static int send_prepare_results(Prepared_statement *stmt)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
enum enum_sql_command sql_command= lex->sql_command; enum enum_sql_command sql_command= lex->sql_command;
int res;
DBUG_ENTER("send_prepare_results"); DBUG_ENTER("send_prepare_results");
DBUG_PRINT("enter",("command: %d, param_count: %ld", DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, lex->param_count)); sql_command, stmt->param_count));
/* Setup prepared stmt */
stmt->param_count= lex->param_count;
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
switch (sql_command) { switch (sql_command) {
case SQLCOM_INSERT: case SQLCOM_INSERT:
if (mysql_test_insert_fields(stmt, tables, lex->field_list, if ((res= mysql_test_insert_fields(stmt, tables, lex->field_list,
lex->many_values)) lex->many_values)))
goto abort; goto error;
break; break;
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, /* XXX: fallthrough */
lex->value_list, select_lex->where))
goto abort;
break;
case SQLCOM_DELETE: case SQLCOM_DELETE:
if (mysql_test_upd_fields(stmt, tables, select_lex->item_list, if ((res= mysql_test_upd_fields(stmt, tables, select_lex->item_list,
lex->value_list, select_lex->where)) lex->value_list, select_lex->where)))
goto abort; goto error;
break; break;
case SQLCOM_SELECT: case SQLCOM_SELECT:
if (mysql_test_select_fields(stmt, tables, select_lex->with_wild, if ((res= mysql_test_select_fields(stmt, tables, select_lex->with_wild,
select_lex->item_list, select_lex->item_list,
select_lex->where, select_lex->where,
select_lex->order_list.elements + select_lex->order_list.elements +
...@@ -864,46 +871,54 @@ static bool send_prepare_results(Prepared_statement *stmt) ...@@ -864,46 +871,54 @@ static bool send_prepare_results(Prepared_statement *stmt)
select_lex->having, select_lex->having,
(ORDER*)lex->proc_list.first, (ORDER*)lex->proc_list.first,
select_lex->options | thd->options, select_lex->options | thd->options,
&(lex->unit), select_lex)) &(lex->unit), select_lex)))
goto abort; goto error;
break; /* Statement and field info has already been sent */
DBUG_RETURN(0);
default: default:
{
/* /*
Rest fall through to default category, no parsing Rest fall through to default category, no parsing
for non-DML statements for non-DML statements
*/ */
if (send_prep_stmt(stmt, 0)) break;
goto abort;
}
} }
DBUG_RETURN(0); DBUG_RETURN(send_prep_stmt(stmt, 0));
abort: error:
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); if (res < 0)
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
/* /*
Initialize parameter items in statement Initialize array of parametes in statement from LEX.
(We need to have quick access to items by number in mysql_send_longdata).
This is to avoid using malloc/realloc in the parser.
*/ */
static bool init_param_items(Prepared_statement *stmt) static bool init_param_array(Prepared_statement *stmt)
{ {
LEX *lex= stmt->lex;
if ((stmt->param_count= lex->param_list.elements))
{
Item_param **to; Item_param **to;
List_iterator<Item_param> param_iterator(lex->param_list);
if (!stmt->param_count) /* Use thd->mem_root as it points at statement mem_root */
stmt->param= (Item_param **)0; stmt->param_array= (Item_param **)
else alloc_root(&stmt->thd->mem_root,
sizeof(Item_param*) * stmt->param_count);
if (!stmt->param_array)
{ {
if (!(stmt->param= to= (Item_param **) send_error(stmt->thd, ER_OUT_OF_RESOURCES);
my_malloc(sizeof(Item_param *)*(stmt->param_count+1),
MYF(MY_WME))))
return 1; return 1;
}
List_iterator<Item> param_iterator(stmt->lex->param_list); for (to= stmt->param_array;
while ((*(to++)= (Item_param *)param_iterator++)); to < stmt->param_array + stmt->param_count;
++to)
{
*to= param_iterator++;
}
} }
return 0; return 0;
} }
...@@ -912,137 +927,104 @@ static bool init_param_items(Prepared_statement *stmt) ...@@ -912,137 +927,104 @@ static bool init_param_items(Prepared_statement *stmt)
/* /*
Parse the query and send the total number of parameters Parse the query and send the total number of parameters
and resultset metadata information back to client (if any), and resultset metadata information back to client (if any),
without executing the query i.e. with out any log/disk without executing the query i.e. without any log/disk
writes. This will allow the queries to be re-executed writes. This will allow the queries to be re-executed
without re-parsing during execute. without re-parsing during execute.
If parameter markers are found in the query, then store If parameter markers are found in the query, then store
the information using Item_param along with maintaining a the information using Item_param along with maintaining a
list in lex->param_list, so that a fast and direct list in lex->param_array, so that a fast and direct
retrieval can be made without going through all field retrieval can be made without going through all field
items. items.
*/ */
bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
{ {
LEX *lex; LEX *lex;
Prepared_statement *stmt= new Prepared_statement(thd); Prepared_statement *stmt= new Prepared_statement(thd);
SELECT_LEX *sl; int error;
DBUG_ENTER("mysql_stmt_prepare"); DBUG_ENTER("mysql_stmt_prepare");
if (stmt == 0) if (stmt == 0)
DBUG_RETURN(0); {
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_VOID_RETURN;
}
if (thd->stmt_map.insert(stmt)) if (thd->stmt_map.insert(stmt))
goto insert_stmt_err; {
delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_VOID_RETURN;
}
thd->stmt_backup.set_statement(thd); thd->stmt_backup.set_statement(thd);
thd->stmt_backup.set_item_arena(thd);
thd->set_statement(stmt); thd->set_statement(stmt);
thd->current_statement= stmt; thd->set_item_arena(stmt);
if (alloc_query(thd, packet, packet_length)) if (alloc_query(thd, packet, packet_length))
goto alloc_query_err; {
stmt->set_statement(thd);
stmt->set_item_arena(thd);
thd->set_statement(&thd->stmt_backup);
thd->set_item_arena(&thd->stmt_backup);
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_VOID_RETURN;
}
mysql_log.write(thd, COM_PREPARE, "%s", packet); mysql_log.write(thd, COM_PREPARE, "%s", packet);
thd->current_statement= stmt;
lex= lex_start(thd, (uchar *) thd->query, thd->query_length); lex= lex_start(thd, (uchar *) thd->query, thd->query_length);
mysql_init_query(thd); mysql_init_query(thd);
lex->safe_to_cache_query= 0; lex->safe_to_cache_query= 0;
lex->param_count= 0;
if (yyparse((void *)thd) || thd->is_fatal_error || send_prepare_results(stmt)) error= yyparse((void *)thd) || thd->is_fatal_error ||
goto yyparse_err; init_param_array(stmt) ||
send_prepare_results(stmt);
lex_end(lex);
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR); my_pthread_setprio(pthread_self(),WAIT_PRIOR);
// save WHERE clause pointers to avoid damaging they by optimisation
for (sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
sl->prep_where= sl->where;
}
stmt->set_statement(thd);
thd->set_statement(&thd->stmt_backup);
thd->current_statement= 0;
if (init_param_items(stmt))
goto init_param_err;
stmt->command= COM_EXECUTE; // set it only once here
DBUG_RETURN(0);
yyparse_err:
lex_end(lex); lex_end(lex);
stmt->set_statement(thd); stmt->set_statement(thd);
stmt->set_item_arena(thd);
thd->set_statement(&thd->stmt_backup); thd->set_statement(&thd->stmt_backup);
init_param_err: thd->set_item_arena(&thd->stmt_backup);
alloc_query_err:
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
thd->current_statement= 0;
DBUG_RETURN(1);
insert_stmt_err:
stmt->set_statement(thd);
thd->set_statement(&thd->stmt_backup);
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
thd->current_statement= 0; thd->current_statement= 0;
delete stmt;
DBUG_RETURN(1);
}
/*
Executes previously prepared query
If there is any parameters (stmt->param_count), then replace
markers with the data supplied from client, and then
execute the query
*/
void mysql_stmt_execute(THD *thd, char *packet)
{
ulong stmt_id= uint4korr(packet);
Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_execute");
if (!(stmt= find_prepared_statement(thd, stmt_id, "execute")))
DBUG_VOID_RETURN;
/* Check if we got an error when sending long data */ if (error)
if (stmt->error_in_prepare)
{ {
send_error(thd, stmt->last_errno, stmt->last_error); /* Statement map deletes statement on erase */
DBUG_VOID_RETURN; thd->stmt_map.erase(stmt);
/* error is sent inside yyparse/send_prepare_results */
} }
else
stmt->query_id= thd->query_id; {
thd->stmt_backup.set_statement(thd); SELECT_LEX *sl= stmt->lex->all_selects_list;
thd->set_statement(stmt);
thd->free_list= 0;
/* /*
To make sure that all runtime data is stored in its own memory root and Save WHERE clause pointers, because they may be changed during query
does not interfere with data possibly present in thd->mem_root. optimisation.
This root is cleaned up in the end of execution.
FIXME: to be replaced with more efficient approach, and verified why we
can not use thd->mem_root safely.
*/ */
init_sql_alloc(&thd->mem_root, for (; sl; sl= sl->next_select_in_list())
thd->variables.query_alloc_block_size, {
thd->variables.query_prealloc_size); sl->prep_where= sl->where;
}
}
DBUG_VOID_RETURN;
}
/* Reinit statement before execution */
for (SELECT_LEX *sl= stmt->lex->all_selects_list; static void reset_stmt_for_execute(Prepared_statement *stmt)
sl; {
sl= sl->next_select_in_list()) THD *thd= stmt->thd;
SELECT_LEX *sl= stmt->lex->all_selects_list;
for (; sl; sl= sl->next_select_in_list())
{ {
/* /*
Copy WHERE clause pointers to avoid damaging they by optimisation Copy WHERE clause pointers to avoid damaging they by optimisation
...@@ -1075,18 +1057,60 @@ void mysql_stmt_execute(THD *thd, char *packet) ...@@ -1075,18 +1057,60 @@ void mysql_stmt_execute(THD *thd, char *packet)
SELECT_LEX_UNIT *unit= sl->master_unit(); SELECT_LEX_UNIT *unit= sl->master_unit();
unit->unclean(); unit->unclean();
unit->types.empty(); unit->types.empty();
// for derived tables & PS (which can't be reset by Item_subquery) /* for derived tables & PS (which can't be reset by Item_subquery) */
unit->reinit_exec_mechanism(); unit->reinit_exec_mechanism();
} }
} }
}
/*
Executes previously prepared query.
If there is any parameters, then replace markers with the data supplied
from client, and then execute the query.
SYNOPSYS
mysql_stmt_execute()
*/
void mysql_stmt_execute(THD *thd, char *packet)
{
ulong stmt_id= uint4korr(packet);
Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_execute");
if (!(stmt= find_prepared_statement(thd, stmt_id, "execute", SEND_ERROR)))
DBUG_VOID_RETURN;
/* Check if we got an error when sending long data */
if (stmt->get_longdata_error)
{
send_error(thd, stmt->last_errno, stmt->last_error);
DBUG_VOID_RETURN;
}
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
reset_stmt_for_execute(stmt);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (stmt->param_count && setup_params_data(stmt)) if (stmt->param_count)
goto end; {
packet+= 4;
uchar *null_array= (uchar *) packet;
if (setup_conversion_functions(stmt, (uchar **) &packet) ||
stmt->set_params(stmt, null_array, (uchar *) packet))
goto set_params_data_err;
}
#else #else
if (stmt->param_count && (*stmt->setup_params_data)(stmt)) /*
goto end; In embedded library we re-install conversion routines each time
we set params, and also we don't need to parse packet.
So we do it in one function.
*/
if (stmt->param_count && stmt->set_params_data(stmt))
goto set_params_data_err;
#endif #endif
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
...@@ -1105,28 +1129,32 @@ void mysql_stmt_execute(THD *thd, char *packet) ...@@ -1105,28 +1129,32 @@ void mysql_stmt_execute(THD *thd, char *packet)
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR); my_pthread_setprio(pthread_self(), WAIT_PRIOR);
free_items(thd->free_list);
cleanup_items(stmt->free_list); cleanup_items(stmt->free_list);
close_thread_tables(thd); // to close derived tables close_thread_tables(thd); // to close derived tables
free_root(&thd->mem_root, MYF(0));
thd->set_statement(&thd->stmt_backup); thd->set_statement(&thd->stmt_backup);
end: 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; DBUG_VOID_RETURN;
} }
/* /*
Reset a prepared statement Reset a prepared statement, in case there was an error in send_longdata.
Note: we don't send any reply to that command.
SYNOPSIS SYNOPSIS
mysql_stmt_reset() mysql_stmt_reset()
thd Thread handle thd Thread handle
packet Packet with stmt handle packet Packet with stmt id
DESCRIPTION DESCRIPTION
This function is useful when one gets an error after calling This function is useful when one gets an error after calling
mysql_stmt_getlongdata() and one wants to reset the handle mysql_stmt_getlongdata() and wants to reset the handle
so that one can call execute again. so that one can call execute again.
See also bug #1664
*/ */
void mysql_stmt_reset(THD *thd, char *packet) void mysql_stmt_reset(THD *thd, char *packet)
...@@ -1136,25 +1164,27 @@ void mysql_stmt_reset(THD *thd, char *packet) ...@@ -1136,25 +1164,27 @@ void mysql_stmt_reset(THD *thd, char *packet)
DBUG_ENTER("mysql_stmt_reset"); DBUG_ENTER("mysql_stmt_reset");
if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", DONT_SEND_ERROR)))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
stmt->error_in_prepare= 0; stmt->get_longdata_error= 0;
Item_param *item= *stmt->param, *end= item + stmt->param_count;
/* Free long data if used */ /* Free long data if used */
if (stmt->long_data_used) if (stmt->long_data_used)
{ {
Item_param **item= stmt->param_array;
Item_param **end= item + stmt->param_count;
stmt->long_data_used= 0; stmt->long_data_used= 0;
for (; item < end ; item++) for (; item < end ; item++)
item->reset(); (**item).reset();
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/* /*
Delete a prepared statement from memory Delete a prepared statement from memory.
Note: we don't send any reply to that command.
*/ */
void mysql_stmt_free(THD *thd, char *packet) void mysql_stmt_free(THD *thd, char *packet)
...@@ -1164,7 +1194,7 @@ void mysql_stmt_free(THD *thd, char *packet) ...@@ -1164,7 +1194,7 @@ void mysql_stmt_free(THD *thd, char *packet)
DBUG_ENTER("mysql_stmt_free"); DBUG_ENTER("mysql_stmt_free");
if (!(stmt= find_prepared_statement(thd, stmt_id, "close"))) if (!(stmt= find_prepared_statement(thd, stmt_id, "close", DONT_SEND_ERROR)))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
/* Statement map deletes statement on erase */ /* Statement map deletes statement on erase */
...@@ -1210,14 +1240,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) ...@@ -1210,14 +1240,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
ulong stmt_id= uint4korr(pos); ulong stmt_id= uint4korr(pos);
uint param_number= uint2korr(pos+4); uint param_number= uint2korr(pos+4);
if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata"))) if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata",
DONT_SEND_ERROR)))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (param_number >= stmt->param_count) if (param_number >= stmt->param_count)
{ {
/* Error will be sent in execute call */ /* Error will be sent in execute call */
stmt->error_in_prepare= 1; stmt->get_longdata_error= 1;
stmt->last_errno= ER_WRONG_ARGUMENTS; stmt->last_errno= ER_WRONG_ARGUMENTS;
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata"); sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata");
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -1225,7 +1256,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) ...@@ -1225,7 +1256,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
pos+= MYSQL_LONG_DATA_HEADER; // Point to data pos+= MYSQL_LONG_DATA_HEADER; // Point to data
#endif #endif
Item_param *param= *(stmt->param+param_number); Item_param *param= stmt->param_array[param_number];
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1); param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1);
#else #else
...@@ -1239,10 +1270,10 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) ...@@ -1239,10 +1270,10 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
Prepared_statement::Prepared_statement(THD *thd_arg) Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(thd_arg), :Statement(thd_arg),
thd(thd_arg), thd(thd_arg),
param(0), param_array(0),
param_count(0), param_count(0),
last_errno(0), last_errno(0),
error_in_prepare(0), get_longdata_error(0),
long_data_used(0), long_data_used(0),
log_full_query(0) log_full_query(0)
{ {
...@@ -1251,23 +1282,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -1251,23 +1282,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
{ {
log_full_query= 1; log_full_query= 1;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
setup_params= insert_params_withlog; set_params= insert_params_withlog;
#else #else
setup_params_data= setup_params_data_withlog; set_params_data= emb_insert_params_withlog;
#endif #endif
} }
else else
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
setup_params= insert_params; // not fully qualified query set_params= insert_params;
#else #else
setup_params_data= ::setup_params_data; set_params_data= emb_insert_params;
#endif #endif
} }
Prepared_statement::~Prepared_statement() Prepared_statement::~Prepared_statement()
{ {
my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR));
free_items(free_list); free_items(free_list);
} }
...@@ -1277,4 +1307,3 @@ Statement::Type Prepared_statement::type() const ...@@ -1277,4 +1307,3 @@ Statement::Type Prepared_statement::type() const
return PREPARED_STATEMENT; return PREPARED_STATEMENT;
} }
...@@ -4478,11 +4478,17 @@ text_string: ...@@ -4478,11 +4478,17 @@ text_string:
param_marker: param_marker:
'?' '?'
{ {
LEX *lex=Lex; THD *thd=YYTHD;
if (YYTHD->command == COM_PREPARE) LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{ {
lex->param_list.push_back($$=new Item_param((uint)(lex->tok_start-(uchar *)YYTHD->query))); Item_param *item= new Item_param((uint) (lex->tok_start -
lex->param_count++; (uchar *) thd->query));
if (!($$= item) || lex->param_list.push_back(item))
{
send_error(thd, ER_OUT_OF_RESOURCES);
YYABORT;
}
} }
else else
{ {
......
...@@ -8470,8 +8470,6 @@ int main(int argc, char **argv) ...@@ -8470,8 +8470,6 @@ int main(int argc, char **argv)
start_time= time((time_t *)0); start_time= time((time_t *)0);
test_subqueries();
client_query(); /* simple client query test */ client_query(); /* simple client query test */
#if NOT_YET_WORKING #if NOT_YET_WORKING
/* Used for internal new development debugging */ /* Used for internal new development debugging */
......
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