summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <konstantin@oak.local>2003-12-20 02:16:10 +0300
committerunknown <konstantin@oak.local>2003-12-20 02:16:10 +0300
commit9c6eed72f04c15711d4de70bb8eb3f02bac8c30c (patch)
treec9441e15b696703bb24911352787db69a6784020
parent0b098472371384f074706e51bd619ed911015d06 (diff)
downloadmariadb-git-9c6eed72f04c15711d4de70bb8eb3f02bac8c30c.tar.gz
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.
-rw-r--r--libmysqld/lib_sql.cc87
-rw-r--r--sql/mysql_priv.h4
-rw-r--r--sql/slave.cc1
-rw-r--r--sql/sql_class.cc140
-rw-r--r--sql/sql_class.h208
-rw-r--r--sql/sql_parse.cc8
-rw-r--r--sql/sql_prepare.cc593
-rw-r--r--tests/client_test.c2
8 files changed, 613 insertions, 430 deletions
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 89012a84857..365e9bc820a 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -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);
-}
-
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 863c1bdd419..ff50993ad50 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -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;
diff --git a/sql/slave.cc b/sql/slave.cc
index 8af38624df6..d2a7d397fa7 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -3111,7 +3111,6 @@ slave_begin:
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);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 0469497790a..606ecdbecbb 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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
@@ -233,22 +228,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
SYNOPSIS
@@ -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);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 2a7523c890b..d663521b296 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -328,30 +328,6 @@ public:
};
-/* 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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
void init(void);
void change_user(void);
- void init_for_queries();
void cleanup(void);
bool store_globals();
#ifdef SIGNAL_WITH_VIO_CLOSE
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3f2325de703..85659f2d2a2 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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));
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index bb5f27672fe..f60e0db58d0 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -71,127 +71,110 @@ Long data handling:
#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
+#ifdef EMBEDDED_LIBRARY
+/* include MYSQL_BIND headers */
+#include <mysql.h>
+#endif
-#define IS_PARAM_NULL(pos, param_no) (pos[param_no/8] & (1 << (param_no & 7)))
+const String my_null_string("NULL", 4, default_charset_info);
-#define STMT_QUERY_LOG_LENGTH 8192
+/******************************************************************************
+ Prepared_statement: statement which can contain placeholders
+******************************************************************************/
-#ifdef EMBEDDED_LIBRARY
-#define SETUP_PARAM_FUNCTION(fn_name) \
-static void fn_name(Item_param *param, uchar **pos, ulong data_len)
+class Prepared_statement: public Statement
+{
+public:
+ THD *thd;
+ Item_param **param; /* array of all placeholders */
+ 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)(Prepared_statement *st, uchar *pos, uchar *read_pos);
#else
-#define SETUP_PARAM_FUNCTION(fn_name) \
-static void fn_name(Item_param *param, uchar **pos)
+ bool (*setup_params_data)(Prepared_statement *st);
#endif
+public:
+ Prepared_statement(THD *thd_arg);
+ virtual ~Prepared_statement();
+ virtual Statement::Type type() const;
+};
-String my_null_string("NULL", 4, default_charset_info);
-/*
- Find prepared statement in thd
+/******************************************************************************
+ Implementation
+******************************************************************************/
- SYNOPSIS
- find_prepared_statement()
- thd Thread handler
- stmt_id Statement id server specified to the client on prepare
- RETURN VALUES
- 0 error. In this case the error is sent with my_error()
- ptr Pointer to statement
-*/
-
-static PREP_STMT *find_prepared_statement(THD *thd, ulong stmt_id,
- const char *when)
+inline bool is_param_null(const uchar *pos, ulong param_no)
{
- PREP_STMT *stmt;
- DBUG_ENTER("find_prepared_statement");
- DBUG_PRINT("enter",("stmt_id: %d", stmt_id));
-
- if (thd->last_prepared_stmt && thd->last_prepared_stmt->stmt_id == stmt_id)
- DBUG_RETURN(thd->last_prepared_stmt);
- if ((stmt= (PREP_STMT*) tree_search(&thd->prepared_statements, &stmt_id,
- (void*) 0)))
- DBUG_RETURN (thd->last_prepared_stmt= stmt);
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, when);
- DBUG_RETURN(0);
+ return pos[param_no/8] & (1 << (param_no & 7));
}
-/*
- Compare two prepared statements; Used to find a prepared statement
-*/
+enum { STMT_QUERY_LOG_LENGTH= 8192 };
-int compare_prep_stmt(void *not_used, PREP_STMT *stmt, ulong *key)
-{
- return (stmt->stmt_id == *key) ? 0 : (stmt->stmt_id < *key) ? -1 : 1;
-}
+#ifdef EMBEDDED_LIBRARY
+#define SETUP_PARAM_FUNCTION(fn_name) \
+static void fn_name(Item_param *param, uchar **pos, ulong data_len)
+#else
+#define SETUP_PARAM_FUNCTION(fn_name) \
+static void fn_name(Item_param *param, uchar **pos)
+#endif
/*
- Free prepared statement.
-
- SYNOPSIS
- standard tree_element_free function.
-
- DESCRIPTION
- We don't have to free the stmt itself as this was stored in the tree
- and will be freed when the node is deleted
+ Seek prepared statement in statement map by id: returns zero if statement
+ was not found, pointer otherwise.
*/
-void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used)
-{
- my_free((char *)stmt->param, MYF(MY_ALLOW_ZERO_PTR));
- if (stmt->query)
- stmt->query->free();
- free_items(stmt->free_list);
- free_root(&stmt->mem_root, MYF(0));
+static Prepared_statement *
+find_prepared_statement(THD *thd, ulong id, const char *where)
+{
+ 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);
+ send_error(thd);
+ return 0;
+ }
+ return (Prepared_statement *) stmt;
}
+
/*
Send prepared stmt info to client after prepare
*/
#ifndef EMBEDDED_LIBRARY
-static bool send_prep_stmt(PREP_STMT *stmt, uint columns)
+static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{
- NET *net=&stmt->thd->net;
+ NET *net= &stmt->thd->net;
char buff[9];
buff[0]= 0;
- int4store(buff+1, stmt->stmt_id);
+ int4store(buff+1, stmt->id);
int2store(buff+5, columns);
int2store(buff+7, stmt->param_count);
- /* This should be fixed to work with prepared statements
- */
+ /* This should be fixed to work with prepared statements */
return (my_net_write(net, buff, sizeof(buff)) || net_flush(net));
}
#else
-static bool send_prep_stmt(PREP_STMT *stmt, uint columns __attribute__((unused)))
+static bool send_prep_stmt(Prepared_statement *stmt,
+ uint columns __attribute__((unused)))
{
THD *thd= stmt->thd;
- thd->client_stmt_id= stmt->stmt_id;
+ thd->client_stmt_id= stmt->id;
thd->client_param_count= stmt->param_count;
thd->net.last_errno= 0;
return 0;
}
-#endif /*!EMBEDDED_LIBRAYR*/
-
-/*
- Send information about all item parameters
+#endif /*!EMBEDDED_LIBRARY*/
- TODO: Not yet ready
-*/
-
-static bool send_item_params(PREP_STMT *stmt)
-{
-#if 0
- char buff[1];
- buff[0]=0;
- if (my_net_write(&stmt->thd->net, buff, sizeof(buff)))
- return 1;
- send_eof(stmt->thd);
-#endif
- return 0;
-}
/*
Read the length of the parameter data and retun back to
@@ -418,17 +401,20 @@ void setup_param_functions(Item_param *param, uchar param_type)
and if binary/update log is set, generate the valid query.
*/
-static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
+static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
+ uchar *read_pos)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex->param_list;
+ List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
- DBUG_ENTER("insert_params_withlog");
- String str, query, *res;
+ String str, query;
+ const String *res;
+
+ DBUG_ENTER("insert_params_withlog");
- if (query.copy(*stmt->query))
+ if (query.copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
ulong param_no= 0;
@@ -438,10 +424,9 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
{
if (param->long_data_supplied)
res= param->query_val_str(&str);
-
else
{
- if (IS_PARAM_NULL(pos,param_no))
+ if (is_param_null(pos,param_no))
{
param->maybe_null= param->null_value= 1;
res= &my_null_string;
@@ -461,23 +446,26 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
}
if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
DBUG_RETURN(1);
+
DBUG_RETURN(0);
}
-static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
+
+static bool insert_params(Prepared_statement *stmt, uchar *pos,
+ uchar *read_pos)
{
- THD *thd= stmt->thd;
- List<Item> &params= thd->lex->param_list;
+ List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
- DBUG_ENTER("insert_params");
-
ulong param_no= 0;
+
+ DBUG_ENTER("insert_params");
+
while ((param= (Item_param *)param_iterator++))
{
if (!param->long_data_supplied)
{
- if (IS_PARAM_NULL(pos,param_no))
+ if (is_param_null(pos,param_no))
param->maybe_null= param->null_value= 1;
else
{
@@ -490,17 +478,18 @@ static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
DBUG_RETURN(0);
}
-static bool setup_params_data(PREP_STMT *stmt)
+static bool setup_params_data(Prepared_statement *stmt)
{
- THD *thd= stmt->thd;
- List<Item> &params= thd->lex->param_list;
+ List<Item> &params= stmt->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
- DBUG_ENTER("setup_params_data");
- uchar *pos=(uchar*) thd->net.read_pos+1+MYSQL_STMT_HEADER; //skip header
+ 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");
+
if (*read_pos++) //types supplied / first execute
{
/*
@@ -518,6 +507,91 @@ static bool setup_params_data(PREP_STMT *stmt)
DBUG_RETURN(0);
}
+#else
+
+bool setup_params_data(Prepared_statement *stmt)
+{
+ List<Item> &params= stmt->lex->param_list;
+ List_iterator<Item> param_iterator(params);
+ Item_param *param;
+ MYSQL_BIND *client_param= stmt->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);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+bool setup_params_data_withlog(Prepared_statement *stmt)
+{
+ THD *thd= stmt->thd;
+ List<Item> &params= stmt->lex->param_list;
+ List_iterator<Item> param_iterator(params);
+ Item_param *param;
+ MYSQL_BIND *client_param= thd->client_params;
+
+ String str, query;
+ const String *res;
+
+ DBUG_ENTER("setup_params_data_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++)
+ {
+ 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;
+ }
+
+ if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
#endif /*!EMBEDDED_LIBRARY*/
/*
@@ -526,7 +600,7 @@ static bool setup_params_data(PREP_STMT *stmt)
- fields count
*/
-static bool mysql_test_insert_fields(PREP_STMT *stmt,
+static bool mysql_test_insert_fields(Prepared_statement *stmt,
TABLE_LIST *table_list,
List<Item> &fields,
List<List_item> &values_list)
@@ -535,18 +609,18 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt,
TABLE *table;
List_iterator_fast<List_item> its(values_list);
List_item *values;
+
DBUG_ENTER("mysql_test_insert_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- my_bool update=(thd->lex->value_list.elements ? UPDATE_ACL : 0);
- ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
+ my_bool update=(stmt->lex->value_list.elements ? UPDATE_ACL : 0);
+ ulong privilege= (stmt->lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
-
if (check_access(thd,privilege,table_list->db,
&table_list->grant.privilege,0,0) ||
(grant_option && check_grant(thd,privilege,table_list,0,0)))
DBUG_RETURN(1);
-#endif
+#endif
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(1);
table= table_list->table;
@@ -574,7 +648,7 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt,
}
}
}
- if (send_prep_stmt(stmt, 0) || send_item_params(stmt))
+ if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
DBUG_RETURN(0);
}
@@ -589,13 +663,14 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt,
and return no fields information back to client.
*/
-static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list,
+static bool mysql_test_upd_fields(Prepared_statement *stmt,
+ TABLE_LIST *table_list,
List<Item> &fields, List<Item> &values,
COND *conds)
{
THD *thd= stmt->thd;
- DBUG_ENTER("mysql_test_upd_fields");
+ DBUG_ENTER("mysql_test_upd_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_access(thd,UPDATE_ACL,table_list->db,
&table_list->grant.privilege,0,0) ||
@@ -613,7 +688,7 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list,
Currently return only column list info only, and we are not
sending any info on where clause.
*/
- if (send_prep_stmt(stmt, 0) || send_item_params(stmt))
+ if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
DBUG_RETURN(0);
}
@@ -630,7 +705,9 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list,
And send column list fields info back to client.
*/
-static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
+
+static bool mysql_test_select_fields(Prepared_statement *stmt,
+ TABLE_LIST *tables,
uint wild_num,
List<Item> &fields, COND *conds,
uint og_num, ORDER *order, ORDER *group,
@@ -640,8 +717,9 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
SELECT_LEX *select_lex)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->main_lex;
- select_result *result= thd->lex->result;
+ LEX *lex= stmt->lex;
+ select_result *result= lex->result;
+
DBUG_ENTER("mysql_test_select_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -663,12 +741,12 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
if (lex->describe)
{
- if (send_prep_stmt(stmt, 0) || send_item_params(stmt))
+ if (send_prep_stmt(stmt, 0))
DBUG_RETURN(1);
- }
+ }
else
{
- fix_tables_pointers(thd->lex->all_selects_list);
+ fix_tables_pointers(lex->all_selects_list);
if (!result && !(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
@@ -683,11 +761,11 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
select_lex, unit))
DBUG_RETURN(1);
if (send_prep_stmt(stmt, fields.elements) ||
- thd->protocol_simple.send_fields(&fields, 0) ||
+ thd->protocol_simple.send_fields(&fields, 0)
#ifndef EMBEDDED_LIBRARY
- net_flush(&thd->net) ||
+ || net_flush(&thd->net)
#endif
- send_item_params(stmt))
+ )
DBUG_RETURN(1);
join->cleanup();
}
@@ -699,19 +777,18 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
Send the prepare query results back to client
*/
-static bool send_prepare_results(PREP_STMT *stmt)
+static bool send_prepare_results(Prepared_statement *stmt)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->main_lex;
- enum enum_sql_command sql_command= thd->lex->sql_command;
+ LEX *lex= stmt->lex;
+ enum enum_sql_command sql_command= lex->sql_command;
+
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;
- stmt->free_list= thd->free_list; // Save items used in stmt
- thd->free_list= 0;
SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
@@ -769,56 +846,13 @@ abort:
}
/*
- Parse the prepare query
-*/
-
-static bool parse_prepare_query(PREP_STMT *stmt,
- char *packet, uint length)
-{
- bool error= 1;
- THD *thd= stmt->thd;
- DBUG_ENTER("parse_prepare_query");
-
- mysql_log.write(thd,COM_PREPARE,"%s",packet);
- mysql_init_query(thd);
- LEX *lex=lex_start(thd, (uchar*) packet, length);
- lex->safe_to_cache_query= 0;
- thd->lex->param_count= 0;
- if (!yyparse((void *)thd) && !thd->is_fatal_error)
- error= send_prepare_results(stmt);
- lex_end(lex);
- DBUG_RETURN(error);
-}
-
-/*
Initialize parameter items in statement
*/
-static bool init_param_items(PREP_STMT *stmt)
+static bool init_param_items(Prepared_statement *stmt)
{
- THD *thd= stmt->thd;
- List<Item> &params= thd->lex->param_list;
Item_param **to;
- uint32 length= thd->query_length;
- stmt->lex= thd->main_lex;
-
- if (mysql_bin_log.is_open() || mysql_update_log.is_open())
- {
- stmt->log_full_query= 1;
-#ifndef EMBEDDED_LIBRARY
- stmt->setup_params= insert_params_withlog;
-#else
- stmt->setup_params_data= setup_params_data_withlog;
-#endif
- }
- else
-#ifndef EMBEDDED_LIBRARY
- stmt->setup_params= insert_params; // not fully qualified query
-#else
- stmt->setup_params_data= setup_params_data;
-#endif
-
if (!stmt->param_count)
stmt->param= (Item_param **)0;
else
@@ -828,44 +862,12 @@ static bool init_param_items(PREP_STMT *stmt)
MYF(MY_WME))))
return 1;
- if (stmt->log_full_query)
- {
- length= thd->query_length+(stmt->param_count*2)+1;
-
- if ( length < STMT_QUERY_LOG_LENGTH )
- length= STMT_QUERY_LOG_LENGTH;
- }
- List_iterator<Item> param_iterator(params);
+ List_iterator<Item> param_iterator(stmt->lex->param_list);
while ((*(to++)= (Item_param *)param_iterator++));
}
- stmt->query= new String(length);
- stmt->query->copy(thd->query, thd->query_length, default_charset_info);
return 0;
}
-/*
- Initialize stmt execution
-*/
-
-static void init_stmt_execute(PREP_STMT *stmt)
-{
- THD *thd= stmt->thd;
- TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
-
- /*
- TODO: When the new table structure is ready, then have a status bit
- to indicate the table is altered, and re-do the setup_*
- and open the tables back.
- */
- for (; tables ; tables= tables->next)
- tables->table= 0; //safety - nasty init
-
- if (!(stmt->log_full_query && stmt->param_count))
- {
- thd->query= stmt->query->c_ptr();
- thd->query_length= stmt->query->length();
- }
-}
/*
Parse the query and send the total number of parameters
@@ -877,58 +879,73 @@ static void init_stmt_execute(PREP_STMT *stmt)
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
- retrieveal can be made without going through all field
+ retrieval can be made without going through all field
items.
*/
bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
{
- MEM_ROOT thd_root= thd->mem_root;
- PREP_STMT stmt;
- SELECT_LEX *sl;
+ LEX *lex;
+ Prepared_statement *stmt= new Prepared_statement(thd);
+
DBUG_ENTER("mysql_stmt_prepare");
- bzero((char*) &stmt, sizeof(stmt));
-
- stmt.stmt_id= ++thd->current_stmt_id;
- init_sql_alloc(&stmt.mem_root,
- thd->variables.query_alloc_block_size,
- thd->variables.query_prealloc_size);
-
- stmt.thd= thd;
- stmt.thd->mem_root= stmt.mem_root;
+ if (stmt == 0)
+ DBUG_RETURN(0);
+
+ if (thd->stmt_map.insert(stmt))
+ goto insert_stmt_err;
- if (alloc_query(stmt.thd, packet, packet_length))
- goto err;
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
- if (parse_prepare_query(&stmt, thd->query, thd->query_length))
- goto err;
+ if (alloc_query(thd, packet, packet_length))
+ goto alloc_query_err;
+
+ mysql_log.write(thd, COM_PREPARE, "%s", packet);
+
+ 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);
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;
+ for (SELECT_LEX *sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
sl->prep_where= sl->where;
}
-
- if (init_param_items(&stmt))
- goto err;
+ stmt->set_statement(thd);
+ thd->set_statement(&thd->stmt_backup);
+
+ if (init_param_items(stmt))
+ goto init_param_err;
+
+ stmt->command= COM_EXECUTE; // set it only once here
-
- stmt.mem_root= stmt.thd->mem_root;
- tree_insert(&thd->prepared_statements, (void *)&stmt, 0, (void *)0);
- thd->mem_root= thd_root; // restore main mem_root
DBUG_RETURN(0);
-err:
- stmt.mem_root= stmt.thd->mem_root;
- free_prep_stmt(&stmt, free_free, (void*) 0);
- thd->mem_root= thd_root; // restore main mem_root
+yyparse_err:
+ lex_end(lex);
+ stmt->set_statement(thd);
+ thd->set_statement(&thd->stmt_backup);
+init_param_err:
+alloc_query_err:
+ /* Statement map deletes statement on erase */
+ thd->stmt_map.erase(stmt);
+ DBUG_RETURN(1);
+insert_stmt_err:
+ delete stmt;
DBUG_RETURN(1);
}
@@ -936,7 +953,7 @@ err:
/*
Executes previously prepared query
- If there is any parameters(thd->param_count), then replace
+ If there is any parameters (stmt->param_count), then replace
markers with the data supplied from client, and then
execute the query
*/
@@ -944,15 +961,12 @@ err:
void mysql_stmt_execute(THD *thd, char *packet)
{
ulong stmt_id= uint4korr(packet);
- PREP_STMT *stmt;
- SELECT_LEX *sl;
- DBUG_ENTER("mysql_stmt_execute");
+ Prepared_statement *stmt;
- if (!(stmt=find_prepared_statement(thd, stmt_id, "execute")))
- {
- send_error(thd);
+ 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)
@@ -961,10 +975,28 @@ void mysql_stmt_execute(THD *thd, char *packet)
DBUG_VOID_RETURN;
}
- LEX thd_lex= thd->main_lex;
- thd->main_lex= stmt->lex;
-
- for (sl= stmt->lex.all_selects_list;
+ /*
+ XXX: while thd->query_id is incremented for each command, stmt->query_id
+ holds query_id of prepare stage. Keeping old query_id seems to be more
+ natural, but differs from the way prepared statements work in 4.1:
+ */
+ /* stmt->query_id= thd->query_id; */
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
+
+ /*
+ 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.
+ */
+ init_sql_alloc(&thd->mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+
+
+ for (SELECT_LEX *sl= stmt->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
@@ -975,7 +1007,16 @@ void mysql_stmt_execute(THD *thd, char *packet)
sl->where= sl->prep_where->copy_andor_structure(thd);
DBUG_ASSERT(sl->join == 0);
}
- init_stmt_execute(stmt);
+
+ /*
+ TODO: When the new table structure is ready, then have a status bit
+ to indicate the table is altered, and re-do the setup_*
+ and open the tables back.
+ */
+ for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first;
+ tables;
+ tables= tables->next)
+ tables->table= 0; // safety - nasty init
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count && setup_params_data(stmt))
@@ -1001,7 +1042,8 @@ void mysql_stmt_execute(THD *thd, char *packet)
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
- thd->main_lex= thd_lex;
+ free_root(&thd->mem_root, MYF(0));
+ thd->set_statement(&thd->stmt_backup);
DBUG_VOID_RETURN;
}
@@ -1023,14 +1065,12 @@ void mysql_stmt_execute(THD *thd, char *packet)
void mysql_stmt_reset(THD *thd, char *packet)
{
ulong stmt_id= uint4korr(packet);
- PREP_STMT *stmt;
+ Prepared_statement *stmt;
+
DBUG_ENTER("mysql_stmt_reset");
if (!(stmt= find_prepared_statement(thd, stmt_id, "reset")))
- {
- send_error(thd);
DBUG_VOID_RETURN;
- }
stmt->error_in_prepare= 0;
Item_param *item= *stmt->param, *end= item + stmt->param_count;
@@ -1053,15 +1093,15 @@ void mysql_stmt_reset(THD *thd, char *packet)
void mysql_stmt_free(THD *thd, char *packet)
{
ulong stmt_id= uint4korr(packet);
+ Prepared_statement *stmt;
+
DBUG_ENTER("mysql_stmt_free");
- if (!find_prepared_statement(thd, stmt_id, "close"))
- {
- send_error(thd); // Not seen by the client
+ if (!(stmt= find_prepared_statement(thd, stmt_id, "close")))
DBUG_VOID_RETURN;
- }
- tree_delete(&thd->prepared_statements, (void*) &stmt_id, (void *)0);
- thd->last_prepared_stmt= (PREP_STMT *)0;
+
+ /* Statement map deletes statement on erase */
+ thd->stmt_map.erase(stmt);
DBUG_VOID_RETURN;
}
@@ -1087,7 +1127,8 @@ void mysql_stmt_free(THD *thd, char *packet)
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
{
- PREP_STMT *stmt;
+ Prepared_statement *stmt;
+
DBUG_ENTER("mysql_stmt_get_longdata");
#ifndef EMBEDDED_LIBRARY
@@ -1103,14 +1144,7 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
uint param_number= uint2korr(pos+4);
if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata")))
- {
- /*
- There is a chance that the client will never see this as
- it doesn't expect an answer from this call...
- */
- send_error(thd);
DBUG_VOID_RETURN;
- }
#ifndef EMBEDDED_LIBRARY
if (param_number >= stmt->param_count)
@@ -1134,3 +1168,46 @@ void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
DBUG_VOID_RETURN;
}
+
+Prepared_statement::Prepared_statement(THD *thd_arg)
+ :Statement(thd_arg),
+ thd(thd_arg),
+ param(0),
+ param_count(0),
+ last_errno(0),
+ error_in_prepare(0),
+ long_data_used(0),
+ log_full_query(0)
+{
+ *last_error= '\0';
+ if (mysql_bin_log.is_open())
+ {
+ log_full_query= 1;
+#ifndef EMBEDDED_LIBRARY
+ setup_params= insert_params_withlog;
+#else
+ setup_params_data= setup_params_data_withlog;
+#endif
+ }
+ else
+#ifndef EMBEDDED_LIBRARY
+ setup_params= insert_params; // not fully qualified query
+#else
+ setup_params_data= setup_params_data;
+#endif
+}
+
+
+Prepared_statement::~Prepared_statement()
+{
+ my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR));
+ free_items(free_list);
+}
+
+
+Statement::Type Prepared_statement::type() const
+{
+ return PREPARED_STATEMENT;
+}
+
+
diff --git a/tests/client_test.c b/tests/client_test.c
index 464d5b632ae..66637dcb04b 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -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;