summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <malff/marcsql@weblab.(none)>2007-09-04 12:25:54 -0600
committerunknown <malff/marcsql@weblab.(none)>2007-09-04 12:25:54 -0600
commitc458f7f6a3b6cf6754c9843d6801c9b0f86ffe48 (patch)
treee709d9cce3d8a2b77acda70637623f1faf7eb64f /sql
parent943ed8fe354734fa52417bf36920462ab23756d3 (diff)
parent6bfae914a2a0e7661c75c030b0bf88c8490323ed (diff)
downloadmariadb-git-c458f7f6a3b6cf6754c9843d6801c9b0f86ffe48.tar.gz
Merge weblab.(none):/home/marcsql/TREE/mysql-5.1-base
into weblab.(none):/home/marcsql/TREE/mysql-5.1-rt50-merge mysql-test/r/sp.result: Auto merged mysql-test/t/mysql.test: Auto merged mysql-test/t/query_cache.test: Auto merged mysql-test/t/sp.test: Auto merged sql/item_cmpfunc.h: Auto merged sql/rpl_utility.h: Auto merged sql/sql_base.cc: Auto merged sql/sql_cache.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_ndbcluster_binlog.h6
-rw-r--r--sql/handler.cc4
-rw-r--r--sql/item_cmpfunc.h18
-rw-r--r--sql/lock.cc2
-rw-r--r--sql/log.cc56
-rw-r--r--sql/mysql_priv.h7
-rw-r--r--sql/rpl_utility.h2
-rw-r--r--sql/sp.cc142
-rw-r--r--sql/sp.h11
-rw-r--r--sql/sp_head.cc25
-rw-r--r--sql/sql_base.cc38
-rw-r--r--sql/sql_class.cc7
-rw-r--r--sql/sql_class.h79
-rw-r--r--sql/sql_db.cc183
-rw-r--r--sql/sql_handler.cc6
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc45
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_prepare.cc48
-rw-r--r--sql/sql_yacc.yy330
21 files changed, 652 insertions, 365 deletions
diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h
index b5b8d0d9745..53852028f08 100644
--- a/sql/ha_ndbcluster_binlog.h
+++ b/sql/ha_ndbcluster_binlog.h
@@ -216,10 +216,12 @@ inline void free_share(NDB_SHARE **share, bool have_lock= FALSE)
inline
Thd_ndb *
-get_thd_ndb(THD *thd) { return (Thd_ndb *) thd->ha_data[ndbcluster_hton->slot]; }
+get_thd_ndb(THD *thd)
+{ return (Thd_ndb *) thd_get_ha_data(thd, ndbcluster_hton); }
inline
void
-set_thd_ndb(THD *thd, Thd_ndb *thd_ndb) { thd->ha_data[ndbcluster_hton->slot]= thd_ndb; }
+set_thd_ndb(THD *thd, Thd_ndb *thd_ndb)
+{ thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); }
Ndb* check_ndb_in_thd(THD* thd);
diff --git a/sql/handler.cc b/sql/handler.cc
index 2cb3c5aa348..75c3a64bc27 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -561,7 +561,7 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
be rolled back already
*/
if (hton->state == SHOW_OPTION_YES && hton->close_connection &&
- thd->ha_data[hton->slot])
+ thd_get_ha_data(thd, hton))
hton->close_connection(hton, thd);
return FALSE;
}
@@ -1509,7 +1509,7 @@ void handler::ha_statistic_increment(ulong SSV::*offset) const
void **handler::ha_data(THD *thd) const
{
- return (void **) thd->ha_data + ht->slot;
+ return thd_ha_data(thd, ht);
}
THD *handler::ha_thd(void) const
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 4015e5618e8..40d0c2dfe91 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1424,6 +1424,7 @@ public:
Item_cond(List<Item> &nlist)
:Item_bool_func(), list(nlist), abort_on_null(0) {}
bool add(Item *item) { return list.push_back(item); }
+ bool add_at_head(Item *item) { return list.push_front(item); }
void add_at_head(List<Item> *nlist) { list.prepand(nlist); }
bool fix_fields(THD *, Item **ref);
@@ -1616,6 +1617,15 @@ public:
Item *neg_transformer(THD *thd);
};
+inline bool is_cond_and(Item *item)
+{
+ if (item->type() != Item::COND_ITEM)
+ return FALSE;
+
+ Item_cond *cond_item= (Item_cond*) item;
+ return (cond_item->functype() == Item_func::COND_AND_FUNC);
+}
+
class Item_cond_or :public Item_cond
{
public:
@@ -1637,6 +1647,14 @@ public:
Item *neg_transformer(THD *thd);
};
+inline bool is_cond_or(Item *item)
+{
+ if (item->type() != Item::COND_ITEM)
+ return FALSE;
+
+ Item_cond *cond_item= (Item_cond*) item;
+ return (cond_item->functype() == Item_func::COND_OR_FUNC);
+}
/*
XOR is Item_cond, not an Item_int_func because we could like to
diff --git a/sql/lock.cc b/sql/lock.cc
index d9e9dd31f81..29a07858bc1 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -276,6 +276,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd->lock_id)];
if (rc > 1) /* a timeout or a deadlock */
{
+ if (sql_lock->table_count)
+ VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
my_error(rc, MYF(0));
my_free((uchar*) sql_lock,MYF(0));
sql_lock= 0;
diff --git a/sql/log.cc b/sql/log.cc
index 74a210a35b3..95204e89d0e 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -656,8 +656,14 @@ int Log_to_csv_event_handler::
table= open_performance_schema_table(thd, & table_list,
& open_tables_backup);
- result= (table ? 0 : 1);
- close_performance_schema_table(thd, & open_tables_backup);
+ if (table)
+ {
+ result= 0;
+ close_performance_schema_table(thd, & open_tables_backup);
+ }
+ else
+ result= 1;
+
DBUG_RETURN(result);
}
@@ -1215,10 +1221,10 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos)
{
DBUG_ENTER("binlog_trans_log_savepos");
DBUG_ASSERT(pos != NULL);
- if (thd->ha_data[binlog_hton->slot] == NULL)
+ if (thd_get_ha_data(thd, binlog_hton) == NULL)
thd->binlog_setup_trx_data();
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
DBUG_ASSERT(mysql_bin_log.is_open());
*pos= trx_data->position();
DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
@@ -1247,12 +1253,12 @@ binlog_trans_log_truncate(THD *thd, my_off_t pos)
DBUG_ENTER("binlog_trans_log_truncate");
DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
- DBUG_ASSERT(thd->ha_data[binlog_hton->slot] != NULL);
+ DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
/* Only true if binlog_trans_log_savepos() wasn't called before */
DBUG_ASSERT(pos != ~(my_off_t) 0);
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
trx_data->truncate(pos);
DBUG_VOID_RETURN;
}
@@ -1283,9 +1289,9 @@ int binlog_init(void *p)
static int binlog_close_connection(handlerton *hton, THD *thd)
{
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
DBUG_ASSERT(trx_data->empty());
- thd->ha_data[binlog_hton->slot]= 0;
+ thd_set_ha_data(thd, binlog_hton, NULL);
trx_data->~binlog_trx_data();
my_free((uchar*)trx_data, MYF(0));
return 0;
@@ -1408,7 +1414,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
{
DBUG_ENTER("binlog_commit");
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
if (trx_data->empty())
{
@@ -1435,7 +1441,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
DBUG_ENTER("binlog_rollback");
int error=0;
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
if (trx_data->empty()) {
trx_data->reset();
@@ -3251,23 +3257,22 @@ int THD::binlog_setup_trx_data()
{
DBUG_ENTER("THD::binlog_setup_trx_data");
binlog_trx_data *trx_data=
- (binlog_trx_data*) ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
if (trx_data)
DBUG_RETURN(0); // Already set up
- ha_data[binlog_hton->slot]= trx_data=
- (binlog_trx_data*) my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL));
+ trx_data= (binlog_trx_data*) my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL));
if (!trx_data ||
open_cached_file(&trx_data->trans_log, mysql_tmpdir,
LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
{
my_free((uchar*)trx_data, MYF(MY_ALLOW_ZERO_PTR));
- ha_data[binlog_hton->slot]= 0;
DBUG_RETURN(1); // Didn't manage to set it up
}
+ thd_set_ha_data(this, binlog_hton, trx_data);
- trx_data= new (ha_data[binlog_hton->slot]) binlog_trx_data;
+ trx_data= new (thd_get_ha_data(this, binlog_hton)) binlog_trx_data;
DBUG_RETURN(0);
}
@@ -3303,7 +3308,7 @@ int THD::binlog_setup_trx_data()
void
THD::binlog_start_trans_and_stmt()
{
- binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot];
+ binlog_trx_data *trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
DBUG_ENTER("binlog_start_trans_and_stmt");
DBUG_PRINT("enter", ("trx_data: 0x%lx trx_data->before_stmt_pos: %lu",
(long) trx_data,
@@ -3323,7 +3328,7 @@ THD::binlog_start_trans_and_stmt()
void THD::binlog_set_stmt_begin() {
binlog_trx_data *trx_data=
- (binlog_trx_data*) ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
/*
The call to binlog_trans_log_savepos() might create the trx_data
@@ -3333,14 +3338,15 @@ void THD::binlog_set_stmt_begin() {
*/
my_off_t pos= 0;
binlog_trans_log_savepos(this, &pos);
- trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot];
+ trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
trx_data->before_stmt_pos= pos;
}
int THD::binlog_flush_transaction_cache()
{
DBUG_ENTER("binlog_flush_transaction_cache");
- binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot];
+ binlog_trx_data *trx_data= (binlog_trx_data*)
+ thd_get_ha_data(this, binlog_hton);
DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data));
if (trx_data)
DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu",
@@ -3403,7 +3409,7 @@ Rows_log_event*
THD::binlog_get_pending_rows_event() const
{
binlog_trx_data *const trx_data=
- (binlog_trx_data*) ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
/*
This is less than ideal, but here's the story: If there is no
trx_data, prepare_pending_rows_event() has never been called
@@ -3416,11 +3422,11 @@ THD::binlog_get_pending_rows_event() const
void
THD::binlog_set_pending_rows_event(Rows_log_event* ev)
{
- if (ha_data[binlog_hton->slot] == NULL)
+ if (thd_get_ha_data(this, binlog_hton) == NULL)
binlog_setup_trx_data();
binlog_trx_data *const trx_data=
- (binlog_trx_data*) ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
DBUG_ASSERT(trx_data);
trx_data->set_pending(ev);
@@ -3443,7 +3449,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
int error= 0;
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
DBUG_ASSERT(trx_data);
@@ -3594,7 +3600,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
goto err;
binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
IO_CACHE *trans_log= &trx_data->trans_log;
my_off_t trans_log_pos= my_b_tell(trans_log);
if (event_info->get_cache_stmt() || trans_log_pos != 0)
@@ -5031,7 +5037,7 @@ int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid)
DBUG_ENTER("TC_LOG_BINLOG::log");
Xid_log_event xle(thd, xid);
binlog_trx_data *trx_data=
- (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
/*
We always commit the entire transaction when writing an XID. Also
note that the return value is inverted.
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 375782787a3..47a42354423 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -937,9 +937,16 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
char *new_table_name, char *new_table_alias,
bool skip_error);
+
bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
bool force_switch);
+bool mysql_opt_change_db(THD *thd,
+ const LEX_STRING *new_db_name,
+ LEX_STRING *saved_db_name,
+ bool force_switch,
+ bool *cur_db_changed);
+
void mysql_parse(THD *thd, const char *inBuf, uint length,
const char ** semicolon);
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 4fd38022da0..26edbdd1405 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -241,8 +241,8 @@ public:
private:
ulong m_size; // Number of elements in the types array
field_type *m_type; // Array of type descriptors
- uint16 *m_field_metadata;
uint m_field_metadata_size;
+ uint16 *m_field_metadata;
uchar *m_null_bits;
uchar *m_memory;
};
diff --git a/sql/sp.cc b/sql/sp.cc
index 437ffb3b484..6032688f7f1 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -520,9 +520,10 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
{
LEX *old_lex= thd->lex, newlex;
String defstr;
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged;
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ bool cur_db_changed;
ulong old_sql_mode= thd->variables.sql_mode;
ha_rows old_select_limit= thd->variables.select_limit;
sp_rcontext *old_spcont= thd->spcont;
@@ -567,16 +568,17 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
}
/*
- Change current database if needed.
+ Change the current database (if needed).
- collation_database will be updated here. However, it can be wrong,
- because it will contain the current value of the database collation.
- We need collation_database to be fixed at the creation time -- so
- we'll update it later in switch_query_ctx().
+ TODO: why do we force switch here?
*/
- if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged)))
+ if (mysql_opt_change_db(thd, &name->m_db, &saved_cur_db_name, TRUE,
+ &cur_db_changed))
+ {
+ ret= SP_INTERNAL_ERROR;
goto end;
+ }
thd->spcont= NULL;
@@ -585,34 +587,42 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
lex_start(thd);
- if (parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL)
- {
- sp_head *sp= newlex.sphead;
+ ret= parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL;
- if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
- goto end;
- delete sp;
- ret= SP_PARSE_ERROR;
+ /*
+ Force switching back to the saved current database (if changed),
+ because it may be NULL. In this case, mysql_change_db() would
+ generate an error.
+ */
+
+ if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
+ {
+ delete newlex.sphead;
+ ret= SP_INTERNAL_ERROR;
+ goto end;
}
- else
+
+ if (ret)
{
- if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
- goto end;
- *sphp= newlex.sphead;
- (*sphp)->set_definer(&definer_user_name, &definer_host_name);
- (*sphp)->set_info(created, modified, &chistics, sql_mode);
- (*sphp)->set_creation_ctx(creation_ctx);
- (*sphp)->optimize();
- /*
- Not strictly necessary to invoke this method here, since we know
- that we've parsed CREATE PROCEDURE/FUNCTION and not an
- UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
- maintain the invariant that this method is called for each
- distinct statement, in case its logic is extended with other
- types of analyses in future.
- */
- newlex.set_trg_event_type_for_tables();
+ delete newlex.sphead;
+ ret= SP_PARSE_ERROR;
+ goto end;
}
+
+ *sphp= newlex.sphead;
+ (*sphp)->set_definer(&definer_user_name, &definer_host_name);
+ (*sphp)->set_info(created, modified, &chistics, sql_mode);
+ (*sphp)->set_creation_ctx(creation_ctx);
+ (*sphp)->optimize();
+ /*
+ Not strictly necessary to invoke this method here, since we know
+ that we've parsed CREATE PROCEDURE/FUNCTION and not an
+ UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+ maintain the invariant that this method is called for each
+ distinct statement, in case its logic is extended with other
+ types of analyses in future.
+ */
+ newlex.set_trg_event_type_for_tables();
}
end:
@@ -2025,69 +2035,3 @@ create_string(THD *thd, String *buf,
buf->append(body, bodylen);
return TRUE;
}
-
-
-
-/*
- Change the current database if needed.
-
- SYNOPSIS
- sp_use_new_db()
- thd thread handle
- new_db new database name (a string and its length)
- old_db [IN] str points to a buffer where to store the old
- database, length contains the size of the buffer
- [OUT] if old db was not NULL, its name is copied
- to the buffer pointed at by str and length is updated
- accordingly. Otherwise str[0] is set to '\0' and length
- is set to 0. The out parameter should be used only if
- the database name has been changed (see dbchangedp).
- dbchangedp [OUT] is set to TRUE if the current database is changed,
- FALSE otherwise. A database is not changed if the old
- name is the same as the new one, both names are empty,
- or an error has occurred.
-
- RETURN VALUE
- 0 success
- 1 access denied or out of memory (the error message is
- set in THD)
-*/
-
-int
-sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
- bool no_access_check, bool *dbchangedp)
-{
- int ret;
- DBUG_ENTER("sp_use_new_db");
- DBUG_PRINT("enter", ("newdb: %s", new_db.str));
-
- /*
- A stored routine always belongs to some database. The
- old database (old_db) might be NULL, but to restore the
- old database we will use mysql_change_db.
- */
- DBUG_ASSERT(new_db.str && new_db.length);
-
- if (thd->db)
- {
- old_db->length= (strmake(old_db->str, thd->db, old_db->length) -
- old_db->str);
- }
- else
- {
- old_db->str[0]= '\0';
- old_db->length= 0;
- }
-
- /* Don't change the database if the new name is the same as the old one. */
- if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0)
- {
- *dbchangedp= FALSE;
- DBUG_RETURN(0);
- }
-
- ret= mysql_change_db(thd, &new_db, no_access_check);
-
- *dbchangedp= ret == 0;
- DBUG_RETURN(ret);
-}
diff --git a/sql/sp.h b/sql/sp.h
index 52b0344a2e2..3797eb289a4 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -85,15 +85,4 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
*/
TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
-
-/*
- Do a "use new_db". The current db is stored at old_db. If new_db is the
- same as the current one, nothing is changed. dbchangedp is set to true if
- the db was actually changed.
-*/
-
-int
-sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
- bool no_access_check, bool *dbchangedp);
-
#endif /* _SP_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index b8535ee9958..828517011d5 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -249,11 +249,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_GRANT:
+ case SQLCOM_REVOKE:
case SQLCOM_BEGIN:
case SQLCOM_RENAME_TABLE:
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_VIEW:
case SQLCOM_DROP_TRIGGER:
@@ -1013,9 +1016,10 @@ bool
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged;
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ bool cur_db_changed= FALSE;
sp_rcontext *ctx;
bool err_status= FALSE;
uint ip= 0;
@@ -1070,8 +1074,11 @@ sp_head::execute(THD *thd)
*/
if (m_db.length &&
- (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged)))
+ (err_status= mysql_opt_change_db(thd, &m_db, &saved_cur_db_name, FALSE,
+ &cur_db_changed)))
+ {
goto done;
+ }
if ((ctx= thd->spcont))
ctx->clear_handler();
@@ -1252,14 +1259,14 @@ sp_head::execute(THD *thd)
If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed
*/
- if (dbchanged)
+ if (cur_db_changed && !thd->killed)
{
/*
- No access check when changing back to where we came from.
- (It would generate an error from mysql_change_db() when old_db=="")
+ Force switching back to the saved current database, because it may be
+ NULL. In this case, mysql_change_db() would generate an error.
*/
- if (! thd->killed)
- err_status|= mysql_change_db(thd, &old_db, TRUE);
+
+ err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
}
m_flags&= ~IS_INVOKED;
DBUG_PRINT("info",
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 309d3061a2f..337fde53dac 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -7747,6 +7747,9 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
*/
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
}
+ else
+ thd->restore_backup_open_tables_state(backup);
+
thd->utime_after_lock= save_utime_after_lock;
DBUG_RETURN(table);
}
@@ -7762,24 +7765,25 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup)
{
bool found_old_table;
- if (thd->lock)
- {
- /*
- Note:
- We do not create explicitly a separate transaction for the
- performance table I/O, but borrow the current transaction.
- lock + unlock will autocommit the change done in the
- performance schema table: this is the expected result.
- The current transaction should not be affected by this code.
- TODO: Note that if a transactional engine is used for log tables,
- this code will need to be revised, as a separate transaction
- might be needed.
- */
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
- }
+ /*
+ If open_performance_schema_table() fails,
+ this function should not be called.
+ */
+ DBUG_ASSERT(thd->lock != NULL);
- safe_mutex_assert_not_owner(&LOCK_open);
+ /*
+ Note:
+ We do not create explicitly a separate transaction for the
+ performance table I/O, but borrow the current transaction.
+ lock + unlock will autocommit the change done in the
+ performance schema table: this is the expected result.
+ The current transaction should not be affected by this code.
+ TODO: Note that if a transactional engine is used for log tables,
+ this code will need to be revised, as a separate transaction
+ might be needed.
+ */
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
pthread_mutex_lock(&LOCK_open);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 55789a3a233..66a51d5bb00 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -387,7 +387,6 @@ THD::THD()
init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
stmt_arena= this;
thread_stack= 0;
- db= 0;
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
@@ -395,7 +394,7 @@ THD::THD()
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
- db_length= col_access=0;
+ col_access=0;
query_error= thread_specific_used= FALSE;
hash_clear(&handler_tables_hash);
tmp_table=0;
@@ -2040,7 +2039,9 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
lex(lex_arg),
query(0),
query_length(0),
- cursor(0)
+ cursor(0),
+ db(NULL),
+ db_length(0)
{
name.str= NULL;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7117c08a7e1..7875870bd1a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -593,6 +593,22 @@ public:
uint32 query_length; // current query length
Server_side_cursor *cursor;
+ /**
+ Name of the current (default) database.
+
+ If there is the current (default) database, "db" contains its name. If
+ there is no current (default) database, "db" is NULL and "db_length" is
+ 0. In other words, "db", "db_length" must either be NULL, or contain a
+ valid database name.
+
+ @note this attribute is set and alloced by the slave SQL thread (for
+ the THD of that thread); that thread is (and must remain, for now) the
+ only responsible for freeing this member.
+ */
+
+ char *db;
+ uint db_length;
+
public:
/* This constructor is called for backup statements */
@@ -1024,18 +1040,21 @@ public:
*/
char *thread_stack;
+ /**
+ Currently selected catalog.
+ */
+ char *catalog;
+
/*
- db - currently selected database
- catalog - currently selected catalog
- WARNING: some members of THD (currently 'db', 'catalog' and 'query') are
- set and alloced by the slave SQL thread (for the THD of that thread); that
- thread is (and must remain, for now) the only responsible for freeing these
- 3 members. If you add members here, and you add code to set them in
- replication, don't forget to free_them_and_set_them_to_0 in replication
- properly. For details see the 'err:' label of the handle_slave_sql()
- in sql/slave.cc.
- */
- char *db, *catalog;
+ WARNING: some members of THD (currently 'Statement::db',
+ 'catalog' and 'query') are set and alloced by the slave SQL thread
+ (for the THD of that thread); that thread is (and must remain, for now)
+ the only responsible for freeing these 3 members. If you add members
+ here, and you add code to set them in replication, don't forget to
+ free_them_and_set_them_to_0 in replication properly. For details see
+ the 'err:' label of the handle_slave_sql() in sql/slave.cc.
+ */
+
Security_context main_security_ctx;
Security_context *security_ctx;
@@ -1390,7 +1409,6 @@ public:
uint tmp_table, global_read_lock;
uint server_status,open_options;
enum enum_thread_type system_thread;
- uint db_length;
uint select_number; //number of select (used for EXPLAIN)
/* variables.transaction_isolation is reset to this after each commit */
enum_tx_isolation session_tx_isolation;
@@ -1802,11 +1820,26 @@ public:
}
}
- /*
- Initialize the current database from a NULL-terminated string with length
- If we run out of memory, we free the current database and return TRUE.
- This way the user will notice the error as there will be no current
- database selected (in addition to the error message set by malloc).
+ /**
+ Set the current database; use deep copy of C-string.
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ Initialize the current database from a NULL-terminated string with
+ length. If we run out of memory, we free the current database and
+ return TRUE. This way the user will notice the error as there will be
+ no current database selected (in addition to the error message set by
+ malloc).
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+
+ @return Operation status
+ @retval FALSE Success
+ @retval TRUE Out-of-memory error
*/
bool set_db(const char *new_db, size_t new_db_len)
{
@@ -1821,6 +1854,18 @@ public:
db_length= db ? new_db_len : 0;
return new_db && !db;
}
+
+ /**
+ Set the current database; use shallow copy of C-string.
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+ */
void reset_db(char *new_db, size_t new_db_len)
{
db= new_db;
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 15506fcbc80..cd7ad048802 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -39,6 +39,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
+static void mysql_change_db_impl(THD *thd,
+ LEX_STRING *new_db_name,
+ ulong new_db_access,
+ CHARSET_INFO *new_db_charset);
/* Database lock hash */
@@ -997,7 +1001,7 @@ exit:
it to 0.
*/
if (thd->db && !strcmp(thd->db, db))
- thd->set_db(NULL, 0);
+ mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
start_waiting_global_read_lock(thd);
exit2:
@@ -1355,22 +1359,108 @@ static void mysql_change_db_impl(THD *thd,
}
+
/**
- @brief Change the current database.
+ Backup the current database name before switch.
+
+ @param[in] thd thread handle
+ @param[in, out] saved_db_name IN: "str" points to a buffer where to store
+ the old database name, "length" contains the
+ buffer size
+ OUT: if the current (default) database is
+ not NULL, its name is copied to the
+ buffer pointed at by "str"
+ and "length" is updated accordingly.
+ Otherwise "str" is set to NULL and
+ "length" is set to 0.
+*/
+
+static void backup_current_db_name(THD *thd,
+ LEX_STRING *saved_db_name)
+{
+ if (!thd->db)
+ {
+ /* No current (default) database selected. */
+
+ saved_db_name->str= NULL;
+ saved_db_name->length= 0;
+ }
+ else
+ {
+ strmake(saved_db_name->str, thd->db, saved_db_name->length);
+ saved_db_name->length= thd->db_length;
+ }
+}
+
+
+/**
+ Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
+
+ The function allows to compare database names according to the MySQL
+ rules. The database names db1 and db2 are equal if:
+ - db1 is NULL and db2 is NULL;
+ or
+ - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
+ db2 in system character set (UTF8).
+*/
+
+static inline bool
+cmp_db_names(const char *db1_name,
+ const char *db2_name)
+{
+ return
+ /* db1 is NULL and db2 is NULL */
+ !db1_name && !db2_name ||
+
+ /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
+ db1_name && db2_name &&
+ my_strcasecmp(system_charset_info, db1_name, db2_name) == 0;
+}
+
+
+/**
+ @brief Change the current database and its attributes unconditionally.
@param thd thread handle
@param new_db_name database name
- @param force_switch if this flag is set (TRUE), mysql_change_db() will
- switch to NULL db if the specified database is not
- available anymore. Corresponding warning will be
- thrown in this case. This flag is used to change
- database in stored-routine-execution code.
-
- @details Check that the database name corresponds to a valid and existent
- database, check access rights (unless called with no_access_check), and
- set the current database. This function is called to change the current
- database upon user request (COM_CHANGE_DB command) or temporarily, to
- execute a stored routine.
+ @param force_switch if force_switch is FALSE, then the operation will fail if
+
+ - new_db_name is NULL or empty;
+
+ - OR new database name is invalid
+ (check_db_name() failed);
+
+ - OR user has no privilege on the new database;
+
+ - OR new database does not exist;
+
+ if force_switch is TRUE, then
+
+ - if new_db_name is NULL or empty, the current
+ database will be NULL, @@collation_database will
+ be set to @@collation_server, the operation will
+ succeed.
+
+ - if new database name is invalid
+ (check_db_name() failed), the current database
+ will be NULL, @@collation_database will be set to
+ @@collation_server, but the operation will fail;
+
+ - user privileges will not be checked
+ (THD::db_access however is updated);
+
+ TODO: is this really the intention?
+ (see sp-security.test).
+
+ - if new database does not exist,the current database
+ will be NULL, @@collation_database will be set to
+ @@collation_server, a warning will be thrown, the
+ operation will succeed.
+
+ @details The function checks that the database name corresponds to a
+ valid and existent database, checks access rights and changes the current
+ database with database attributes (@@collation_database session variable,
+ THD::db_access).
This function is not the only way to switch the database that is
currently employed. When the replication slave thread switches the
@@ -1407,8 +1497,13 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (force_switch)
{
/*
- This can only happen when we restore the old db in THD after
- execution of a routine is complete. Change db to NULL.
+ This can happen only if we're switching the current database back
+ after loading stored program. The thing is that loading of stored
+ program can happen when there is no current database.
+
+ TODO: actually, new_db_name and new_db_name->str seem to be always
+ non-NULL. In case of stored program, new_db_name->str == "" and
+ new_db_name->length == 0.
*/
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
@@ -1426,7 +1521,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (my_strcasecmp(system_charset_info, new_db_name->str,
INFORMATION_SCHEMA_NAME.str) == 0)
{
- /* Switch database to INFORMATION_SCHEMA. */
+ /* Switch the current database to INFORMATION_SCHEMA. */
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL,
system_charset_info);
@@ -1453,8 +1548,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
even if we are called from sp_head::execute().
It's next to impossible however to get this error when we are called
- from sp_head::execute(). But let's switch database to NULL in this case
- to be sure.
+ from sp_head::execute(). But let's switch the current database to NULL
+ in this case to be sure.
*/
if (check_db_name(&new_db_file_name))
@@ -1463,10 +1558,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
my_free(new_db_file_name.str, MYF(0));
if (force_switch)
- {
- /* Change db to NULL. */
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
- }
+
DBUG_RETURN(TRUE);
}
@@ -1501,6 +1594,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
{
if (force_switch)
{
+ /* Throw a warning and free new_db_file_name. */
+
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
new_db_file_name.str);
@@ -1511,12 +1606,19 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ /* The operation succeed. */
+
DBUG_RETURN(FALSE);
}
else
{
+ /* Report an error and free new_db_file_name. */
+
my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
my_free(new_db_file_name.str, MYF(0));
+
+ /* The operation failed. */
+
DBUG_RETURN(TRUE);
}
}
@@ -1534,6 +1636,43 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
}
+/**
+ Change the current database and its attributes if needed.
+
+ @param thd thread handle
+ @param new_db_name database name
+ @param[in, out] saved_db_name IN: "str" points to a buffer where to store
+ the old database name, "length" contains the
+ buffer size
+ OUT: if the current (default) database is
+ not NULL, its name is copied to the
+ buffer pointed at by "str"
+ and "length" is updated accordingly.
+ Otherwise "str" is set to NULL and
+ "length" is set to 0.
+ @param force_switch @see mysql_change_db()
+ @param[out] cur_db_changed out-flag to indicate whether the current
+ database has been changed (valid only if
+ the function suceeded)
+*/
+
+bool mysql_opt_change_db(THD *thd,
+ const LEX_STRING *new_db_name,
+ LEX_STRING *saved_db_name,
+ bool force_switch,
+ bool *cur_db_changed)
+{
+ *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str);
+
+ if (!*cur_db_changed)
+ return FALSE;
+
+ backup_current_db_name(thd, saved_db_name);
+
+ return mysql_change_db(thd, new_db_name, force_switch);
+}
+
+
static int
lock_databases(THD *thd, const char *db1, uint length1,
const char *db2, uint length2)
@@ -1829,7 +1968,7 @@ bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db)
/* Step9: Let's do "use newdb" if we renamed the current database */
if (change_to_newdb)
- error|= mysql_change_db(thd, new_db, 0);
+ error|= mysql_change_db(thd, new_db, FALSE);
exit:
pthread_mutex_lock(&LOCK_lock_db);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 8b9896870c0..1adce48c539 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -444,7 +444,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
cond->cleanup(); // File was reopened
if ((!cond->fixed &&
cond->fix_fields(thd, &cond)) || cond->check_cols(1))
- goto err0;
+ goto err;
}
if (keyname)
@@ -452,13 +452,13 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
{
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
- goto err0;
+ goto err;
}
}
if (insert_fields(thd, &thd->lex->select_lex.context,
tables->db, tables->alias, &it, 0))
- goto err0;
+ goto err;
protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 157309a3366..ebbf4cafb19 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -397,7 +397,7 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
if (duplic == DUP_UPDATE ||
duplic == DUP_REPLACE && *lock_type == TL_WRITE_CONCURRENT_INSERT)
{
- *lock_type= TL_WRITE;
+ *lock_type= TL_WRITE_DEFAULT;
return;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 66f5540d286..6b94505e46e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -323,7 +323,6 @@ void lex_start(THD *thd)
lex->length=0;
lex->part_info= 0;
lex->select_lex.in_sum_expr=0;
- lex->select_lex.expr_list.empty();
lex->select_lex.ftfunc_list_alloc.empty();
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
lex->select_lex.group_list.empty();
@@ -720,6 +719,7 @@ static inline uint int_token(const char *str,uint length)
int MYSQLlex(void *arg, void *yythd)
{
reg1 uchar c;
+ bool comment_closed;
int tokval, result_state;
uint length;
enum my_lex_states state;
@@ -1212,7 +1212,10 @@ int MYSQLlex(void *arg, void *yythd)
/*
The special comment format is very strict:
'/' '*' '!', followed by exactly
- 2 digits (major), then 3 digits (minor).
+ 1 digit (major), 2 digits (minor), then 2 digits (dot).
+ 32302 -> 3.23.02
+ 50032 -> 5.0.32
+ 50114 -> 5.1.14
*/
char version_str[6];
version_str[0]= lip->yyPeekn(0);
@@ -1231,7 +1234,7 @@ int MYSQLlex(void *arg, void *yythd)
ulong version;
version=strtol(version_str, NULL, 10);
- /* Accept 'M' 'M' 'm' 'm' 'm' */
+ /* Accept 'M' 'm' 'm' 'd' 'd' */
lip->yySkipn(5);
if (version <= MYSQL_VERSION_ID)
@@ -1255,16 +1258,36 @@ int MYSQLlex(void *arg, void *yythd)
lip->yySkip(); // Accept /
lip->yySkip(); // Accept *
}
-
- while (! lip->eof() &&
- ((c=lip->yyGet()) != '*' || lip->yyPeek() != '/'))
+ /*
+ Discard:
+ - regular '/' '*' comments,
+ - special comments '/' '*' '!' for a future version,
+ by scanning until we find a closing '*' '/' marker.
+ Note: There is no such thing as nesting comments,
+ the first '*' '/' sequence seen will mark the end.
+ */
+ comment_closed= FALSE;
+ while (! lip->eof())
{
- if (c == '\n')
+ c= lip->yyGet();
+ if (c == '*')
+ {
+ if (lip->yyPeek() == '/')
+ {
+ lip->yySkip();
+ comment_closed= TRUE;
+ state = MY_LEX_START;
+ break;
+ }
+ }
+ else if (c == '\n')
lip->yylineno++;
}
- if (! lip->eof())
- lip->yySkip(); // remove last '/'
+ /* Unbalanced comments with a missing '*' '/' are a syntax error */
+ if (! comment_closed)
+ return (ABORT_SYM);
state = MY_LEX_START; // Try again
+ lip->in_comment= NO_COMMENT;
lip->set_echo(TRUE);
break;
case MY_LEX_END_LONG_COMMENT:
@@ -1316,6 +1339,9 @@ int MYSQLlex(void *arg, void *yythd)
lip->set_echo(FALSE);
lip->yySkip();
lip->set_echo(TRUE);
+ /* Unbalanced comments with a missing '*' '/' are a syntax error */
+ if (lip->in_comment != NO_COMMENT)
+ return (ABORT_SYM);
lip->next_state=MY_LEX_END; // Mark for next loop
return(END_OF_INPUT);
}
@@ -1556,7 +1582,6 @@ void st_select_lex::init_select()
options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0;
- expr_list.empty();
interval_list.empty();
ftfunc_list_alloc.empty();
inner_sum_func_list= 0;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 057d59d3ccf..da0134a7f72 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -596,7 +596,6 @@ public:
const char *type; /* type of select for EXPLAIN */
SQL_LIST order_list; /* ORDER clause */
- List<List_item> expr_list;
SQL_LIST *gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
// Arrays of pointers to top elements of all_fields list
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 2e4ce65f1c4..bbd6cb16d11 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3381,6 +3381,8 @@ end_with_restore_list:
}
case SQLCOM_REVOKE_ALL:
{
+ if (end_active_trans(thd))
+ goto error;
if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
check_global_access(thd,CREATE_USER_ACL))
break;
@@ -3392,6 +3394,9 @@ end_with_restore_list:
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
+ if (end_active_trans(thd))
+ goto error;
+
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
first_table ? first_table->db : select_lex->db,
first_table ? &first_table->grant.privilege : 0,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 6b60f89b8e3..9337a2aa329 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2868,6 +2868,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
init_param_array(this);
lex->set_trg_event_type_for_tables();
+ /* Remember the current database. */
+
+ if (thd->db && thd->db_length)
+ {
+ db= this->strmake(thd->db, thd->db_length);
+ db_length= thd->db_length;
+ }
+ else
+ {
+ db= NULL;
+ db_length= 0;
+ }
+
/*
While doing context analysis of the query (in check_prepared_statement)
we allocate a lot of additional memory: for open tables, JOINs, derived
@@ -2974,6 +2987,13 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
Query_arena *old_stmt_arena;
bool error= TRUE;
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ bool cur_db_changed;
+
+ LEX_STRING stmt_db_name= { db, db_length };
+
status_var_increment(thd->status_var.com_stmt_execute);
/* Check if we got an error when sending long data */
@@ -3022,6 +3042,21 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
*/
thd->set_n_backup_statement(this, &stmt_backup);
+
+ /*
+ Change the current database (if needed).
+
+ Force switching, because the database of the prepared statement may be
+ NULL (prepared statements can be created while no current database
+ selected).
+ */
+
+ if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
+ &cur_db_changed))
+ goto error;
+
+ /* Allocate query. */
+
if (expanded_query->length() &&
alloc_query(thd, (char*) expanded_query->ptr(),
expanded_query->length()+1))
@@ -3050,6 +3085,8 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
thd->protocol= protocol; /* activate stmt protocol */
+ /* Go! */
+
if (open_cursor)
error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
&result, &cursor);
@@ -3068,6 +3105,17 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
}
}
+ /*
+ Restore the current database (if changed).
+
+ Force switching back to the saved current database (if changed),
+ because it may be NULL. In this case, mysql_change_db() would generate
+ an error.
+ */
+
+ if (cur_db_changed)
+ mysql_change_db(thd, &saved_cur_db_name, TRUE);
+
thd->protocol= &thd->protocol_text; /* use normal protocol */
/* Assert that if an error, no cursor is open */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2d6baf5b846..efd8c20e6d7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -31,7 +31,7 @@
#define MYSQL_YACC
#define YYINITDEPTH 100
-#define YYMAXDEPTH 3200 /* Because of 64K stack */
+#define YYMAXDEPTH 3200 /* Because of 64K stack */
#define Lex (YYTHD->lex)
#define Select Lex->current_select
#include "mysql_priv.h"
@@ -506,12 +506,12 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%}
-%pure_parser /* We have threads */
+%pure_parser /* We have threads */
/*
- Currently there is 286 shift/reduce conflict. We should not introduce
- new conflicts any more.
+ Currently there are 280 shift/reduce conflicts.
+ We should not introduce new conflicts any more.
*/
-%expect 286
+%expect 280
/*
Comments for TOKENS.
@@ -1091,7 +1091,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
/* A dummy token to force the priority of table_ref production in a join. */
%left TABLE_REF_PRIORITY
%left SET_VAR
-%left OR_OR_SYM OR_SYM OR2_SYM XOR
+%left OR_OR_SYM OR_SYM OR2_SYM
+%left XOR
%left AND_SYM AND_AND_SYM
%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM
@@ -1104,6 +1105,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%left NEG '~'
%right NOT_SYM NOT2_SYM
%right BINARY COLLATE_SYM
+%left INTERVAL_SYM
%type <lex_str>
IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM
@@ -1152,8 +1154,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <item>
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
- variable variable_aux bool_term bool_factor bool_test bool_pri
- predicate bit_expr bit_term bit_factor value_expr term factor
+ variable variable_aux bool_pri
+ predicate bit_expr
table_wild simple_expr udf_expr
expr_or_default set_expr_or_default interval_expr
param_marker geometry_function
@@ -1171,7 +1173,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
NUM_literal
%type <item_list>
- expr_list udf_expr_list udf_expr_list2 when_list
+ expr_list opt_udf_expr_list udf_expr_list when_list
ident_list ident_list_arg opt_expr_list
%type <var_type>
@@ -1245,7 +1247,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- expr_list2 udf_expr_list3 handler
+ handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
@@ -1315,10 +1317,11 @@ rule: <-- starts at col 1
}
; <-- on a line by itself, starts at col 9
-Also, please do not use any <TAB>, but spaces.
-Having a uniform indentation in this file helps
-code reviews, patches, merges, and make maintenance easier.
-Thanks.
+ Also, please do not use any <TAB>, but spaces.
+ Having a uniform indentation in this file helps
+ code reviews, patches, merges, and make maintenance easier.
+ Tip: grep [[:cntrl:]] sql_yacc.yy
+ Thanks.
*/
query:
@@ -5157,7 +5160,7 @@ opt_bin_charset:
MYSQL_YYABORT;
}
}
- | charset charset_name { Lex->charset=$2; }
+ | charset charset_name { Lex->charset=$2; }
;
opt_primary:
@@ -5379,7 +5382,7 @@ alter:
lex->create_info.row_type= ROW_TYPE_NOT_USED;
lex->alter_info.reset();
lex->no_write_to_binlog= 0;
- lex->create_info.storage_media= HA_SM_DEFAULT;
+ lex->create_info.storage_media= HA_SM_DEFAULT;
}
alter_commands
{}
@@ -6564,83 +6567,131 @@ optional_braces:
;
/* all possible expressions */
-expr:
- bool_term { Select->expr_list.push_front(new List<Item>); }
- bool_or_expr
+expr:
+ expr or expr %prec OR_SYM
{
- List<Item> *list= Select->expr_list.pop();
- if (list->elements)
+ /*
+ Design notes:
+ Do not use a manually maintained stack like thd->lex->xxx_list,
+ but use the internal bison stack ($$, $1 and $3) instead.
+ Using the bison stack is:
+ - more robust to changes in the grammar,
+ - guaranteed to be in sync with the parser state,
+ - better for performances (no memory allocation).
+ */
+ Item_cond_or *item1;
+ Item_cond_or *item3;
+ if (is_cond_or($1))
{
- list->push_front($1);
- $$= new Item_cond_or(*list);
- /* optimize construction of logical OR to reduce
- amount of objects for complex expressions */
+ item1= (Item_cond_or*) $1;
+ if (is_cond_or($3))
+ {
+ item3= (Item_cond_or*) $3;
+ /*
+ (X1 OR X2) OR (Y1 OR Y2) ==> OR (X1, X2, Y1, Y2)
+ */
+ item3->add_at_head(item1->argument_list());
+ $$ = $3;
+ }
+ else
+ {
+ /*
+ (X1 OR X2) OR Y ==> OR (X1, X2, Y)
+ */
+ item1->add($3);
+ $$ = $1;
+ }
+ }
+ else if (is_cond_or($3))
+ {
+ item3= (Item_cond_or*) $3;
+ /*
+ X OR (Y1 OR Y2) ==> OR (X, Y1, Y2)
+ */
+ item3->add_at_head($1);
+ $$ = $3;
}
else
- $$= $1;
- delete list;
+ {
+ /* X OR Y */
+ $$ = new (YYTHD->mem_root) Item_cond_or($1, $3);
+ }
}
- ;
-
-bool_or_expr:
- /* empty */
- | bool_or_expr or bool_term
- { Select->expr_list.head()->push_back($3); }
- ;
-
-bool_term:
- bool_term XOR bool_term { $$= new Item_cond_xor($1,$3); }
- | bool_factor { Select->expr_list.push_front(new List<Item>); }
- bool_and_expr
+ | expr XOR expr %prec XOR
+ {
+ /* XOR is a proprietary extension */
+ $$ = new (YYTHD->mem_root) Item_cond_xor($1, $3);
+ }
+ | expr and expr %prec AND_SYM
{
- List<Item> *list= Select->expr_list.pop();
- if (list->elements)
+ /* See comments in rule expr: expr or expr */
+ Item_cond_and *item1;
+ Item_cond_and *item3;
+ if (is_cond_and($1))
{
- list->push_front($1);
- $$= new Item_cond_and(*list);
- /* optimize construction of logical AND to reduce
- amount of objects for complex expressions */
+ item1= (Item_cond_and*) $1;
+ if (is_cond_and($3))
+ {
+ item3= (Item_cond_and*) $3;
+ /*
+ (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2)
+ */
+ item3->add_at_head(item1->argument_list());
+ $$ = $3;
+ }
+ else
+ {
+ /*
+ (X1 AND X2) AND Y ==> AND (X1, X2, Y)
+ */
+ item1->add($3);
+ $$ = $1;
+ }
+ }
+ else if (is_cond_and($3))
+ {
+ item3= (Item_cond_and*) $3;
+ /*
+ X AND (Y1 AND Y2) ==> AND (X, Y1, Y2)
+ */
+ item3->add_at_head($1);
+ $$ = $3;
}
else
- $$= $1;
- delete list;
+ {
+ /* X AND Y */
+ $$ = new (YYTHD->mem_root) Item_cond_and($1, $3);
+ }
}
- ;
-
-bool_and_expr:
- /* empty */
- | bool_and_expr and bool_factor
- { Select->expr_list.head()->push_back($3); }
- ;
-
-bool_factor:
- NOT_SYM bool_factor { $$= negate_expression(YYTHD, $2); }
- | bool_test
- ;
-
-bool_test:
- bool_pri IS TRUE_SYM
+ | NOT_SYM expr %prec NOT_SYM
+ { $$= negate_expression(YYTHD, $2); }
+ | bool_pri IS TRUE_SYM %prec IS
{ $$= new (YYTHD->mem_root) Item_func_istrue($1); }
- | bool_pri IS not TRUE_SYM
+ | bool_pri IS not TRUE_SYM %prec IS
{ $$= new (YYTHD->mem_root) Item_func_isnottrue($1); }
- | bool_pri IS FALSE_SYM
+ | bool_pri IS FALSE_SYM %prec IS
{ $$= new (YYTHD->mem_root) Item_func_isfalse($1); }
- | bool_pri IS not FALSE_SYM
+ | bool_pri IS not FALSE_SYM %prec IS
{ $$= new (YYTHD->mem_root) Item_func_isnotfalse($1); }
- | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); }
- | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); }
+ | bool_pri IS UNKNOWN_SYM %prec IS
+ { $$= new Item_func_isnull($1); }
+ | bool_pri IS not UNKNOWN_SYM %prec IS
+ { $$= new Item_func_isnotnull($1); }
| bool_pri
;
bool_pri:
- bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); }
- | bool_pri IS not NULL_SYM { $$= new Item_func_isnotnull($1); }
- | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); }
+ bool_pri IS NULL_SYM %prec IS
+ { $$= new Item_func_isnull($1); }
+ | bool_pri IS not NULL_SYM %prec IS
+ { $$= new Item_func_isnotnull($1); }
+ | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
+ { $$= new Item_func_equal($1,$3); }
| bool_pri comp_op predicate %prec EQ
{ $$= (*$2)(0)->create($1,$3); }
| bool_pri comp_op all_or_any '(' subselect ')' %prec EQ
{ $$= all_any_subquery_creator($1, $2, $3, $5); }
- | predicate
+ | predicate ;
;
predicate:
@@ -6700,44 +6751,34 @@ predicate:
;
bit_expr:
- bit_expr '|' bit_term { $$= new Item_func_bit_or($1,$3); }
- | bit_term
- ;
-
-bit_term:
- bit_term '&' bit_factor { $$= new Item_func_bit_and($1,$3); }
- | bit_factor
- ;
-
-bit_factor:
- bit_factor SHIFT_LEFT value_expr
+ bit_expr '|' bit_expr %prec '|'
+ { $$= new Item_func_bit_or($1,$3); }
+ | bit_expr '&' bit_expr %prec '&'
+ { $$= new Item_func_bit_and($1,$3); }
+ | bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT
{ $$= new Item_func_shift_left($1,$3); }
- | bit_factor SHIFT_RIGHT value_expr
+ | bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT
{ $$= new Item_func_shift_right($1,$3); }
- | value_expr
- ;
-
-value_expr:
- value_expr '+' term { $$= new Item_func_plus($1,$3); }
- | value_expr '-' term { $$= new Item_func_minus($1,$3); }
- | value_expr '+' interval_expr interval
+ | bit_expr '+' bit_expr %prec '+'
+ { $$= new Item_func_plus($1,$3); }
+ | bit_expr '-' bit_expr %prec '-'
+ { $$= new Item_func_minus($1,$3); }
+ | bit_expr '+' interval_expr interval %prec '+'
{ $$= new Item_date_add_interval($1,$3,$4,0); }
- | value_expr '-' interval_expr interval
+ | bit_expr '-' interval_expr interval %prec '-'
{ $$= new Item_date_add_interval($1,$3,$4,1); }
- | term
- ;
-
-term:
- term '*' factor { $$= new Item_func_mul($1,$3); }
- | term '/' factor { $$= new Item_func_div($1,$3); }
- | term '%' factor { $$= new Item_func_mod($1,$3); }
- | term DIV_SYM factor { $$= new Item_func_int_div($1,$3); }
- | term MOD_SYM factor { $$= new Item_func_mod($1,$3); }
- | factor
- ;
-
-factor:
- factor '^' simple_expr { $$= new Item_func_bit_xor($1,$3); }
+ | bit_expr '*' bit_expr %prec '*'
+ { $$= new Item_func_mul($1,$3); }
+ | bit_expr '/' bit_expr %prec '/'
+ { $$= new Item_func_div($1,$3); }
+ | bit_expr '%' bit_expr %prec '%'
+ { $$= new Item_func_mod($1,$3); }
+ | bit_expr DIV_SYM bit_expr %prec DIV_SYM
+ { $$= new Item_func_int_div($1,$3); }
+ | bit_expr MOD_SYM bit_expr %prec MOD_SYM
+ { $$= new Item_func_mod($1,$3); }
+ | bit_expr '^' bit_expr
+ { $$= new Item_func_bit_xor($1,$3); }
| simple_expr
;
@@ -6776,7 +6817,8 @@ all_or_any:
;
interval_expr:
- INTERVAL_SYM expr { $$=$2; }
+ INTERVAL_SYM expr %prec INTERVAL_SYM
+ { $$=$2; }
;
simple_expr:
@@ -6799,7 +6841,7 @@ simple_expr:
| sum_expr
| simple_expr OR_OR_SYM simple_expr
{ $$= new (YYTHD->mem_root) Item_func_concat($1, $3); }
- | '+' simple_expr %prec NEG { $$= $2; }
+ | '+' simple_expr %prec NEG { $$= $2; }
| '-' simple_expr %prec NEG
{ $$= new (YYTHD->mem_root) Item_func_neg($2); }
| '~' simple_expr %prec NEG
@@ -7210,7 +7252,7 @@ function_call_generic:
$<udf>$= udf;
#endif
}
- udf_expr_list ')'
+ opt_udf_expr_list ')'
{
THD *thd= YYTHD;
Create_func *builder;
@@ -7307,27 +7349,23 @@ opt_query_expansion:
| WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; }
;
+opt_udf_expr_list:
+ /* empty */ { $$= NULL; }
+ | udf_expr_list { $$= $1; }
+ ;
+
udf_expr_list:
- /* empty */ { $$= NULL; }
- | udf_expr_list2 { $$= $1;}
- ;
-
-udf_expr_list2:
- { Select->expr_list.push_front(new List<Item>); }
- udf_expr_list3
- { $$= Select->expr_list.pop(); }
- ;
-
-udf_expr_list3:
- udf_expr
- {
- Select->expr_list.head()->push_back($1);
- }
- | udf_expr_list3 ',' udf_expr
- {
- Select->expr_list.head()->push_back($3);
- }
- ;
+ udf_expr
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ $$->push_back($1);
+ }
+ | udf_expr_list ',' udf_expr
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
udf_expr:
remember_name expr remember_end select_alias
@@ -7525,13 +7563,17 @@ opt_expr_list:
;
expr_list:
- { Select->expr_list.push_front(new List<Item>); }
- expr_list2
- { $$= Select->expr_list.pop(); };
-
-expr_list2:
- expr { Select->expr_list.head()->push_back($1); }
- | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); };
+ expr
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ $$->push_back($1);
+ }
+ | expr_list ',' expr
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
ident_list_arg:
ident_list { $$= $1; }
@@ -7539,13 +7581,17 @@ ident_list_arg:
;
ident_list:
- { Select->expr_list.push_front(new List<Item>); }
- ident_list2
- { $$= Select->expr_list.pop(); };
-
-ident_list2:
- simple_ident { Select->expr_list.head()->push_back($1); }
- | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); };
+ simple_ident
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ $$->push_back($1);
+ }
+ | ident_list ',' simple_ident
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
opt_expr:
/* empty */ { $$= NULL; }