diff options
-rw-r--r-- | mysql-test/r/view.result | 2 | ||||
-rw-r--r-- | sql/item_subselect.cc | 17 | ||||
-rw-r--r-- | sql/item_subselect.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 30 | ||||
-rw-r--r-- | sql/sql_acl.cc | 36 | ||||
-rw-r--r-- | sql/sql_base.cc | 210 | ||||
-rw-r--r-- | sql/sql_delete.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 158 | ||||
-rw-r--r-- | sql/sql_lex.cc | 12 | ||||
-rw-r--r-- | sql/sql_parse.cc | 68 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 65 | ||||
-rw-r--r-- | sql/sql_show.cc | 6 | ||||
-rw-r--r-- | sql/sql_update.cc | 4 | ||||
-rw-r--r-- | sql/sql_view.cc | 278 | ||||
-rw-r--r-- | sql/table.cc | 36 | ||||
-rw-r--r-- | sql/table.h | 8 | ||||
-rw-r--r-- | tests/client_test.c | 5 |
18 files changed, 487 insertions, 454 deletions
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 98c04b31e96..37e9c8fc2fa 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -138,7 +138,6 @@ v3 VIEW v4 VIEW v5 VIEW v6 VIEW -vt1 VIEW show table status; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 MyISAM 9 Fixed 5 9 45 38654705663 1024 0 NULL # # NULL latin1_swedish_ci NULL @@ -148,7 +147,6 @@ v3 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL vie v4 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view v5 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view v6 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view -vt1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view drop view v1,v2,v3,v4,v5,v6; create view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 33e661ce46e..b1d6d7bbe35 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -990,23 +990,6 @@ Item_in_subselect::select_transformer(JOIN *join) } -Item_subselect::trans_res -Item_in_subselect::no_select_transform() -{ - DBUG_ENTER("Item_in_subselect::no_select_transform"); - // We have execute fix_fields() for left expression - SELECT_LEX *current= thd->lex->current_select, *up; - thd->lex->current_select= up= current->return_after_parsing(); - if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) - { - thd->lex->current_select= current; - DBUG_RETURN(RES_ERROR); - } - thd->lex->current_select= current; - DBUG_RETURN(RES_OK); -} - - void Item_in_subselect::print(String *str) { if (transformed) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index d50688e0b58..1268b435816 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -84,7 +84,6 @@ public: null_value= 1; } virtual trans_res select_transformer(JOIN *join); - virtual trans_res no_select_transform() { return RES_OK; } bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -220,7 +219,6 @@ public: was_null= 0; } trans_res select_transformer(JOIN *join); - trans_res no_select_transform(); trans_res single_value_transformer(JOIN *join, Comp_creator *func); trans_res row_value_transformer(JOIN * join); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ba19ee15ce6..ac0890c60df 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -398,8 +398,9 @@ void free_items(Item *item); void cleanup_items(Item *item); class THD; void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); -int check_one_table_access(THD *thd, ulong privilege, +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); +bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); int multi_update_precheck(THD *thd, TABLE_LIST *tables); @@ -764,12 +765,10 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, - const char *db_name, - const char *table_name); -TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, - const char *db_name, - const char *table_name); +TABLE_LIST *find_table_in_list(TABLE_LIST *table, + uint offset_to_list, + const char *db_name, + const char *table_name); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); void close_temporary(TABLE *table, bool delete_table=1); @@ -785,6 +784,23 @@ int fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors); int fill_record(Field **field,List<Item> &values, bool ignore_errors); OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild); +inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) +{ + return find_table_in_list(table, offsetof(TABLE_LIST, next_global), + db_name, table_name); +} + +inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) +{ + return find_table_in_list(table, offsetof(TABLE_LIST, next_local), + db_name, table_name); +} + + /* sql_calc.cc */ bool eval_const_cond(COND *cond); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 10a12ef6d04..394d7336c79 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2750,7 +2750,22 @@ void grant_reload(THD *thd) /**************************************************************************** Check table level grants - All errors are written directly to the client if no_errors is given ! + + SYNPOSIS + bool check_grant() + thd Thread handler + want_access Bits of privileges user needs to have + tables List of tables to check. The user should have 'want_access' + to all tables in list. + show_table <> 0 if we are in show table. In this case it's enough to have + any privilege for the table + number Check at most this number of tables. + no_errors If 0 then we write an error. The error is sent directly to + the client + + RETURN + 0 ok + 1 Error: User did not have the requested privielges ****************************************************************************/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, @@ -2758,14 +2773,17 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, { TABLE_LIST *table; char *user = thd->priv_user; + DBUG_ENTER("check_grant"); + DBUG_ASSERT(number > 0); - want_access &= ~thd->master_access; + want_access&= ~thd->master_access; if (!want_access) - return 0; // ok + DBUG_RETURN(0); // ok rw_rdlock(&LOCK_grant); for (table= tables; table && number--; table= table->next_global) { + GRANT_TABLE *grant_table; if (!(~table->grant.privilege & want_access) || table->derived) { /* @@ -2775,10 +2793,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, table->grant.want_privilege= 0; continue; // Already checked } - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, - table->db,user, - table->real_name,0); - if (!grant_table) + if (!(grant_table= table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0))) { want_access &= ~table->grant.privilege; goto err; // No grants @@ -2802,7 +2818,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } } rw_unlock(&LOCK_grant); - return 0; + DBUG_RETURN(0); err: rw_unlock(&LOCK_grant); @@ -2837,7 +2853,7 @@ err: thd->host_or_ip, table ? table->real_name : "unknown"); } - return 1; + DBUG_RETURN(1); } @@ -2931,7 +2947,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, if (!(grant_table= grant->grant_table)) goto err; /* purecov: inspected */ - for (; fields->end(); fields->next()) + for (; !fields->end_of_fields(); fields->next()) { const char *field_name= fields->name(); grant_column= column_hash_search(grant_table, field_name, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 781e7273e7a..6fdc8014db1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -548,81 +548,37 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } -#ifdef UNUSED -/* - Find first suitable table by alias in given list. - - SYNOPSIS - find_table_in_list() - table - pointer to table list - db_name - data base name or 0 for any - table_name - table name or 0 for any - - RETURN VALUES - NULL Table not found - # Pointer to found table. -*/ - -TABLE_LIST * find_table_in_list(TABLE_LIST *table, - const char *db_name, const char *table_name) -{ - for (; table; table= table->next) - if ((!db_name || !strcmp(table->db, db_name)) && - (!table_name || !my_strcasecmp(table_alias_charset, - table->alias, table_name))) - break; - return table; -} -#endif /*UNUSED*/ /* - Find real table in given global list. + Find table in list. SYNOPSIS - find_real_table_in_list() - table - pointer to table list - db_name - data base name - table_name - table name - - RETURN VALUES - NULL Table not found - # Pointer to found table. -*/ - -TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) -{ - for (; table; table= table->next_global) - if (!strcmp(table->db, db_name) && - !strcmp(table->real_name, table_name)) - break; - return table; -} - - -/* - Find real table in given local list. + find_table_in_list() + table Pointer to table list + offset Offset to which list in table structure to use + db_name Data base name + table_name Table name - SYNOPSIS - find_real_table_in_local_list() - table - pointer to table list - db_name - data base name - table_name - table name + NOTES: + This is called by find_table_in_local_list() and + find_table_in_global_list(). RETURN VALUES NULL Table not found # Pointer to found table. */ -TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) +TABLE_LIST *find_table_in_list(TABLE_LIST *table, + uint offset, + const char *db_name, + const char *table_name) { - for (; table; table= table->next_local) + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { if (!strcmp(table->db, db_name) && !strcmp(table->real_name, table_name)) break; + } return table; } @@ -1363,7 +1319,7 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) db Database name name Table name alias Alias name - table_desc TABLE_LIST descriptor + table_desc TABLE_LIST descriptor (used with views) mem_root temporary mem_root for parsing NOTES @@ -1403,10 +1359,10 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (!entry->crashed) { /* - Frm file could not be found on disk - Since it does not exist, no one can be using it - LOCK_open has been locked to protect from someone else - trying to discover the table at the same time. + Frm file could not be found on disk + Since it does not exist, no one can be using it + LOCK_open has been locked to protect from someone else + trying to discover the table at the same time. */ if (discover_retry_count++ != 0) goto err; @@ -1603,7 +1559,7 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) tables->table->grant= tables->grant; } thd->proc_info=0; - free_root(&new_frm_mem, MYF(0)); + free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block DBUG_RETURN(result); } @@ -1671,7 +1627,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) thd->proc_info="Opening table"; thd->current_tablenr= 0; - while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh) ; + while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh) + ; if (table) { @@ -1900,7 +1857,7 @@ bool rm_temporary_table(enum db_type base, char *path) ** return unique field ******************************************************************************/ -// Special Field pointers for find_field_in_tables returning +/* Special Field pointers for find_field_in_tables returning */ const Field *not_found_field= (Field*) 0x1; const Field *view_ref_found= (Field*) 0x2; @@ -1979,6 +1936,7 @@ Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, return fld; } + /* Find field in table @@ -2561,23 +2519,47 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, } -/**************************************************************************** - This just drops in all fields instead of current '*' field - Returns pointer to last inserted field if ok -****************************************************************************/ +/* + Drops in all fields instead of current '*' field + + SYNOPSIS + insert_fields() + thd Thread handler + tables List of tables + db_name Database name in case of 'database_name.table_name.*' + table_name Table name in case of 'table_name.*' + it Pointer to '*' + any_privileges 0 If we should ensure that we have SELECT privileges + for all columns + 1 If any privilege is ok + RETURN + 0 ok + 'it' is updated to point at last inserted + 1 error. The error message is sent to client +*/ bool insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it, bool any_privileges) { + /* allocate variables on stack to avoid pool alloaction */ + Field_iterator_table table_iter; + Field_iterator_view view_iter; uint found; DBUG_ENTER("insert_fields"); - found=0; + found= 0; for (; tables; tables= tables->next_local) { - TABLE *table=tables->table; + Field_iterator *iterator; + TABLE_LIST *natural_join_table; + Field *field; + TABLE_LIST *embedded; + TABLE_LIST *last; + TABLE_LIST *embedding; + TABLE *table= tables->table; + if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, tables->alias) && (!db_name || !strcmp(tables->db,db_name)))) @@ -2588,36 +2570,26 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, { if (tables->view) { - Field_iterator_view fields; - fields.set(tables); + view_iter.set(tables); if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant, tables->view_db.str, tables->view_name.str, - &fields)) - DBUG_RETURN(1); + &view_iter)) + goto err; } else { - Field_iterator_table fields; - fields.set(tables); + table_iter.set(tables); if (check_grant_all_columns(thd, SELECT_ACL, &table->grant, table->table_cache_key, table->real_name, - &fields)) - DBUG_RETURN(1); + &table_iter)) + goto err; } } #endif - /* allocate 2 variables on stack to avoid pool alloaction */ - Field_iterator_table table_iter; - Field_iterator_view view_iter; - Field_iterator *iterator; - TABLE_LIST *natural_join_table= 0; - Field *field; - - thd->used_tables|=table->map; - TABLE_LIST *embedded= tables; - TABLE_LIST *last= embedded; - TABLE_LIST *embedding; + natural_join_table= 0; + thd->used_tables|= table->map; + last= embedded= tables; while ((embedding= embedded->embedding) && embedding->join_list->elements != 1) @@ -2648,7 +2620,7 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, iterator= &table_iter; iterator->set(tables); - for (; iterator->end(); iterator->next()) + for (; !iterator->end_of_fields(); iterator->next()) { Item *not_used_item; uint not_used_field_index= NO_CACHED_FIELD_INDEX; @@ -2699,9 +2671,7 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, thd->host_or_ip, fld->field_name, tab); - /* TODO: should be removed to have only one send_error */ - send_error(thd); - DBUG_RETURN(1); + goto err; } } #endif @@ -2735,15 +2705,17 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, table->used_fields=table->fields; } } - if (!found) - { - if (!table_name) - my_error(ER_NO_TABLES_USED,MYF(0)); - else - my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name); - send_error(thd); - } - DBUG_RETURN(!found); + if (found) + DBUG_RETURN(0); + + if (!table_name) + my_error(ER_NO_TABLES_USED, MYF(0)); + else + my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name); + +err: + send_error(thd); + DBUG_RETURN(1); } @@ -2832,13 +2804,16 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) if (arena) thd->set_n_backup_item_arena(arena, &backup); + TABLE *t1=tab1->table; TABLE *t2=tab2->table; - /* allocate 2 variables on stack to avoid pool alloaction */ Field_iterator_table table_iter; Field_iterator_view view_iter; Field_iterator *iterator; + Field *t1_field, *t2_field; + Item *item_t2; Item_cond_and *cond_and=new Item_cond_and(); + if (!cond_and) // If not out of memory DBUG_RETURN(1); cond_and->top_level_item(); @@ -2854,9 +2829,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) table_iter.set(tab1); } - Field *t1_field, *t2_field; - Item *item_t2; - for (; iterator->end(); iterator->next()) + for (; !iterator->end_of_fields(); iterator->next()) { const char *t1_field_name= iterator->name(); uint not_used_field_index= NO_CACHED_FIELD_INDEX; @@ -3190,17 +3163,19 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) table_desc TABLE_LIST descriptor mem_root temporary MEM_ROOT for parsing */ + static my_bool open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root) { - DBUG_ENTER("open_new_frm"); LEX_STRING pathstr; - pathstr.str= (char *)path; + File_parser *parser; + DBUG_ENTER("open_new_frm"); + + pathstr.str= (char*) path; pathstr.length= strlen(path); - File_parser *parser= sql_parse_prepare(&pathstr, mem_root, 1); - if (parser) + if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) { if (!strncmp("VIEW", parser->type()->str, parser->type()->length)) { @@ -3217,10 +3192,7 @@ open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, bzero(outparam, sizeof(outparam)); // do not run repair DBUG_RETURN(1); } + DBUG_RETURN(0); } - else - { - DBUG_RETURN(1); - } - DBUG_RETURN(0); + DBUG_RETURN(1); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 910b673dc32..d9d60be162a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -282,7 +282,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(-1); } - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 33b99d4ccf9..7d3479a18bb 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -51,6 +51,8 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, ulong counter, bool check_unique) { TABLE *table= table_list->table; + int error; + if (fields.elements == 0 && values.elements != 0) { if (values.elements != table->fields) @@ -61,11 +63,11 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, return -1; } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option) { Field_iterator_table fields; fields.set_table(table); - if (grant_option && - check_grant_all_columns(thd, INSERT_ACL, &table->grant, + if (check_grant_all_columns(thd, INSERT_ACL, &table->grant, table->table_cache_key, table->real_name, &fields)) return -1; @@ -75,7 +77,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, } else { // Part field list - TABLE_LIST *save_next= table_list->next_local; + TABLE_LIST *save_next; if (fields.elements != values.elements) { my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, @@ -84,14 +86,13 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, return -1; } - table_list->next_local= 0; thd->dupp_field=0; - if (setup_fields(thd, 0, table_list, fields, 1, 0, 0)) - { - table_list->next_local= save_next; - return -1; - } + save_next= table_list->next_local; // fields only from first table + table_list->next_local= 0; + error= setup_fields(thd, 0, table_list, fields, 1, 0, 0); table_list->next_local= save_next; + if (error) + return -1; // setup_fields failed if (check_unique && thd->dupp_field) { @@ -407,7 +408,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, !thd->cuted_fields)) { thd->row_count_func= info.copied+info.deleted+info.updated; - send_ok(thd, (ulong) (ulong) thd->row_count_func, id); + send_ok(thd, (ulong) thd->row_count_func, id); } else { @@ -420,7 +421,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields); thd->row_count_func= info.copied+info.deleted+info.updated; - ::send_ok(thd, (ulong) thd->row_count_func, (ulonglong)id,buff); + ::send_ok(thd, (ulong) thd->row_count_func, id, buff); } free_underlaid_joins(thd, &thd->lex->select_lex); table->insert_values=0; @@ -444,46 +445,58 @@ abort: check_view_insertability() view - reference on VIEW + IMPLEMENTATION + A view is insertable if the folloings are true: + - All columns in the view are columns from a table + - All not used columns in table have a default values + - All field in view are unique (not referring to the same column) + RETURN FALSE - OK + view->contain_auto_increment is 1 if and only if the view contains an + auto_increment field + TRUE - can't be used for insert */ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) { - DBUG_ENTER("check_key_in_view"); - - uint i; + uint num= view->view->select_lex.item_list.elements; TABLE *table= view->table; - Item **trans= view->field_translation; + Item **trans_start= view->field_translation, **trans_end=trans_start+num; + Item **trans; Field **field_ptr= table->field; - uint num= view->view->select_lex.item_list.elements; ulong other_query_id= query_id - 1; + DBUG_ENTER("check_key_in_view"); + DBUG_ASSERT(view->table != 0 && view->field_translation != 0); view->contain_auto_increment= 0; /* check simplicity and prepare unique test of view */ - for (i= 0; i < num; i++) + for (trans= trans_start; trans != trans_end; trans++) { + Item_field *field; /* simple SELECT list entry (field without expression) */ - if (trans[i]->type() != Item::FIELD_ITEM) + if ((*trans)->type() != Item::FIELD_ITEM) DBUG_RETURN(TRUE); - if (((Item_field *)trans[i])->field->type() == Field::NEXT_NUMBER) + field= (Item_field *)(*trans); + if (field->field->unireg_check == Field::NEXT_NUMBER) view->contain_auto_increment= 1; /* prepare unique test */ - ((Item_field *)trans[i])->field->query_id= other_query_id; + field->field->query_id= other_query_id; } /* unique test */ - for (i= 0; i < num; i++) + for (trans= trans_start; trans != trans_end; trans++) { - Item_field *field= (Item_field *)trans[i]; + /* Thanks to test above, we know that all columns are of type Item_field */ + Item_field *field= (Item_field *)(*trans); if (field->field->query_id == query_id) DBUG_RETURN(TRUE); field->field->query_id= query_id; } /* VIEW contain all fields without default value */ - for (; *field_ptr; ++field_ptr) + for (; *field_ptr; field_ptr++) { Field *field= *field_ptr; /* field have not default value */ @@ -491,14 +504,13 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) (table->timestamp_field != field || field->unireg_check == Field::TIMESTAMP_UN_FIELD)) { - uint i= 0; - for (; i < num; i++) + for (trans= trans_start; ; trans++) { - if (((Item_field *)trans[i])->field == *field_ptr) - break; + if (trans == trans_end) + DBUG_RETURN(TRUE); // Field was not part of view + if (((Item_field *)(*trans))->field == *field_ptr) + break; // ok } - if (i >= num) - DBUG_RETURN(TRUE); } } DBUG_RETURN(FALSE); @@ -506,29 +518,28 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) /* - Prepare items in INSERT statement + Check if table can be updated SYNOPSIS - mysql_prepare_insert() - thd - thread handler - table_list - global/local table list - - RETURN VALUE - 0 - OK - -1 - error (message is not sent to user) + mysql_prepare_insert_check_table() + thd Thread handle + table_list Table list (only one table) + fields List of fields to be updated + where Pointer to where clause + + RETURN + 0 ok + 1 ERROR */ -int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, - List<Item> &fields, List_item *values, - List<Item> &update_fields, List<Item> &update_values, - enum_duplicates duplic) + +static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, + List<Item> &fields, COND **where) { bool insert_into_view= (table_list->view != 0); - DBUG_ENTER("mysql_prepare_insert"); + DBUG_ENTER("mysql_prepare_insert_check_table"); - /* TODO: use this condition for 'WHITH CHECK OPTION' */ - Item *unused_conds= 0; - if (setup_tables(thd, table_list, &unused_conds)) - DBUG_RETURN(-1); + if (setup_tables(thd, table_list, where)) + DBUG_RETURN(1); if (insert_into_view && !fields.elements) { @@ -542,8 +553,37 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, check_view_insertability(table_list, thd->query_id))) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); - DBUG_RETURN(-1); + DBUG_RETURN(1); } + DBUG_RETURN(0); +} + + +/* + Prepare items in INSERT statement + + SYNOPSIS + mysql_prepare_insert() + thd Thread handler + table_list Global/local table list + + RETURN VALUE + 0 OK + -1 error (message is not sent to user) +*/ + +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, + List<Item> &fields, List_item *values, + List<Item> &update_fields, List<Item> &update_values, + enum_duplicates duplic) +{ + bool insert_into_view= (table_list->view != 0); + /* TODO: use this condition for 'WITH CHECK OPTION' */ + Item *unused_conds= 0; + DBUG_ENTER("mysql_prepare_insert"); + + if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds)) + DBUG_RETURN(-1); if (check_insert_fields(thd, table_list, fields, *values, 1, !insert_into_view) || @@ -553,7 +593,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, setup_fields(thd, 0, table_list, update_values, 0, 0, 0)))) DBUG_RETURN(-1); - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); @@ -1555,27 +1595,11 @@ bool delayed_insert::handle_inserts(void) int mysql_insert_select_prepare(THD *thd) { LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_insert_select_prepare"); - - if (setup_tables(thd, table_list, &lex->select_lex.where)) + if (mysql_prepare_insert_check_table(thd, lex->query_tables, + lex->field_list, + &lex->select_lex.where)) DBUG_RETURN(-1); - - if (insert_into_view && !lex->field_list.elements) - { - lex->empty_field_list_on_rset= 1; - insert_view_fields(&lex->field_list, table_list); - } - - if (!table_list->updatable || - check_key_in_view(thd, table_list) || - (insert_into_view && - check_view_insertability(table_list, thd->query_id))) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); - DBUG_RETURN(-1); - } DBUG_RETURN(0); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d96c6371ff1..aa5cb04f628 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1422,7 +1422,7 @@ bool st_select_lex_unit::check_updateable(char *db, char *table) */ bool st_select_lex::check_updateable(char *db, char *table) { - if (find_real_table_in_local_list(get_table_list(), db, table)) + if (find_table_in_local_list(get_table_list(), db, table)) return 1; for (SELECT_LEX_UNIT *un= first_inner_unit(); @@ -1631,14 +1631,16 @@ void st_select_lex_unit::set_limit(SELECT_LEX *values, SYNOPSIS unlink_first_table() - link_to_local do we need link this table to local + link_to_local Set to 1 if caller should link this table to local NOTES We rely on fact that first table in both list are same or local list is empty RETURN + 0 If 'query_tables' == 0 unlinked table + In this case link_to_local is set. */ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) @@ -1692,11 +1694,11 @@ void st_lex::first_lists_tables_same() TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first; if (query_tables != first_table && first_table != 0) { + TABLE_LIST *next; if (query_tables_last == &first_table->next_global) query_tables_last= first_table->prev_global; - TABLE_LIST *next= *first_table->prev_global= first_table->next_global; - first_table->next_global= 0; - if (next) + + if ((next= *first_table->prev_global= first_table->next_global)) next->prev_global= first_table->prev_global; /* include in new place */ first_table->next_global= query_tables; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index be063211a7d..0ba1789cfbf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1905,7 +1905,7 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(first_table == all_tables && first_table != 0); */ lex->first_lists_tables_same(); - /* should be assigned after making firts tables same */ + /* should be assigned after making first tables same */ all_tables= lex->query_tables; if (lex->sql_command != SQLCOM_CREATE_PROCEDURE && @@ -2381,8 +2381,8 @@ mysql_execute_command(THD *thd) of query */ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - find_real_table_in_list(select_tables, create_table->db, - create_table->real_name)) + find_table_in_global_list(select_tables, create_table->db, + create_table->real_name)) { net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name); goto create_error; @@ -2752,7 +2752,7 @@ unsent_create_error: unit->set_limit(select_lex, select_lex); // is table which we are changing used somewhere in other parts of query - if (find_real_table_in_list(all_tables->next_global, + if (find_table_in_global_list(all_tables->next_global, first_table->db, first_table->real_name)) { /* Using same table for INSERT and SELECT */ @@ -3907,7 +3907,7 @@ error: 1 - access denied, error is sent to client */ -int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { if (check_access(thd, privilege, all_tables->db, &all_tables->grant.privilege, 0, 0)) @@ -3951,13 +3951,13 @@ bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors) { - DBUG_ENTER("check_access"); - DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access, - thd->master_access)); #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; #endif ulong dummy; + DBUG_ENTER("check_access"); + DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", + db ? db : "", want_access, thd->master_access)); if (save_priv) *save_priv=0; else @@ -3965,8 +3965,9 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { + DBUG_PRINT("error",("No database")); if (!no_errors) - send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -3991,6 +3992,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || ! db && dont_check_global_grants) { // We can never grant this + DBUG_PRINT("error",("No possible access")); if (!no_errors) net_printf(thd,ER_ACCESS_DENIED_ERROR, thd->priv_user, @@ -4009,13 +4011,17 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, db_access=thd->db_access; /* Remove SHOW attribute and access rights we already have */ want_access &= ~(thd->master_access | EXTRA_ACL); + DBUG_PRINT("info",("db_access: %lu want_access: %lu", + db_access, want_access)); db_access= ((*save_priv=(db_access | thd->master_access)) & want_access); /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || - ((grant_option && !dont_check_global_grants) && + (grant_option && !dont_check_global_grants && !(want_access & ~(db_access | TABLE_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ + + DBUG_PRINT("error",("Access denied")); if (!no_errors) net_printf(thd,ER_DBACCESS_DENIED_ERROR, thd->priv_user, @@ -4103,6 +4109,42 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, return FALSE; } + +/* + Check if the given table has any of the asked privileges + + SYNOPSIS + check_some_access() + thd Thread handler + want_access Bitmap of possible privileges to check for + + RETURN + 0 ok + 1 error +*/ + + +bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) +{ + ulong access; + DBUG_ENTER("check_some_access"); + + /* This loop will work as long as we have less than 32 privileges */ + for (access= 1; access < want_access ; access<<= 1) + { + if (access & want_access) + { + if (!check_access(thd, access, table->db, + &table->grant.privilege, 0, 1) && + !grant_option || !check_grant(thd, access, table, 0, 1, 1)) + DBUG_RETURN(0); + } + } + DBUG_PRINT("exit",("no matching access rights")); + DBUG_RETURN(1); +} + + bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) { @@ -4960,6 +5002,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { register TABLE_LIST *ptr; char *alias_str; + LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); if (!table) @@ -5011,7 +5054,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; - ptr->select_lex= thd->lex->current_select; + ptr->select_lex= lex->current_select; ptr->cacheable_table= 1; if (use_index_arg) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg, @@ -5035,8 +5078,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } } + /* Link table in local list (list for current select) */ table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); - LEX *lex= thd->lex; + /* Link table in global list (all used tables) */ *(ptr->prev_global= lex->query_tables_last)= ptr; lex->query_tables_last= &ptr->next_global; DBUG_RETURN(ptr); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 5214ae95b86..46d83e61e22 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1665,8 +1665,8 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) if (lex->empty_field_list_on_rset) { - lex->field_list.empty(); lex->empty_field_list_on_rset= 0; + lex->field_list.empty(); } for (; sl; sl= sl->next_select_in_list()) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 328fd4f9976..10a4d551d32 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -225,25 +225,18 @@ int handle_select(THD *thd, LEX *lex, select_result *result) thd->net.report_error)); if (thd->net.report_error) res= 1; - if (res > 0) + if (unlikely(res)) { if (result) { - result->send_error(0, NullS); + if (res > 0) + result->send_error(0, NullS); result->abort(); } - else + else if (res > 0) send_error(thd, 0, NullS); res= 1; // Error sent to client } - if (res < 0) - { - if (result) - { - result->abort(); - } - res= 1; - } if (result != lex->result) delete result; DBUG_RETURN(res); @@ -348,9 +341,7 @@ JOIN::prepare(Item ***rref_pointer_array, if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; - if ((res= ((!thd->lex->view_prepare_mode) ? - subselect->select_transformer(this) : - subselect->no_select_transform())) != + if ((res= subselect->select_transformer(this)) != Item_subselect::RES_OK) { select_lex->fix_prepare_information(thd, &conds); @@ -11192,7 +11183,7 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) SYNOPSIS print_join() thd thread handler - str string where table should bbe printed + str string where table should be printed tables list of tables in join */ @@ -11248,30 +11239,31 @@ void st_table_list::print(THD *thd, String *str) print_join(thd, str, &nested_join->join_list); str->append(')'); } - else if (view_name.str) + else { - str->append(view_db.str, view_db.length); - str->append('.'); - str->append(view_name.str, view_name.length); - if (my_strcasecmp(table_alias_charset, view_name.str, alias)) + const char *cmp_name; // Name to compare with alias + if (view_name.str) { - str->append(' '); - str->append(alias); + str->append(view_db.str, view_db.length); + str->append('.'); + str->append(view_name.str, view_name.length); + cmp_name= view_name.str; } - } - else if (derived) - { - str->append('('); - derived->print(str); - str->append(") ", 2); - str->append(alias); - } - else - { - str->append(db); - str->append('.'); - str->append(real_name); - if (my_strcasecmp(table_alias_charset, real_name, alias)) + else if (derived) + { + str->append('('); + derived->print(str); + str->append(')'); + cmp_name= ""; // Force printing of alias + } + else + { + str->append(db); + str->append('.'); + str->append(real_name); + cmp_name= real_name; + } + if (my_strcasecmp(table_alias_charset, cmp_name, alias)) { str->append(' '); str->append(alias); @@ -11279,6 +11271,7 @@ void st_table_list::print(THD *thd, String *str) } } + void st_select_lex::print(THD *thd, String *str) { if (!thd) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index da0a6ed3d28..d9180df3791 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1551,11 +1551,11 @@ view_store_create_info(THD *thd, TABLE_LIST *table, String *buff) MODE_MAXDB | MODE_ANSI)) != 0; buff->append("CREATE ", 7); - if(!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE || - table->algorithm == VIEW_ALGORITHM_TMEPTABLE)) + if (!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE || + table->algorithm == VIEW_ALGORITHM_TMPTABLE)) { buff->append("ALGORITHM=", 10); - if (table->algorithm == VIEW_ALGORITHM_TMEPTABLE) + if (table->algorithm == VIEW_ALGORITHM_TMPTABLE) buff->append("TMPTABLE ", 9); else buff->append("MERGE ", 6); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7d49c422194..77d48dccb10 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -483,7 +483,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(-1); /* Check that we are not using table that we are updating in a sub select */ - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); @@ -760,7 +760,7 @@ int multi_update::prepare(List<Item> ¬_used_values, { TABLE *table=table_ref->table; if (!(tables_to_update & table->map) && - find_real_table_in_list(update_tables, table_ref->db, + find_table_in_global_list(update_tables, table_ref->db, table_ref->real_name)) table->no_cache= 1; // Disable row cache } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 5a16f0ff3fc..d8198f98a32 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -79,25 +79,8 @@ int mysql_create_view(THD *thd, /* Ensure that we have some privilage on this table, more strict check will be done on column level after preparation, - - SELECT_ACL will be checked for sure for all fields because it is - listed first (if we have not rights to SELECT from whole table this - right will be written as tbl->grant.want_privilege and will be checked - later (except fields which need any privilege and can be updated). */ - if ((check_access(thd, SELECT_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 1)) && - (check_access(thd, INSERT_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, INSERT_ACL, tbl, 0, 1, 1)) && - (check_access(thd, DELETE_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, DELETE_ACL, tbl, 0, 1, 1)) && - (check_access(thd, UPDATE_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, UPDATE_ACL, tbl, 0, 1, 1)) - ) + if (check_some_access(thd, VIEW_ANY_ACL, tbl)) { my_printf_error(ER_TABLEACCESS_DENIED_ERROR, ER(ER_TABLEACCESS_DENIED_ERROR), @@ -113,7 +96,7 @@ int mysql_create_view(THD *thd, /* We need to check only SELECT_ACL for all normal fields, fields - where we need any privilege will be pmarked later + where we need any privilege will be marked later */ tbl->grant.want_privilege= SELECT_ACL; /* @@ -166,7 +149,7 @@ int mysql_create_view(THD *thd, /* check that tables are not temporary */ for (tbl= tables; tbl; tbl= tbl->next_global) { - if (tbl->table->tmp_table != NO_TMP_TABLE && !test(tbl->view)) + if (tbl->table->tmp_table != NO_TMP_TABLE && !tbl->view) { my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias); res= -1; @@ -189,19 +172,18 @@ int mysql_create_view(THD *thd, /* view list (list of view fields names) */ if (lex->view_list.elements) { - if (lex->view_list.elements != select_lex->item_list.elements) - { - my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0)); - goto err; - } List_iterator_fast<Item> it(select_lex->item_list); List_iterator_fast<LEX_STRING> nm(lex->view_list); Item *item; LEX_STRING *name; - while((item= it++, name= nm++)) + + if (lex->view_list.elements != select_lex->item_list.elements) { - item->set_name(name->str, name->length, system_charset_info); + my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0)); + goto err; } + while ((item= it++, name= nm++)) + item->set_name(name->str, name->length, system_charset_info); } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -214,7 +196,7 @@ int mysql_create_view(THD *thd, Item *item; fill_effective_table_privileges(thd, &view->grant, db, view->real_name); - while((item= it++)) + while ((item= it++)) { uint priv= (get_column_grant(thd, &view->grant, db, view->real_name, item->name) & @@ -223,10 +205,10 @@ int mysql_create_view(THD *thd, { Item_field *fld= (Item_field *)item; /* - There are no any privileges on VIWE column or there are + There are no any privileges on VIEW column or there are some other privileges then we have for underlaying table */ - if (priv == 0 || test(~fld->have_privileges & priv)) + if (priv == 0 || (~fld->have_privileges & priv)) { /* VIEW column has more privileges */ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, @@ -242,7 +224,7 @@ int mysql_create_view(THD *thd, } else { - if (!test(priv & SELECT_ACL)) + if (!(priv & SELECT_ACL)) { /* user have not privilege to SELECT expression */ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, @@ -266,14 +248,11 @@ int mysql_create_view(THD *thd, goto err; } VOID(pthread_mutex_lock(&LOCK_open)); - if ((res= mysql_register_view(thd, view, mode))) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - start_waiting_global_read_lock(thd); - goto err; - } + res= mysql_register_view(thd, view, mode); VOID(pthread_mutex_unlock(&LOCK_open)); start_waiting_global_read_lock(thd); + if (res) + goto err; send_ok(thd); lex->link_first_table_back(view, link_to_local); @@ -292,40 +271,36 @@ err: // index of revision number in following table static const int revision_number_position= 4; -static char *view_field_names[]= -{ - (char*)"query", - (char*)"md5", - (char*)"updatable", - (char*)"algorithm", - (char*)"revision", - (char*)"timestamp", - (char*)"create-version", - (char*)"source" -}; +/* + table of VIEW .frm field descriptors + + Note that one should NOT change the order for this, as it's used by + parse() +*/ -// table of VIEW .frm field descriprors static File_option view_parameters[]= -{{{view_field_names[0], 5}, offsetof(TABLE_LIST, query), +{{{(char*) "query", 5}, offsetof(TABLE_LIST, query), FILE_OPTIONS_STRING}, - {{view_field_names[1], 3}, offsetof(TABLE_LIST, md5), + {{(char*) "md5", 3}, offsetof(TABLE_LIST, md5), FILE_OPTIONS_STRING}, - {{view_field_names[2], 9}, offsetof(TABLE_LIST, updatable), + {{(char*) "updatable", 9}, offsetof(TABLE_LIST, updatable), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[3], 9}, offsetof(TABLE_LIST, algorithm), + {{(char*) "algorithm", 9}, offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[4], 8}, offsetof(TABLE_LIST, revision), + {{(char*) "revision", 8}, offsetof(TABLE_LIST, revision), FILE_OPTIONS_REV}, - {{view_field_names[5], 9}, offsetof(TABLE_LIST, timestamp), + {{(char*) "timestamp", 9}, offsetof(TABLE_LIST, timestamp), FILE_OPTIONS_TIMESTAMP}, - {{view_field_names[6], 14}, offsetof(TABLE_LIST, file_version), + {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[7], 6}, offsetof(TABLE_LIST, source), + {{(char*) "source", 6}, offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, {{NULL, 0}, 0, FILE_OPTIONS_STRING} }; +static const uint required_view_parameters= 6; + static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}}; @@ -368,16 +343,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, dir.length= strlen(dir_buff); file.str= file_buff; - file.length= my_snprintf(file_buff, FN_REFLEN, "%s%s", - view->real_name, reg_ext); + file.length= (strxnmov(file_buff, FN_REFLEN, view->real_name, reg_ext, + NullS) - file_buff); /* init timestamp */ - if (!test(view->timestamp.str)) + if (!view->timestamp.str) view->timestamp.str= view->timestamp_buffer; // check old .frm { char path_buff[FN_REFLEN]; LEX_STRING path; + File_parser *parser; + path.str= path_buff; fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME); path.length= strlen(path_buff); @@ -390,34 +367,27 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, DBUG_RETURN(1); } - File_parser *parser= sql_parse_prepare(&path, &thd->mem_root, 0); - if (parser) + if (!(parser= sql_parse_prepare(&path, &thd->mem_root, 0))) + DBUG_RETURN(1); + + if (!parser->ok() || + strncmp("VIEW", parser->type()->str, parser->type()->length)) { - if(parser->ok() && - !strncmp("VIEW", parser->type()->str, parser->type()->length)) - { - /* - read revision number - - TODO: read dependense list, too, to process cascade/restrict - TODO: special cascade/restrict procedure for alter? - */ - if (parser->parse((gptr)view, &thd->mem_root, - view_parameters + revision_number_position, 1)) - { - DBUG_RETURN(1); - } - } - else - { - my_error(ER_WRONG_OBJECT, MYF(0), (view->db?view->db:thd->db), - view->real_name, "VIEW"); - DBUG_RETURN(1); - } + my_error(ER_WRONG_OBJECT, MYF(0), (view->db ? view->db : thd->db), + view->real_name, "VIEW"); + DBUG_RETURN(1); } - else + + /* + read revision number + + TODO: read dependense list, too, to process cascade/restrict + TODO: special cascade/restrict procedure for alter? + */ + if (parser->parse((gptr)view, &thd->mem_root, + view_parameters + revision_number_position, 1)) { - DBUG_RETURN(1); + DBUG_RETURN(1); } } else @@ -448,14 +418,14 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, } view->algorithm= thd->lex->create_view_algorithm; if ((view->updatable= (can_be_merged && - view->algorithm != VIEW_ALGORITHM_TMEPTABLE))) + view->algorithm != VIEW_ALGORITHM_TMPTABLE))) { // TODO: change here when we will support UNIONs for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first; tbl; tbl= tbl->next_local) { - if (tbl->view != 0 && !tbl->updatable) + if (tbl->view && !tbl->updatable) { view->updatable= 0; break; @@ -478,7 +448,13 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, mysql_make_view() parser - parser object; table - TABLE_LIST structure for filling + + RETURN + 0 ok + 1 error + */ + my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table) { @@ -487,7 +463,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (table->view) { DBUG_PRINT("info", - ("VIEW %s.%s is already processed on previos PS/SP execution", + ("VIEW %s.%s is already processed on previous PS/SP execution", table->view_db.str, table->view_name.str)); DBUG_RETURN(0); } @@ -507,13 +483,14 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) thd->set_n_backup_item_arena(arena, &backup); /* init timestamp */ - if (!test(table->timestamp.str)) + if (!table->timestamp.str) table->timestamp.str= table->timestamp_buffer; /* TODO: when VIEWs will be stored in cache, table mem_root should be used here */ - if (parser->parse((gptr)table, &thd->mem_root, view_parameters, 6)) + if (parser->parse((gptr)table, &thd->mem_root, view_parameters, + required_view_parameters)) goto err; /* @@ -535,7 +512,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) */ table->view= lex= thd->lex= (LEX*) new(&thd->mem_root) st_lex_local; lex_start(thd, (uchar*)table->query.str, table->query.length); - mysql_init_query(thd, true); + mysql_init_query(thd, TRUE); lex->select_lex.select_number= ++thd->select_number; old_lex->derived_tables|= DERIVED_VIEW; { @@ -613,7 +590,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) - VIEW SELECT allow marging - VIEW used in subquery or command support MERGE algorithm */ - if (table->algorithm != VIEW_ALGORITHM_TMEPTABLE && + if (table->algorithm != VIEW_ALGORITHM_TMPTABLE && lex->can_be_merged() && (table->select_lex->master_unit() != &old_lex->unit || old_lex->can_use_merged())) @@ -656,11 +633,13 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) goto ok; } - table->effective_algorithm= VIEW_ALGORITHM_TMEPTABLE; + table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE; if (table->updatable) { - //TOTO: warning: can't be updateable, .frm edited by hand. version - //downgrade? + /* + TODO: warning: can't be updateable, .frm edited by hand. version + downgrade? + */ table->updatable= 0; } @@ -672,7 +651,8 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) { if ((tbl_end= table->next_global)) { - for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next); + for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next) + ; if ((tbl_end->next_global= old_next)) tbl_end->next_global->prev_global= &tbl_end->next_global; } @@ -720,6 +700,7 @@ err: -1 Error 1 Error and error message given */ + int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) { DBUG_ENTER("mysql_drop_view"); @@ -729,8 +710,8 @@ int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) for (view= views; view; view= view->next_local) { - strxmov(path, mysql_data_home, "/", view->db, "/", view->real_name, - reg_ext, NullS); + strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/", + view->real_name, reg_ext, NullS); (void) unpack_filename(path, path); VOID(pthread_mutex_lock(&LOCK_open)); if (access(path, F_OK) || (type= (mysql_frm_type(path) != FRMTYPE_VIEW))) @@ -782,21 +763,20 @@ frm_type_enum mysql_frm_type(char *path) { File file; char header[10]; //"TYPE=VIEW\n" it is 10 characters + int length; DBUG_ENTER("mysql_frm_type"); if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0) { DBUG_RETURN(FRMTYPE_ERROR); } - if (my_read(file, header, 10, MYF(MY_WME)) == MY_FILE_ERROR) - { - my_close(file, MYF(MY_WME)); - DBUG_RETURN(FRMTYPE_ERROR); - } + length= my_read(file, header, 10, MYF(MY_WME)); my_close(file, MYF(MY_WME)); - if (strncmp(header, "TYPE=VIEW\n", 10) != 0) - DBUG_RETURN(FRMTYPE_TABLE); - DBUG_RETURN(FRMTYPE_VIEW); + if (length == (int) MY_FILE_ERROR) + DBUG_RETURN(FRMTYPE_ERROR); + if (!strncmp(header, "TYPE=VIEW\n", 10)) + DBUG_RETURN(FRMTYPE_VIEW); + DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table } @@ -815,72 +795,81 @@ frm_type_enum mysql_frm_type(char *path) bool check_key_in_view(THD *thd, TABLE_LIST *view) { + TABLE *table; + Item **trans; + KEY *key_info, *key_info_end; + uint i, elements_in_view; DBUG_ENTER("check_key_in_view"); + if (!view->view) DBUG_RETURN(FALSE); /* it is normal table */ + table= view->table; + trans= view->field_translation; + key_info_end= (key_info= table->key_info)+ table->keys; - TABLE *table= view->table; - Item **trans= view->field_translation; - KEY *key_info= table->key_info; - uint primary_key= table->primary_key; - uint num= view->view->select_lex.item_list.elements; + elements_in_view= view->view->select_lex.item_list.elements; DBUG_ASSERT(view->table != 0 && view->field_translation != 0); - /* try to find key */ - for (uint i=0; i < table->keys; i++, key_info++) + /* Loop over all keys to see if a unique-not-null key is used */ + for (;key_info != key_info_end ; key_info++) { - if (i == primary_key && !strcmp(key_info->name, primary_key_name) || - key_info->flags & HA_NOSAME) + if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) { KEY_PART_INFO *key_part= key_info->key_part; - bool found= 1; - for (uint j=0; j < key_info->key_parts && found; j++, key_part++) + KEY_PART_INFO *key_part_end= key_part + key_info->key_parts; + + /* check that all key parts are used */ + for (;;) { - found= 0; - for (uint k= 0; k < num; k++) + uint k; + for (k= 0; k < elements_in_view; k++) { if (trans[k]->type() == Item::FIELD_ITEM && - ((Item_field *)trans[k])->field == key_part->field && - (key_part->field->flags & NOT_NULL_FLAG)) - { - found= 1; + ((Item_field *)trans[k])->field == key_part->field) break; - } } + if (k == elements_in_view) + break; // Key is not possible + if (++key_part == key_part_end) + DBUG_RETURN(FALSE); // Found usable key } - if (found) - DBUG_RETURN(FALSE); } } + DBUG_PRINT("info", ("checking if all fields of table are used")); /* check all fields presence */ { - Field **field_ptr= table->field; - for (; *field_ptr; ++field_ptr) + Field **field_ptr; + for (field_ptr= table->field; *field_ptr; field_ptr++) { - uint i= 0; - for (; i < num; i++) + for (i= 0; i < elements_in_view; i++) { if (trans[i]->type() == Item::FIELD_ITEM && ((Item_field *)trans[i])->field == *field_ptr) break; } - if (i >= num) + if (i == elements_in_view) // If field didn't exists { ulong mode= thd->variables.sql_updatable_view_key; - /* 1 == YES, 2 == LIMIT1 */ + /* + 0 == NO ; Don't give any errors + 1 == YES ; Give always an error + 2 == LIMIT1 ; Give an error if this is used with LIMIT 1 + This is used to protect against gui programs that + uses LIMIT 1 to update just the current row. This + doesn't work reliable if the view doesn't have a + unique key or if the view doesn't use all fields in + table. + */ if (mode == 1 || (mode == 2 && - thd->lex->select_lex.select_limit == 1)) + thd->lex->unit.global_parameters->select_limit == 1)) { DBUG_RETURN(TRUE); } - else - { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); - DBUG_RETURN(FALSE); - } + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); + DBUG_RETURN(FALSE); } } } @@ -899,18 +888,17 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) void insert_view_fields(List<Item> *list, TABLE_LIST *view) { - uint num= view->view->select_lex.item_list.elements; - Item **trans= view->field_translation; + uint elements_in_view= view->view->select_lex.item_list.elements; + Item **trans; DBUG_ENTER("insert_view_fields"); - if (!trans) + + if (!(trans= view->field_translation)) DBUG_VOID_RETURN; - for (uint i= 0; i < num; i++) + for (uint i= 0; i < elements_in_view; i++) { if (trans[i]->type() == Item::FIELD_ITEM) - { list->push_back(trans[i]); - } } DBUG_VOID_RETURN; } diff --git a/sql/table.cc b/sql/table.cc index 43b39ffb37e..8ac0f409e2f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -88,6 +88,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam)); error=1; + disk_buff=NULL; + old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); if ((file=my_open(fn_format(index_file, name, "", reg_ext, MY_UNPACK_FILENAME), @@ -118,11 +120,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, bzero((char*) outparam,sizeof(*outparam)); outparam->blob_ptr_size=sizeof(char*); - disk_buff=NULL; record= NULL; keynames=NullS; outparam->db_stat = db_stat; - init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root); outparam->real_name=strdup_root(&outparam->mem_root, @@ -742,11 +741,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, DBUG_RETURN (0); err_w_init: - //awoid problem with uninitialized data + /* Avoid problem with uninitialized data */ bzero((char*) outparam,sizeof(*outparam)); outparam->real_name= (char*)name+dirname_length(name); - old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); - disk_buff= 0; err_not_open: x_free((gptr) disk_buff); @@ -1447,13 +1444,14 @@ db_type get_table_type(const char *name) st_table_list::calc_md5() buffer buffer for md5 writing */ + void st_table_list::calc_md5(char *buffer) { my_MD5_CTX context; - unsigned char digest[16]; - my_MD5Init (&context); - my_MD5Update (&context,(unsigned char *) query.str, query.length); - my_MD5Final (digest, &context); + uchar digest[16]; + my_MD5Init(&context); + my_MD5Update(&context,(uchar *) query.str, query.length); + my_MD5Final(digest, &context); sprintf((char *) buffer, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], @@ -1469,6 +1467,7 @@ void st_table_list::calc_md5(char *buffer) SYNOPSIS st_table_list::set_ancestor() */ + void st_table_list::set_ancestor() { if (ancestor->ancestor) @@ -1496,6 +1495,7 @@ void st_table_list::set_ancestor() (without fields) for name resolving, but substituted expressions will return correct used tables mask. */ + bool st_table_list::setup_ancestor(THD *thd, Item **conds) { Item **transl; @@ -1515,13 +1515,13 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) thd->set_query_id= 1; /* this view was prepared already on previous PS/SP execution */ Item **end= field_translation + select->item_list.elements; - for (Item **i= field_translation; i < end; i++) + for (Item **item= field_translation; item < end; item++) { - //TODO: fix for several tables in VIEW + /* TODO: fix for several tables in VIEW */ uint want_privilege= ancestor->table->grant.want_privilege; /* real rights will be checked in VIEW field */ ancestor->table->grant.want_privilege= 0; - if (!(*i)->fixed && (*i)->fix_fields(thd, ancestor, i)) + if (!(*item)->fixed && (*item)->fix_fields(thd, ancestor, item)) goto err; ancestor->table->grant.want_privilege= want_privilege; } @@ -1545,19 +1545,17 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) thd->set_query_id= 1; while ((item= it++)) { - //TODO: fix for several tables in VIEW + /* TODO: fix for several tables in VIEW */ uint want_privilege= ancestor->table->grant.want_privilege; /* real rights will be checked in VIEW field */ ancestor->table->grant.want_privilege= 0; if (!item->fixed && item->fix_fields(thd, ancestor, &item)) - { goto err; - } ancestor->table->grant.want_privilege= want_privilege; transl[i++]= item; } field_translation= transl; - //TODO: sort this list? Use hash for big number of fields + /* TODO: sort this list? Use hash for big number of fields */ if (where) { @@ -1566,12 +1564,12 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) goto err; if (arena) - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_item_arena(arena, &backup); if (outer_join) { /* Store WHERE condition to ON expression for outer join, because we - can't use WHERE to correctly execute jeft joins on VIEWs and this + can't use WHERE to correctly execute jeft joins on VIEWs and this expression will not be moved to WHERE condition (i.e. will be clean correctly for PS/SP) */ diff --git a/sql/table.h b/sql/table.h index 5e00820a6e5..02e470a69e0 100644 --- a/sql/table.h +++ b/sql/table.h @@ -172,7 +172,7 @@ struct st_table { #define JOIN_TYPE_RIGHT 2 #define VIEW_ALGORITHM_UNDEFINED 0 -#define VIEW_ALGORITHM_TMEPTABLE 1 +#define VIEW_ALGORITHM_TMPTABLE 1 #define VIEW_ALGORITHM_MERGE 2 struct st_lex; @@ -255,7 +255,7 @@ public: virtual ~Field_iterator() {} virtual void set(TABLE_LIST *)= 0; virtual void next()= 0; - virtual bool end()= 0; + virtual bool end_of_fields()= 0; /* Return 1 at end of list */ virtual const char *name()= 0; virtual Item *item(THD *)= 0; virtual Field *field()= 0; @@ -270,7 +270,7 @@ public: void set(TABLE_LIST *table) { ptr= table->table->field; } void set_table(TABLE *table) { ptr= table->field; } void next() { ptr++; } - bool end() { return test(*ptr); } + bool end_of_fields() { return *ptr == 0; } const char *name(); Item *item(THD *thd); Field *field() { return *ptr; } @@ -284,7 +284,7 @@ public: Field_iterator_view() :ptr(0), array_end(0) {} void set(TABLE_LIST *table); void next() { ptr++; } - bool end() { return ptr < array_end; } + bool end_of_fields() { return ptr == array_end; } const char *name(); Item *item(THD *thd) { return *ptr; } Field *field() { return 0; } diff --git a/tests/client_test.c b/tests/client_test.c index 13f5a3ac852..e9adf40d8b6 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -30,6 +30,7 @@ #define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */ +#define MAX_KEY 64 /* set default options */ static char *opt_db= 0; @@ -6818,13 +6819,13 @@ static void test_explain_bug() "", "", "", 10, 0); verify_prepare_field(result, 4, "possible_keys", "", MYSQL_TYPE_VAR_STRING, - "", "", "", NAME_LEN*64, 0); + "", "", "", NAME_LEN*MAX_KEY, 0); verify_prepare_field(result, 5, "key", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_LEN, 0); verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING, - "", "", "", NAME_LEN*64, 0); + "", "", "", NAME_LEN*MAX_KEY, 0); verify_prepare_field(result, 7, "ref", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_LEN*16, 0); |