diff options
author | unknown <malff/marcsql@weblab.(none)> | 2007-09-04 12:25:54 -0600 |
---|---|---|
committer | unknown <malff/marcsql@weblab.(none)> | 2007-09-04 12:25:54 -0600 |
commit | c458f7f6a3b6cf6754c9843d6801c9b0f86ffe48 (patch) | |
tree | e709d9cce3d8a2b77acda70637623f1faf7eb64f /sql | |
parent | 943ed8fe354734fa52417bf36920462ab23756d3 (diff) | |
parent | 6bfae914a2a0e7661c75c030b0bf88c8490323ed (diff) | |
download | mariadb-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.h | 6 | ||||
-rw-r--r-- | sql/handler.cc | 4 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 18 | ||||
-rw-r--r-- | sql/lock.cc | 2 | ||||
-rw-r--r-- | sql/log.cc | 56 | ||||
-rw-r--r-- | sql/mysql_priv.h | 7 | ||||
-rw-r--r-- | sql/rpl_utility.h | 2 | ||||
-rw-r--r-- | sql/sp.cc | 142 | ||||
-rw-r--r-- | sql/sp.h | 11 | ||||
-rw-r--r-- | sql/sp_head.cc | 25 | ||||
-rw-r--r-- | sql/sql_base.cc | 38 | ||||
-rw-r--r-- | sql/sql_class.cc | 7 | ||||
-rw-r--r-- | sql/sql_class.h | 79 | ||||
-rw-r--r-- | sql/sql_db.cc | 183 | ||||
-rw-r--r-- | sql/sql_handler.cc | 6 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.cc | 45 | ||||
-rw-r--r-- | sql/sql_lex.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 5 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 48 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 330 |
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); -} @@ -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; } |