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);
#define my_free_lock(A,B) my_free((A),(B))
#endif
#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,
uint pre_alloc_size);
extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size);
......
......@@ -375,9 +375,9 @@ class Item_param :public Item
bool get_time(TIME *tm);
void reset() {}
#ifndef EMBEDDED_LIBRARY
void (*setup_param_func)(Item_param *param, uchar **pos);
void (*set_param_func)(Item_param *param, uchar **pos);
#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
enum Item_result result_type () const
{ return item_result_type; }
......
......@@ -620,14 +620,13 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text);
/* 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_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);
int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
List<Item> &values, ulong counter);
void setup_param_functions(Item_param *param, uchar param_type);
/* sql_error.cc */
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);
char *net_store_data(char *to,int32 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),
init();
/* Initialize sub structures */
bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root));
bzero((char*) &warn_root,sizeof(warn_root));
clear_alloc_root(&transaction.mem_root);
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
......@@ -331,7 +330,7 @@ THD::~THD()
dbug_sentry = THD_SENTRY_GONE;
#endif
/* 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;
}
......@@ -1185,10 +1184,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
Statement::Statement(THD *thd)
:id(++thd->statement_id_counter),
query_id(thd->query_id),
set_query_id(1),
allow_sum_func(0),
command(thd->command),
lex(&main_lex),
query(0),
query_length(0),
......@@ -1207,10 +1204,8 @@ Statement::Statement(THD *thd)
Statement::Statement()
:id(0),
query_id(0), /* initialized later */
set_query_id(1),
allow_sum_func(0), /* initialized later */
command(COM_SLEEP), /* initialized later */
lex(&main_lex),
query(0), /* these two are set */
query_length(0), /* in alloc_query() */
......@@ -1229,15 +1224,11 @@ Statement::Type Statement::type() const
void Statement::set_statement(Statement *stmt)
{
id= stmt->id;
query_id= stmt->query_id;
set_query_id= stmt->set_query_id;
allow_sum_func= stmt->allow_sum_func;
command= stmt->command;
lex= stmt->lex;
query= stmt->query;
query_length= stmt->query_length;
free_list= stmt->free_list;
mem_root= stmt->mem_root;
}
......
......@@ -434,15 +434,6 @@ class Statement
*/
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
field list can not contain duplicates.
......@@ -461,11 +452,6 @@ class Statement
See item_sum.cc for details.
*/
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
/*
......@@ -676,6 +662,11 @@ class THD :public ilink,
uint dbug_sentry; // watch out for memory corruption
#endif
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 file_id; // for LOAD DATA INFILE
/*
......@@ -751,6 +742,15 @@ class THD :public ilink,
List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
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;
/* Statement id is thread-wide. This counter is used to generate ids */
......
......@@ -552,7 +552,7 @@ typedef struct st_lex
List<Item> *insert_list,field_list,value_list;
List<List_item> many_values;
List<set_var_base> var_list;
List<Item> param_list;
List<Item_param> param_list;
SQL_LIST proc_list, auxilliary_table_list, save_list;
TYPELIB *interval;
create_field *last_field;
......@@ -577,7 +577,6 @@ typedef struct st_lex
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
uint param_count;
uint slave_thd_opt;
uint8 describe;
bool drop_if_exists, drop_temporary, local_file;
......
......@@ -1232,10 +1232,34 @@ bool do_command(THD *thd)
command_name[command]));
}
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));
}
#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,
char* packet, uint packet_length)
......
......@@ -86,16 +86,17 @@ class Prepared_statement: public Statement
{
public:
THD *thd;
Item_param **param; /* array of all placeholders */
Item_param **param_array;
uint param_count;
uint last_errno;
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;
#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
bool (*setup_params_data)(Prepared_statement *st);
bool (*set_params_data)(Prepared_statement *st);
#endif
public:
Prepared_statement(THD *thd_arg);
......@@ -117,13 +118,14 @@ inline bool is_param_null(const uchar *pos, ulong param_no)
enum { STMT_QUERY_LOG_LENGTH= 8192 };
#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)
#else
#define SETUP_PARAM_FUNCTION(fn_name) \
#define SET_PARAM_FUNCTION(fn_name) \
static void fn_name(Item_param *param, uchar **pos)
#endif
enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR };
/*
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)
*/
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);
if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
{
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
if (se == SEND_ERROR)
send_error(thd);
return 0;
}
......@@ -154,11 +158,11 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{
NET *net= &stmt->thd->net;
char buff[9];
buff[0]= 0;
buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id);
int2store(buff+5, columns);
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));
}
#else
......@@ -177,8 +181,8 @@ static bool send_prep_stmt(Prepared_statement *stmt,
/*
Read the length of the parameter data and retun back to
caller by positing the pointer to param data
Read the length of the parameter data and return back to
caller by positing the pointer to param data.
*/
#ifndef EMBEDDED_LIBRARY
......@@ -208,49 +212,49 @@ static ulong get_param_length(uchar **packet)
#endif /*!EMBEDDED_LIBRARY*/
/*
Setup param conversion routines
setup_param_xx()
param Parameter Item
pos Input data buffer
Data conversion routines
SYNOPSIS
set_param_xx()
param parameter item
pos input data buffer
len length of data in the buffer
All these functions reads the data from pos and sets up that data
through 'param' and advances the buffer position to predifined
length position.
All these functions read the data from pos, convert it to requested type
and assign to param; pos is advanced to predefined length.
Make a note that the NULL handling is examined at first execution
(i.e. when input types altered) and for all subsequent executions
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));
*pos+= 1;
}
SETUP_PARAM_FUNCTION(setup_param_short)
SET_PARAM_FUNCTION(set_param_short)
{
param->set_int((longlong)sint2korr(*pos));
*pos+= 2;
}
SETUP_PARAM_FUNCTION(setup_param_int32)
SET_PARAM_FUNCTION(set_param_int32)
{
param->set_int((longlong)sint4korr(*pos));
*pos+= 4;
}
SETUP_PARAM_FUNCTION(setup_param_int64)
SET_PARAM_FUNCTION(set_param_int64)
{
param->set_int((longlong)sint8korr(*pos));
*pos+= 8;
}
SETUP_PARAM_FUNCTION(setup_param_float)
SET_PARAM_FUNCTION(set_param_float)
{
float data;
float4get(data,*pos);
......@@ -258,7 +262,7 @@ SETUP_PARAM_FUNCTION(setup_param_float)
*pos+= 4;
}
SETUP_PARAM_FUNCTION(setup_param_double)
SET_PARAM_FUNCTION(set_param_double)
{
double data;
float8get(data,*pos);
......@@ -266,7 +270,7 @@ SETUP_PARAM_FUNCTION(setup_param_double)
*pos+= 8;
}
SETUP_PARAM_FUNCTION(setup_param_time)
SET_PARAM_FUNCTION(set_param_time)
{
ulong length;
......@@ -290,11 +294,11 @@ SETUP_PARAM_FUNCTION(setup_param_time)
*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;
TIME tm;
......@@ -320,7 +324,7 @@ SETUP_PARAM_FUNCTION(setup_param_datetime)
*pos+= length;
}
SETUP_PARAM_FUNCTION(setup_param_date)
SET_PARAM_FUNCTION(set_param_date)
{
ulong length;
......@@ -342,55 +346,55 @@ SETUP_PARAM_FUNCTION(setup_param_date)
*pos+= length;
}
SETUP_PARAM_FUNCTION(setup_param_str)
SET_PARAM_FUNCTION(set_param_str)
{
ulong len= get_param_length(pos);
param->set_value((const char *)*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) {
case FIELD_TYPE_TINY:
param->setup_param_func= setup_param_tiny;
param->set_param_func= set_param_tiny;
param->item_result_type= INT_RESULT;
break;
case FIELD_TYPE_SHORT:
param->setup_param_func= setup_param_short;
param->set_param_func= set_param_short;
param->item_result_type= INT_RESULT;
break;
case FIELD_TYPE_LONG:
param->setup_param_func= setup_param_int32;
param->set_param_func= set_param_int32;
param->item_result_type= INT_RESULT;
break;
case FIELD_TYPE_LONGLONG:
param->setup_param_func= setup_param_int64;
param->set_param_func= set_param_int64;
param->item_result_type= INT_RESULT;
break;
case FIELD_TYPE_FLOAT:
param->setup_param_func= setup_param_float;
param->set_param_func= set_param_float;
param->item_result_type= REAL_RESULT;
break;
case FIELD_TYPE_DOUBLE:
param->setup_param_func= setup_param_double;
param->set_param_func= set_param_double;
param->item_result_type= REAL_RESULT;
break;
case FIELD_TYPE_TIME:
param->setup_param_func= setup_param_time;
param->set_param_func= set_param_time;
param->item_result_type= STRING_RESULT;
break;
case FIELD_TYPE_DATE:
param->setup_param_func= setup_param_date;
param->set_param_func= set_param_date;
param->item_result_type= STRING_RESULT;
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
param->setup_param_func= setup_param_datetime;
param->set_param_func= set_param_datetime;
param->item_result_type= STRING_RESULT;
break;
default:
param->setup_param_func= setup_param_str;
param->set_param_func= set_param_str;
param->item_result_type= STRING_RESULT;
}
}
......@@ -405,9 +409,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
uchar *read_pos)
{
THD *thd= stmt->thd;
List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
Item_param **begin= stmt->param_array;
Item_param **end= begin + stmt->param_count;
uint32 length= 0;
String str, query;
const String *res;
......@@ -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))
DBUG_RETURN(1);
ulong param_no= 0;
uint32 length= 0;
while ((param= (Item_param *)param_iterator++))
for (Item_param **it= begin; it < end; ++it)
{
Item_param *param= *it;
if (param->long_data_supplied)
res= param->query_val_str(&str);
else
{
if (is_param_null(pos,param_no))
if (is_param_null(pos, it - begin))
{
param->maybe_null= param->null_value= 1;
res= &my_null_string;
......@@ -434,7 +436,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
else
{
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);
}
}
......@@ -442,7 +444,6 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
DBUG_RETURN(1);
length+= res->length()-1;
param_no++;
}
if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
DBUG_RETURN(1);
......@@ -454,41 +455,35 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
static bool insert_params(Prepared_statement *stmt, uchar *pos,
uchar *read_pos)
{
List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
ulong param_no= 0;
Item_param **begin= stmt->param_array;
Item_param **end= begin + stmt->param_count;
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 (is_param_null(pos,param_no))
if (is_param_null(pos, it - begin))
param->maybe_null= param->null_value= 1;
else
{
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);
}
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;
List_iterator<Item> param_iterator(params);
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
/* skip null bits */
uchar *read_pos= *data + (stmt->param_count+7) / 8;
DBUG_ENTER("setup_params_data");
DBUG_ENTER("setup_conversion_functions");
if (*read_pos++) //types supplied / first execute
{
......@@ -496,31 +491,32 @@ static bool setup_params_data(Prepared_statement *stmt)
First execute or types altered by the client, setup the
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;
}
param_iterator.rewind();
}
stmt->setup_params(stmt,pos,read_pos);
*data= read_pos;
DBUG_RETURN(0);
}
#else
bool setup_params_data(Prepared_statement *stmt)
static bool emb_insert_params(Prepared_statement *stmt)
{
List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
Item_param **it= stmt->param_array;
Item_param **end= it + stmt->param_count;
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 (*client_param->is_null)
......@@ -529,7 +525,7 @@ bool setup_params_data(Prepared_statement *stmt)
{
uchar *buff= (uchar*)client_param->buffer;
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->buffer_length);
......@@ -539,27 +535,27 @@ bool setup_params_data(Prepared_statement *stmt)
DBUG_RETURN(0);
}
bool setup_params_data_withlog(Prepared_statement *stmt)
static bool emb_insert_params_withlog(Prepared_statement *stmt)
{
THD *thd= stmt->thd;
List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
Item_param **it= stmt->param_array;
Item_param **end= it + stmt->param_count;
MYSQL_BIND *client_param= thd->client_params;
String str, query;
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))
DBUG_RETURN(1);
uint32 length= 0;
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)
res= param->query_val_str(&str);
else
......@@ -573,7 +569,7 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
{
uchar *buff= (uchar*)client_param->buffer;
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->buffer_length);
......@@ -582,7 +578,6 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
}
if (query.replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1);
length+= res->length()-1;
}
......@@ -596,11 +591,17 @@ bool setup_params_data_withlog(Prepared_statement *stmt)
/*
Validate the following information for INSERT statement:
- field existance
- field existence
- 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,
List<Item> &fields,
List<List_item> &values_list)
......@@ -630,7 +631,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
if (open_and_lock_tables(thd, table_list))
{
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
table= table_list->table;
......@@ -643,7 +644,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
if (check_insert_fields(thd,table,fields,*values,1))
{
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
thd->free_temporary_memory_pool_for_ps_preparing();
......@@ -658,7 +659,7 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
MYF(0), counter);
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
}
}
......@@ -666,22 +667,23 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt,
{
thd->free_temporary_memory_pool_for_ps_preparing();
}
if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
DBUG_RETURN(0);
}
/*
Validate the following information
UPDATE - set and where clause DELETE - where clause
And send update-set clause column list fields info
back to client. For DELETE, just validate where clause
and return no fields information back to client.
Validate the following information:
UPDATE - set and where clause
DELETE - where clause
SYNOPSIS
mysql_test_upd_fields()
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,
List<Item> &fields, List<Item> &values,
COND *conds)
......@@ -711,32 +713,32 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt,
thd->free_temporary_memory_pool_for_ps_preparing();
/*
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);
/* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(0);
err:
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(1);
DBUG_RETURN(-1);
}
/*
Validate the following information:
SELECT - column list
- where clause
- order clause
- having clause
- group by clause
- if no column spec i.e. '*', then setup all fields
And send column list fields info back to client.
In case of success, if this query is not EXPLAIN, send column list info
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,
uint wild_num,
List<Item> &fields, COND *conds,
......@@ -748,7 +750,6 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
select_result *result= lex->result;
DBUG_ENTER("mysql_test_select_fields");
......@@ -772,7 +773,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
*/
thd->allocate_temporary_memory_pool_for_ps_preparing();
if (open_and_lock_tables(thd, tables))
{
send_error(thd);
goto err;
}
if (lex->describe)
{
......@@ -781,6 +785,7 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
}
else
{
select_result *result= lex->result;
if (!result && !(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
......@@ -790,7 +795,10 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
thd->used_tables= 0; // Updated by setup_fields
if (unit->prepare(thd, result, 0))
{
send_error(thd);
goto err_prep;
}
if (send_prep_stmt(stmt, fields.elements) ||
thd->protocol_simple.send_fields(&fields, 0)
......@@ -815,46 +823,45 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
/*
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;
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;
int res;
DBUG_ENTER("send_prepare_results");
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, lex->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;
sql_command, stmt->param_count));
switch (sql_command) {
case SQLCOM_INSERT:
if (mysql_test_insert_fields(stmt, tables, lex->field_list,
lex->many_values))
goto abort;
if ((res= mysql_test_insert_fields(stmt, tables, lex->field_list,
lex->many_values)))
goto error;
break;
case SQLCOM_UPDATE:
if (mysql_test_upd_fields(stmt, tables, select_lex->item_list,
lex->value_list, select_lex->where))
goto abort;
break;
/* XXX: fallthrough */
case SQLCOM_DELETE:
if (mysql_test_upd_fields(stmt, tables, select_lex->item_list,
lex->value_list, select_lex->where))
goto abort;
if ((res= mysql_test_upd_fields(stmt, tables, select_lex->item_list,
lex->value_list, select_lex->where)))
goto error;
break;
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->where,
select_lex->order_list.elements +
......@@ -864,46 +871,54 @@ static bool send_prepare_results(Prepared_statement *stmt)
select_lex->having,
(ORDER*)lex->proc_list.first,
select_lex->options | thd->options,
&(lex->unit), select_lex))
goto abort;
break;
&(lex->unit), select_lex)))
goto error;
/* Statement and field info has already been sent */
DBUG_RETURN(0);
default:
{
/*
Rest fall through to default category, no parsing
for non-DML statements
*/
if (send_prep_stmt(stmt, 0))
goto abort;
}
break;
}
DBUG_RETURN(0);
DBUG_RETURN(send_prep_stmt(stmt, 0));
abort:
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
error:
if (res < 0)
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
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;
if (!stmt->param_count)
stmt->param= (Item_param **)0;
else
List_iterator<Item_param> param_iterator(lex->param_list);
/* Use thd->mem_root as it points at statement mem_root */
stmt->param_array= (Item_param **)
alloc_root(&stmt->thd->mem_root,
sizeof(Item_param*) * stmt->param_count);
if (!stmt->param_array)
{
if (!(stmt->param= to= (Item_param **)
my_malloc(sizeof(Item_param *)*(stmt->param_count+1),
MYF(MY_WME))))
send_error(stmt->thd, ER_OUT_OF_RESOURCES);
return 1;
List_iterator<Item> param_iterator(stmt->lex->param_list);
while ((*(to++)= (Item_param *)param_iterator++));
}
for (to= stmt->param_array;
to < stmt->param_array + stmt->param_count;
++to)
{
*to= param_iterator++;
}
}
return 0;
}
......@@ -912,137 +927,104 @@ static bool init_param_items(Prepared_statement *stmt)
/*
Parse the query and send the total number of parameters
and resultset metadata information back to client (if any),
without executing the query i.e. 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
without re-parsing during execute.
If parameter markers are found in the query, then store
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
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;
Prepared_statement *stmt= new Prepared_statement(thd);
SELECT_LEX *sl;
int error;
DBUG_ENTER("mysql_stmt_prepare");
if (stmt == 0)
DBUG_RETURN(0);
{
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_VOID_RETURN;
}
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_item_arena(thd);
thd->set_statement(stmt);
thd->current_statement= stmt;
thd->set_item_arena(stmt);
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);
thd->current_statement= stmt;
lex= lex_start(thd, (uchar *) thd->query, thd->query_length);
mysql_init_query(thd);
lex->safe_to_cache_query= 0;
lex->param_count= 0;
if (yyparse((void *)thd) || thd->is_fatal_error || send_prepare_results(stmt))
goto yyparse_err;
lex_end(lex);
error= yyparse((void *)thd) || thd->is_fatal_error ||
init_param_array(stmt) ||
send_prepare_results(stmt);
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_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);
stmt->set_statement(thd);
stmt->set_item_arena(thd);
thd->set_statement(&thd->stmt_backup);
init_param_err:
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->set_item_arena(&thd->stmt_backup);
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 (stmt->error_in_prepare)
if (error)
{
send_error(thd, stmt->last_errno, stmt->last_error);
DBUG_VOID_RETURN;
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
/* error is sent inside yyparse/send_prepare_results */
}
stmt->query_id= thd->query_id;
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
thd->free_list= 0;
else
{
SELECT_LEX *sl= stmt->lex->all_selects_list;
/*
To make sure that all runtime data is stored in its own memory root and
does not interfere with data possibly present in thd->mem_root.
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.
Save WHERE clause pointers, because they may be changed during query
optimisation.
*/
init_sql_alloc(&thd->mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
for (; sl; sl= sl->next_select_in_list())
{
sl->prep_where= sl->where;
}
}
DBUG_VOID_RETURN;
}
/* Reinit statement before execution */
for (SELECT_LEX *sl= stmt->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
static void reset_stmt_for_execute(Prepared_statement *stmt)
{
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
......@@ -1075,18 +1057,60 @@ void mysql_stmt_execute(THD *thd, char *packet)
SELECT_LEX_UNIT *unit= sl->master_unit();
unit->unclean();
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();
}
}
}
/*
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
if (stmt->param_count && setup_params_data(stmt))
goto end;
if (stmt->param_count)
{
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
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
if (!(specialflag & SPECIAL_NO_PRIOR))
......@@ -1105,28 +1129,32 @@ void mysql_stmt_execute(THD *thd, char *packet)
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
free_items(thd->free_list);
cleanup_items(stmt->free_list);
close_thread_tables(thd); // to close derived tables
free_root(&thd->mem_root, MYF(0));
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;
}
/*
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
mysql_stmt_reset()
thd Thread handle
packet Packet with stmt handle
packet Packet with stmt id
DESCRIPTION
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.
See also bug #1664
*/
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");
if (!(stmt= find_prepared_statement(thd, stmt_id, "reset")))
if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", DONT_SEND_ERROR)))
DBUG_VOID_RETURN;
stmt->error_in_prepare= 0;
Item_param *item= *stmt->param, *end= item + stmt->param_count;
stmt->get_longdata_error= 0;
/* Free long data if used */
if (stmt->long_data_used)
{
Item_param **item= stmt->param_array;
Item_param **end= item + stmt->param_count;
stmt->long_data_used= 0;
for (; item < end ; item++)
item->reset();
(**item).reset();
}
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)
......@@ -1164,7 +1194,7 @@ void mysql_stmt_free(THD *thd, char *packet)
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;
/* Statement map deletes statement on erase */
......@@ -1210,14 +1240,15 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
ulong stmt_id= uint4korr(pos);
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;
#ifndef EMBEDDED_LIBRARY
if (param_number >= stmt->param_count)
{
/* Error will be sent in execute call */
stmt->error_in_prepare= 1;
stmt->get_longdata_error= 1;
stmt->last_errno= ER_WRONG_ARGUMENTS;
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata");
DBUG_VOID_RETURN;
......@@ -1225,7 +1256,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
pos+= MYSQL_LONG_DATA_HEADER; // Point to data
#endif
Item_param *param= *(stmt->param+param_number);
Item_param *param= stmt->param_array[param_number];
#ifndef EMBEDDED_LIBRARY
param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1);
#else
......@@ -1239,10 +1270,10 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(thd_arg),
thd(thd_arg),
param(0),
param_array(0),
param_count(0),
last_errno(0),
error_in_prepare(0),
get_longdata_error(0),
long_data_used(0),
log_full_query(0)
{
......@@ -1251,23 +1282,22 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
{
log_full_query= 1;
#ifndef EMBEDDED_LIBRARY
setup_params= insert_params_withlog;
set_params= insert_params_withlog;
#else
setup_params_data= setup_params_data_withlog;
set_params_data= emb_insert_params_withlog;
#endif
}
else
#ifndef EMBEDDED_LIBRARY
setup_params= insert_params; // not fully qualified query
set_params= insert_params;
#else
setup_params_data= ::setup_params_data;
set_params_data= emb_insert_params;
#endif
}
Prepared_statement::~Prepared_statement()
{
my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR));
free_items(free_list);
}
......@@ -1277,4 +1307,3 @@ Statement::Type Prepared_statement::type() const
return PREPARED_STATEMENT;
}
......@@ -4478,11 +4478,17 @@ text_string:
param_marker:
'?'
{
LEX *lex=Lex;
if (YYTHD->command == COM_PREPARE)
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
lex->param_list.push_back($$=new Item_param((uint)(lex->tok_start-(uchar *)YYTHD->query)));
lex->param_count++;
Item_param *item= new Item_param((uint) (lex->tok_start -
(uchar *) thd->query));
if (!($$= item) || lex->param_list.push_back(item))
{
send_error(thd, ER_OUT_OF_RESOURCES);
YYABORT;
}
}
else
{
......
......@@ -8470,8 +8470,6 @@ int main(int argc, char **argv)
start_time= time((time_t *)0);
test_subqueries();
client_query(); /* simple client query test */
#if NOT_YET_WORKING
/* 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