Commit 32c6b0d7 authored by unknown's avatar unknown

Prepared_statement deployed instead of PREP_STMT.


libmysqld/lib_sql.cc:
  Prepared_statement now resides entirely in sql_prepare.cc
  Embedded versions of setup_params_data moved to sql_prepare.cc
sql/mysql_priv.h:
  removed declarations for non-existing functions
sql/slave.cc:
  no thd->init_for_queries() any more
sql/sql_class.cc:
  added Statement and Statement_map classes.
  PREP_STMT replaced with Statement (Prepared_statement) and moved to
  sql_prepare.cc
sql/sql_class.h:
  added Statement and Statement_map classes.
  PREP_STMT replaced with Statement (Prepared_statement) and moved to
  sql_prepare.cc
sql/sql_parse.cc:
  thd->init_for_queries() doesn't exist any more
  comment moved to proper place
sql/sql_prepare.cc:
  PREP_STMT replaced with Prepared_statement
  minor code cleanups
tests/client_test.c:
  Later in the test we rely on order of rows, which normally is not defined.
  My patch changes the order.
parent 8d987f9e
......@@ -743,90 +743,3 @@ bool Protocol::convert_str(const char *from, uint length)
}
#endif
bool setup_params_data(st_prep_stmt *stmt)
{
THD *thd= stmt->thd;
List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
ulong param_no= 0;
MYSQL_BIND *client_param= thd->client_params;
DBUG_ENTER("setup_params_data");
for (;(param= (Item_param *)param_iterator++); client_param++)
{
setup_param_functions(param, client_param->buffer_type);
if (!param->long_data_supplied)
{
if (*client_param->is_null)
param->maybe_null= param->null_value= 1;
else
{
uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&buff,
client_param->length ?
*client_param->length :
client_param->buffer_length);
}
}
param_no++;
}
DBUG_RETURN(0);
}
bool setup_params_data_withlog(st_prep_stmt *stmt)
{
THD *thd= stmt->thd;
List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
MYSQL_BIND *client_param= thd->client_params;
DBUG_ENTER("setup_params_data");
String str, *res, *query= new String(stmt->query->alloced_length());
query->copy(*stmt->query);
ulong param_no= 0;
uint32 length= 0;
for (;(param= (Item_param *)param_iterator++); client_param++)
{
setup_param_functions(param, client_param->buffer_type);
if (param->long_data_supplied)
res= param->query_val_str(&str);
else
{
if (*client_param->is_null)
{
param->maybe_null= param->null_value= 1;
res= &my_null_string;
}
else
{
uchar *buff= (uchar*)client_param->buffer;
param->maybe_null= param->null_value= 0;
param->setup_param_func(param,&buff,
client_param->length ?
*client_param->length :
client_param->buffer_length);
res= param->query_val_str(&str);
}
}
if (query->replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1);
length+= res->length()-1;
param_no++;
}
if (alloc_query(stmt->thd, (char *)query->ptr(), query->length()+1))
DBUG_RETURN(1);
query->free();
DBUG_RETURN(0);
}
......@@ -612,8 +612,6 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */
int compare_prep_stmt(void *not_used, PREP_STMT *stmt, ulong *key);
void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used);
bool 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);
......@@ -855,7 +853,7 @@ extern I_List<THD> threads;
extern I_List<NAMED_LIST> key_caches;
extern MY_BITMAP temp_pool;
extern String my_empty_string;
extern String my_null_string;
extern const String my_null_string;
extern SHOW_VAR init_vars[],status_vars[], internal_vars[];
extern SHOW_COMP_OPTION have_isam;
extern SHOW_COMP_OPTION have_innodb;
......
......@@ -3111,7 +3111,6 @@ extern "C" pthread_handler_decl(handle_slave_sql,arg)
sql_print_error("Failed during slave thread initialization");
goto err;
}
thd->init_for_queries();
rli->sql_thd= thd;
thd->temporary_tables = rli->save_temporary_tables; // restore temp tables
pthread_mutex_lock(&LOCK_thread_count);
......
......@@ -88,21 +88,20 @@ THD::THD():user_time(0), is_fatal_error(0),
insert_id_used(0), rand_used(0), in_lock_tables(0),
global_read_lock(0), bootstrap(0)
{
host=user=priv_user=db=query=ip=0;
lex= &main_lex;
host= user= priv_user= db= ip=0;
host_or_ip= "connecting host";
locked=killed=some_tables_deleted=no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
db_length=query_length=col_access=0;
db_length= col_access= 0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
open_tables= temporary_tables= handler_tables= derived_tables= 0;
handler_items=0;
tmp_table=0;
lock=locked_tables=0;
used_tables=0;
cuted_fields= sent_row_count= current_stmt_id= 0L;
cuted_fields= sent_row_count= 0L;
statement_id_counter= 0UL;
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
start_time=(time_t) 0;
......@@ -138,7 +137,6 @@ THD::THD():user_time(0), is_fatal_error(0),
server_id = ::server_id;
slave_net = 0;
command=COM_CONNECT;
set_query_id=1;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access=NO_ACCESS;
#endif
......@@ -146,10 +144,11 @@ THD::THD():user_time(0), is_fatal_error(0),
*scramble= '\0';
init();
init_sql_alloc(&mem_root, // must be after init()
variables.query_alloc_block_size,
variables.query_prealloc_size);
/* Initialize sub structures */
bzero((char*) &mem_root,sizeof(mem_root));
bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root));
bzero((char*) &con_root,sizeof(con_root));
bzero((char*) &warn_root,sizeof(warn_root));
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
......@@ -166,12 +165,6 @@ THD::THD():user_time(0), is_fatal_error(0),
else
bzero((char*) &user_var_events, sizeof(user_var_events));
/* Prepared statements */
last_prepared_stmt= 0;
init_tree(&prepared_statements, 0, 0, sizeof(PREP_STMT),
(qsort_cmp2) compare_prep_stmt, 1,
(tree_element_free) free_prep_stmt, 0);
/* Protocol */
protocol= &protocol_simple; // Default protocol
protocol_simple.init(this);
......@@ -189,7 +182,9 @@ THD::THD():user_time(0), is_fatal_error(0),
transaction.trans_log.end_of_file= max_binlog_cache_size;
}
#endif
init_sql_alloc(&transaction.mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
/*
We need good random number initialization for new thread
Just coping global one will not work
......@@ -232,22 +227,6 @@ void THD::init(void)
}
/*
Init THD for query processing
This has to be called once before we call mysql_parse()
*/
void THD::init_for_queries()
{
init_sql_alloc(&mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
init_sql_alloc(&transaction.mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
}
/*
Do what's needed when one invokes change user
......@@ -276,7 +255,6 @@ void THD::cleanup(void)
{
DBUG_ENTER("THD::cleanup");
ha_rollback(this);
delete_tree(&prepared_statements);
if (locked_tables)
{
lock=locked_tables; locked_tables=0;
......@@ -340,8 +318,6 @@ THD::~THD()
safeFree(user);
safeFree(db);
safeFree(ip);
free_root(&mem_root,MYF(0));
free_root(&con_root,MYF(0));
free_root(&warn_root,MYF(0));
free_root(&transaction.mem_root,MYF(0));
mysys_var=0; // Safety (shouldn't be needed)
......@@ -1193,6 +1169,102 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
}
return 0;
}
/*
Statement functions
*/
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),
free_list(0)
{
init_sql_alloc(&mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
}
/*
This constructor is called when statement is a subobject of THD:
Some variables are initialized in THD::init due to locking problems
This statement object will be used to
*/
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() */
free_list(0)
{
bzero((char *) &mem_root, sizeof(mem_root));
}
Statement::Type Statement::type() const
{
return STATEMENT;
}
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;
}
Statement::~Statement()
{
free_root(&mem_root, MYF(0));
}
C_MODE_START
static byte *
get_statement_id_as_hash_key(const byte *record, uint *key_length,
my_bool not_used __attribute__((unused)))
{
const Statement *statement= (const Statement *) record;
*key_length= sizeof(statement->id);
return (byte *) &((const Statement *) statement)->id;
}
static void delete_statement_as_hash_key(void *key)
{
delete (Statement *) key;
}
C_MODE_END
Statement_map::Statement_map() :
last_found_statement(0)
{
enum { START_HASH_SIZE = 16 };
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0));
}
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
......
......@@ -328,30 +328,6 @@ class MYSQL_ERROR: public Sql_alloc
};
/* This is a struct as it's allocated in tree_insert */
typedef struct st_prep_stmt
{
THD *thd;
LEX lex;
Item_param **param;
Item *free_list;
MEM_ROOT mem_root;
String *query;
ulong stmt_id;
uint param_count;
uint last_errno;
char last_error[MYSQL_ERRMSG_SIZE];
bool error_in_prepare, long_data_used;
bool log_full_query;
#ifndef EMBEDDED_LIBRARY
bool (*setup_params)(st_prep_stmt *stmt, uchar *pos, uchar *read_pos);
#else
bool (*setup_params_data)(st_prep_stmt *stmt);
#endif
} PREP_STMT;
class delayed_insert;
class select_result;
......@@ -428,12 +404,158 @@ struct system_variables
};
void free_tmp_table(THD *thd, TABLE *entry);
class Prepared_statement;
/*
State of a single command executed against this connection.
One connection can contain a lot of simultaneously running statements,
some of which could be:
- prepared, that is, contain placeholders,
- opened as cursors. We maintain 1 to 1 relationship between
statement and cursor - if user wants to create another cursor for his
query, we create another statement for it.
To perform some action with statement we reset THD part to the state of
that statement, do the action, and then save back modified state from THD
to the statement. It will be changed in near future, and Statement will
be used explicitly.
*/
class Statement
{
Statement(const Statement &rhs); /* not implemented: */
Statement &operator=(const Statement &rhs); /* non-copyable */
public:
/* FIXME: must be private */
LEX main_lex;
public:
/*
Uniquely identifies each statement object in thread scope; change during
statement lifetime. FIXME: must be const
*/
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.
*/
bool set_query_id;
/*
This variable is used in post-parse stage to declare that sum-functions,
or functions which have sense only if GROUP BY is present, are allowed.
For example in queries
SELECT MIN(i) FROM foo
SELECT GROUP_CONCAT(a, b, MIN(i)) FROM ... GROUP BY ...
MIN(i) have no sense.
Though it's grammar-related issue, it's hard to catch it out during the
parse stage because GROUP BY clause goes in the end of query. This
variable is mainly used in setup_fields/fix_fields.
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
/*
Points to the query associated with this statement. It's const, but
we need to declare it char * because all table handlers are written
in C and need to point to it.
*/
char *query;
uint32 query_length; // current query length
/*
List of items created in the parser for this query. Every item puts
itself to the list on creation (see Item::Item() for details))
*/
Item *free_list;
MEM_ROOT mem_root;
public:
/* We build without RTTI, so dynamic_cast can't be used. */
enum Type
{
STATEMENT,
PREPARED_STATEMENT
};
/*
This constructor is called when statement is a subobject of THD:
some variables are initialized in THD::init due to locking problems
*/
Statement();
Statement(THD *thd);
virtual ~Statement();
/* Assign execution context (note: not all members) of given stmt to self */
void set_statement(Statement *stmt);
/* return class type */
virtual Type type() const;
};
/*
Used to seek all existing statements in the connection
Deletes all statements in destructor.
*/
class Statement_map
{
public:
Statement_map();
int insert(Statement *statement)
{
int rc= my_hash_insert(&st_hash, (byte *) statement);
if (rc == 0)
last_found_statement= statement;
return rc;
}
Statement *find(ulong id)
{
if (last_found_statement == 0 || id != last_found_statement->id)
last_found_statement= (Statement *) hash_search(&st_hash, (byte *) &id,
sizeof(id));
return last_found_statement;
}
void erase(Statement *statement)
{
if (statement == last_found_statement)
last_found_statement= 0;
hash_delete(&st_hash, (byte *) statement);
}
~Statement_map()
{
hash_free(&st_hash);
}
private:
HASH st_hash;
Statement *last_found_statement;
};
/*
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
*/
class THD :public ilink
class THD :public ilink,
public Statement
{
public:
#ifdef EMBEDDED_LIBRARY
......@@ -446,23 +568,25 @@ class THD :public ilink
ulong extra_length;
#endif
NET net; // client connection descriptor
LEX main_lex;
LEX *lex; // parse tree descriptor
MEM_ROOT mem_root; // 1 command-life memory pool
MEM_ROOT con_root; // connection-life memory
MEM_ROOT warn_root; // For warnings and errors
Protocol *protocol; // Current protocol
Protocol_simple protocol_simple; // Normal protocol
Protocol_prep protocol_prep; // Binary protocol
HASH user_vars; // hash for user variables
TREE prepared_statements;
String packet; // dynamic buffer for network I/O
struct sockaddr_in remote; // client socket address
struct rand_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
pthread_mutex_t LOCK_delete; // Locked before thd is deleted
char *query; // Points to the current query,
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
/*
keeps THD state while it is used for active statement
Note, that double free_root() is safe, so we don't need to do any
special cleanup for it in THD destructor.
*/
Statement stmt_backup;
/*
A pointer to the stack frame of handle_one_connection(),
which is called first in the thread for handling a client
......@@ -502,7 +626,6 @@ class THD :public ilink
and are still in use by this thread
*/
TABLE *open_tables,*temporary_tables, *handler_tables, *derived_tables;
// TODO: document the variables below
MYSQL_LOCK *lock; /* Current locks */
MYSQL_LOCK *locked_tables; /* Tables locked with LOCK */
/*
......@@ -511,12 +634,10 @@ class THD :public ilink
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
*/
ULL *ull;
PREP_STMT *last_prepared_stmt;
#ifndef DBUG_OFF
uint dbug_sentry; // watch out for memory corruption
#endif
struct st_my_thread_var *mysys_var;
enum enum_server_command command;
uint32 server_id;
uint32 file_id; // for LOAD DATA INFILE
/*
......@@ -549,7 +670,6 @@ class THD :public ilink
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
}
} transaction;
Item *free_list, *handler_items;
Field *dupp_field;
#ifndef __WIN__
sigset_t signals,block_signals;
......@@ -580,18 +700,25 @@ class THD :public ilink
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
List<TABLE> temporary_tables_should_be_free; // list of temporary tables
/*
FIXME: this, and some other variables like 'count_cuted_fields'
maybe should be statement/cursor local, that is, moved to Statement
class. With current implementation warnings produced in each prepared
statement/cursor settle here.
*/
List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count;
ulong query_id, warn_id, version, options, thread_id, col_access;
ulong current_stmt_id;
ulong warn_id, version, options, thread_id, col_access;
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
ulong rand_saved_seed1, rand_saved_seed2;
ulong row_count; // Row counter, mainly for errors and warnings
long dbug_thread_id;
pthread_t real_id;
uint current_tablenr,tmp_table;
uint server_status,open_options,system_thread;
uint32 query_length;
uint32 db_length;
uint select_number; //number of select (used for EXPLAIN)
/* variables.transaction_isolation is reset to this after each commit */
......@@ -604,9 +731,9 @@ class THD :public ilink
char scramble[SCRAMBLE_LENGTH+1];
bool slave_thread;
bool set_query_id,locked,some_tables_deleted;
bool locked, some_tables_deleted;
bool last_cuted_field;
bool no_errors, allow_sum_func, password, is_fatal_error;
bool no_errors, password, is_fatal_error;
bool query_start_used,last_insert_id_used,insert_id_used,rand_used;
bool in_lock_tables,global_read_lock;
bool query_error, bootstrap, cleanup_done;
......@@ -634,7 +761,6 @@ class THD :public ilink
void init(void);
void change_user(void);
void init_for_queries();
void cleanup(void);
bool store_globals();
#ifdef SIGNAL_WITH_VIO_CLOSE
......
......@@ -974,7 +974,6 @@ pthread_handler_decl(handle_one_connection,arg)
thd->proc_info=0;
thd->set_time();
thd->init_for_queries();
while (!net->error && net->vio != 0 && !thd->killed)
{
if (do_command(thd))
......@@ -1055,7 +1054,6 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
buff= (char*) thd->net.buff;
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
uint length=(uint) strlen(buff);
......@@ -1221,13 +1219,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
NET *net= &thd->net;
bool error= 0;
DBUG_ENTER("dispatch_command");
thd->command=command;
/*
Commands which will always take a long time should be marked with
this so that they will not get logged to the slow query log
*/
DBUG_ENTER("dispatch_command");
thd->command=command;
thd->slow_command=FALSE;
thd->set_time();
VOID(pthread_mutex_lock(&LOCK_thread_count));
......
This diff is collapsed.
......@@ -7350,7 +7350,7 @@ static void test_fetch_column()
rc = mysql_query(mysql, "insert into test_column(c2) values('venu'),('mysql')");
myquery(rc);
stmt = mysql_prepare(mysql,"select * from test_column",50);
stmt = mysql_prepare(mysql,"select * from test_column order by c2 desc", 50);
mystmt_init(stmt);
bind[0].buffer_type= MYSQL_TYPE_LONG;
......
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