Commit a535342d authored by bell@sanja.is.com.ua's avatar bell@sanja.is.com.ua

after review PS fixes

parent e8137a13
...@@ -477,9 +477,8 @@ bool Item_in_optimizer::fix_left(THD *thd, ...@@ -477,9 +477,8 @@ bool Item_in_optimizer::fix_left(THD *thd,
struct st_table_list *tables, struct st_table_list *tables,
Item **ref) Item **ref)
{ {
if ((!args[0]->fixed && args[0]->fix_fields(thd, tables, args))) if (!args[0]->fixed && args[0]->fix_fields(thd, tables, args) ||
return 1; !cache && !(cache= Item_cache::get_cache(args[0]->result_type())))
if (!cache && !(cache= Item_cache::get_cache(args[0]->result_type())))
return 1; return 1;
cache->setup(args[0]); cache->setup(args[0]);
......
...@@ -348,12 +348,17 @@ void cleanup_items(Item *item); ...@@ -348,12 +348,17 @@ void cleanup_items(Item *item);
class THD; class THD;
void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
int check_one_table_access(THD *thd, ulong privilege, int check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables, bool no_errors); TABLE_LIST *tables);
bool check_merge_table_access(THD *thd, char *db, bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list); TABLE_LIST *table_list);
int multi_update_precheck(THD *thd, TABLE_LIST *tables); int multi_update_precheck(THD *thd, TABLE_LIST *tables);
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count);
int insert_select_precheck(THD *thd, TABLE_LIST *tables); int insert_select_precheck(THD *thd, TABLE_LIST *tables);
int update_precheck(THD *thd, TABLE_LIST *tables);
int delete_precheck(THD *thd, TABLE_LIST *tables);
int insert_precheck(THD *thd, TABLE_LIST *tables, bool update);
int create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table);
#include "sql_class.h" #include "sql_class.h"
#include "opt_range.h" #include "opt_range.h"
...@@ -532,6 +537,9 @@ bool mysql_rename_table(enum db_type base, ...@@ -532,6 +537,9 @@ bool mysql_rename_table(enum db_type base,
int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys); int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
List<Alter_drop> &drop_list); List<Alter_drop> &drop_list);
int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *update_table_list,
Item **conds, uint order_num, ORDER *order);
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
List<Item> &values,COND *conds, List<Item> &values,COND *conds,
uint order_num, ORDER *order, ha_rows limit, uint order_num, ORDER *order, ha_rows limit,
...@@ -541,9 +549,15 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list, ...@@ -541,9 +549,15 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list,
COND *conds, ulong options, COND *conds, ulong options,
enum enum_duplicates handle_duplicates, enum enum_duplicates handle_duplicates,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex); SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *insert_table_list, TABLE *table,
List<Item> &fields, List_item *values,
List<Item> &update_fields,
List<Item> &update_values, enum_duplicates duplic);
int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
List<List_item> &values, List<Item> &update_fields, List<List_item> &values, List<Item> &update_fields,
List<Item> &update_values, enum_duplicates flag); List<Item> &update_values, enum_duplicates flag);
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order, int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order,
ha_rows rows, ulong options); ha_rows rows, ulong options);
int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0); int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0);
......
...@@ -2529,6 +2529,18 @@ int set_var::check(THD *thd) ...@@ -2529,6 +2529,18 @@ int set_var::check(THD *thd)
} }
/*
Check variable, but without assigning value (used by PS)
SYNOPSIS
set_var::light_check()
thd thread handler
RETURN VALUE
0 ok
1 ERROR, message sent (normally no variables was updated)
-1 ERROR, message not sent
*/
int set_var::light_check(THD *thd) int set_var::light_check(THD *thd)
{ {
if (var->check_type(type)) if (var->check_type(type))
...@@ -2538,7 +2550,7 @@ int set_var::light_check(THD *thd) ...@@ -2538,7 +2550,7 @@ int set_var::light_check(THD *thd)
var->name); var->name);
return -1; return -1;
} }
if ((type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))) if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))
return 1; return 1;
if (value && (value->fix_fields(thd, 0, &value) || value->check_cols(1))) if (value && (value->fix_fields(thd, 0, &value) || value->check_cols(1)))
...@@ -2574,6 +2586,18 @@ int set_var_user::check(THD *thd) ...@@ -2574,6 +2586,18 @@ int set_var_user::check(THD *thd)
} }
/*
Check variable, but without assigning value (used by PS)
SYNOPSIS
set_var_user::light_check()
thd thread handler
RETURN VALUE
0 ok
1 ERROR, message sent (normally no variables was updated)
-1 ERROR, message not sent
*/
int set_var_user::light_check(THD *thd) int set_var_user::light_check(THD *thd)
{ {
/* /*
......
...@@ -1388,7 +1388,7 @@ static bool test_if_create_new_users(THD *thd) ...@@ -1388,7 +1388,7 @@ static bool test_if_create_new_users(THD *thd)
thd->priv_user, tl.db, 0); thd->priv_user, tl.db, 0);
if (!(db_access & INSERT_ACL)) if (!(db_access & INSERT_ACL))
{ {
if (check_grant(thd,INSERT_ACL,&tl,0,1)) if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
create_new_users=0; create_new_users=0;
} }
} }
...@@ -2650,7 +2650,7 @@ void grant_reload(THD *thd) ...@@ -2650,7 +2650,7 @@ void grant_reload(THD *thd)
****************************************************************************/ ****************************************************************************/
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_table, bool no_errors) uint show_table, uint number, bool no_errors)
{ {
TABLE_LIST *table; TABLE_LIST *table;
char *user = thd->priv_user; char *user = thd->priv_user;
...@@ -2660,7 +2660,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ...@@ -2660,7 +2660,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
return 0; // ok return 0; // ok
rw_rdlock(&LOCK_grant); rw_rdlock(&LOCK_grant);
for (table=tables; table ;table=table->next) for (table= tables; table && number--; table= table->next)
{ {
if (!(~table->grant.privilege & want_access) || table->derived) if (!(~table->grant.privilege & want_access) || table->derived)
{ {
......
...@@ -154,7 +154,7 @@ my_bool grant_init(THD *thd); ...@@ -154,7 +154,7 @@ my_bool grant_init(THD *thd);
void grant_free(void); void grant_free(void);
void grant_reload(THD *thd); void grant_reload(THD *thd);
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_command, bool dont_print_error); uint show_command, uint number, bool dont_print_error);
bool check_grant_column (THD *thd,TABLE *table, const char *name, uint length, bool check_grant_column (THD *thd,TABLE *table, const char *name, uint length,
uint show_command=0); uint show_command=0);
bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table); bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table);
...@@ -168,6 +168,6 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list); ...@@ -168,6 +168,6 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list);
int mysql_revoke_all(THD *thd, List <LEX_USER> &list); int mysql_revoke_all(THD *thd, List <LEX_USER> &list);
#ifdef NO_EMBEDDED_ACCESS_CHECKS #ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E) 0 #define check_grant(A,B,C,D,E,F) 0
#define check_grant_db(A,B) 0 #define check_grant_db(A,B) 0
#endif #endif
...@@ -37,8 +37,6 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ...@@ -37,8 +37,6 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
bool using_limit=limit != HA_POS_ERROR; bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, log_delayed, safe_update, const_cond; bool transactional_table, log_delayed, safe_update, const_cond;
ha_rows deleted; ha_rows deleted;
TABLE_LIST *delete_table_list= (TABLE_LIST*)
thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_delete"); DBUG_ENTER("mysql_delete");
if ((open_and_lock_tables(thd, table_list))) if ((open_and_lock_tables(thd, table_list)))
...@@ -47,15 +45,9 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ...@@ -47,15 +45,9 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
thd->proc_info="init"; thd->proc_info="init";
table->map=1; table->map=1;
if (setup_conds(thd, delete_table_list, &conds) ||
setup_ftfuncs(&thd->lex->select_lex)) if ((error= mysql_prepare_delete(thd, table_list, &conds)))
DBUG_RETURN(-1); DBUG_RETURN(error);
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
const_cond= (!conds || conds->const_item()); const_cond= (!conds || conds->const_item());
safe_update=test(thd->options & OPTION_SAFE_UPDATES); safe_update=test(thd->options & OPTION_SAFE_UPDATES);
...@@ -246,6 +238,38 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ...@@ -246,6 +238,38 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
} }
/*
Prepare items in DELETE statement
SYNOPSIS
mysql_prepare_delete()
thd - thread handler
table_list - global table list
conds - conditions
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
{
TABLE_LIST *delete_table_list=
(TABLE_LIST*)thd->lex->select_lex.table_list.first;
DBUG_ENTER(" mysql_prepare_delete");
if (setup_conds(thd, delete_table_list, conds) ||
setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1);
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
}
/*************************************************************************** /***************************************************************************
Delete multiple tables from join Delete multiple tables from join
***************************************************************************/ ***************************************************************************/
......
...@@ -207,19 +207,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -207,19 +207,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
goto abort; goto abort;
} }
if (check_insert_fields(thd,table,fields,*values,1) || if (mysql_prepare_insert(thd, table_list, insert_table_list, table,
setup_tables(insert_table_list) || fields, values, update_fields,
setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || update_values, duplic))
(duplic == DUP_UPDATE &&
(setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
goto abort; goto abort;
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
goto abort;
}
value_count= values->elements; value_count= values->elements;
while ((values= its++)) while ((values= its++))
...@@ -438,6 +429,43 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -438,6 +429,43 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
} }
/*
Prepare items in INSERT statement
SYNOPSIS
mysql_prepare_update()
thd - thread handler
table_list - global table list
insert_table_list - local table list of INSERT SELECT_LEX
RETURN VALUE
0 - OK
-1 - error (message is not sent to user)
*/
int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *insert_table_list, TABLE *table,
List<Item> &fields, List_item *values,
List<Item> &update_fields, List<Item> &update_values,
enum_duplicates duplic)
{
DBUG_ENTER("mysql_prepare_insert");
if (check_insert_fields(thd, table, fields, *values, 1) ||
setup_tables(insert_table_list) ||
setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) ||
(duplic == DUP_UPDATE &&
(setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
DBUG_RETURN(-1);
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
DBUG_RETURN(0);
}
/* Check if there is more uniq keys after field */ /* Check if there is more uniq keys after field */
static int last_uniq_key(TABLE *table,uint keynr) static int last_uniq_key(TABLE *table,uint keynr)
......
...@@ -1634,8 +1634,10 @@ void st_select_lex::print_limit(THD *thd, String *str) ...@@ -1634,8 +1634,10 @@ void st_select_lex::print_limit(THD *thd, String *str)
} }
} }
/* /*
unlink first table from table lists Unlink first table from global table list and first must outer select list
(lex->select_lex)
SYNOPSIS SYNOPSIS
unlink_first_table() unlink_first_table()
...@@ -1652,9 +1654,13 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables, ...@@ -1652,9 +1654,13 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
{ {
*global_first= tables; *global_first= tables;
*local_first= (TABLE_LIST*)select_lex.table_list.first; *local_first= (TABLE_LIST*)select_lex.table_list.first;
// exclude from global table list /*
exclude from global table list
*/
tables= tables->next; tables= tables->next;
// and from local list if it is not the same /*
and from local list if it is not the same
*/
if (&select_lex != all_selects_list) if (&select_lex != all_selects_list)
select_lex.table_list.first= (gptr)(*local_first)->next; select_lex.table_list.first= (gptr)(*local_first)->next;
else else
...@@ -1663,8 +1669,9 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables, ...@@ -1663,8 +1669,9 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
return tables; return tables;
} }
/* /*
link unlinked first table back Link table back that was unlinked with unlink_first_table()
SYNOPSIS SYNOPSIS
link_first_table_back() link_first_table_back()
...@@ -1680,7 +1687,6 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables, ...@@ -1680,7 +1687,6 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
TABLE_LIST *local_first) TABLE_LIST *local_first)
{ {
global_first->next= tables; global_first->next= tables;
tables= global_first;
if (&select_lex != all_selects_list) if (&select_lex != all_selects_list)
{ {
/* /*
...@@ -1690,8 +1696,8 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables, ...@@ -1690,8 +1696,8 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
select_lex.table_list.first= (gptr) local_first; select_lex.table_list.first= (gptr) local_first;
} }
else else
select_lex.table_list.first= (gptr) tables; select_lex.table_list.first= (gptr) global_first;
return tables; return global_first;
} }
/* /*
......
...@@ -1173,7 +1173,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) ...@@ -1173,7 +1173,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
DBUG_RETURN(1); DBUG_RETURN(1);
if (check_one_table_access(thd, SELECT_ACL, table_list, 0)) if (check_one_table_access(thd, SELECT_ACL, table_list))
goto err; goto err;
thd->free_list = 0; thd->free_list = 0;
thd->query_length=(uint) strlen(tbl_name); thd->query_length=(uint) strlen(tbl_name);
...@@ -1530,7 +1530,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1530,7 +1530,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
0, 0)) 0, 0))
break; break;
if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2,0)) if (grant_option &&
check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
break; break;
mysqld_list_fields(thd,&table_list,fields); mysqld_list_fields(thd,&table_list,fields);
free_items(thd->free_list); free_items(thd->free_list);
...@@ -2135,10 +2136,7 @@ mysql_execute_command(THD *thd) ...@@ -2135,10 +2136,7 @@ mysql_execute_command(THD *thd)
if (grant_option) if (grant_option)
{ {
/* Check that the first table has CREATE privilege */ /* Check that the first table has CREATE privilege */
TABLE_LIST *tmp_table_list=tables->next; bool error= check_grant(thd, CREATE_ACL, tables, 0, 1, 0);
tables->next=0;
bool error=check_grant(thd,CREATE_ACL,tables,0,0);
tables->next=tmp_table_list;
if (error) if (error)
goto error; goto error;
} }
...@@ -2169,18 +2167,9 @@ mysql_execute_command(THD *thd) ...@@ -2169,18 +2167,9 @@ mysql_execute_command(THD *thd)
tables= lex->unlink_first_table(tables, &create_table, tables= lex->unlink_first_table(tables, &create_table,
&create_table_local); &create_table_local);
ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? if ((res= create_table_precheck(thd, tables, create_table)))
CREATE_TMP_ACL : CREATE_ACL); goto unsent_create_error;
lex->create_info.alias= create_table->alias;
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege, 0, 0) ||
check_merge_table_access(thd, create_table->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
goto create_error; /* purecov: inspected */
if (grant_option && want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table,0,0))
goto create_error;
#ifndef HAVE_READLINK #ifndef HAVE_READLINK
lex->create_info.data_file_name=lex->create_info.index_file_name=0; lex->create_info.data_file_name=lex->create_info.index_file_name=0;
#else #else
...@@ -2272,7 +2261,7 @@ mysql_execute_command(THD *thd) ...@@ -2272,7 +2261,7 @@ mysql_execute_command(THD *thd)
break; break;
} }
case SQLCOM_CREATE_INDEX: case SQLCOM_CREATE_INDEX:
if (check_one_table_access(thd, INDEX_ACL, tables, 0)) if (check_one_table_access(thd, INDEX_ACL, tables))
goto error; /* purecov: inspected */ goto error; /* purecov: inspected */
thd->slow_command=TRUE; thd->slow_command=TRUE;
if (end_active_trans(thd)) if (end_active_trans(thd))
...@@ -2339,7 +2328,7 @@ mysql_execute_command(THD *thd) ...@@ -2339,7 +2328,7 @@ mysql_execute_command(THD *thd)
goto error; /* purecov: inspected */ goto error; /* purecov: inspected */
if (grant_option) if (grant_option)
{ {
if (check_grant(thd,ALTER_ACL,tables,0,0)) if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
goto error; goto error;
if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
{ // Rename of table { // Rename of table
...@@ -2348,7 +2337,8 @@ mysql_execute_command(THD *thd) ...@@ -2348,7 +2337,8 @@ mysql_execute_command(THD *thd)
tmp_table.real_name=lex->name; tmp_table.real_name=lex->name;
tmp_table.db=select_lex->db; tmp_table.db=select_lex->db;
tmp_table.grant.privilege=priv; tmp_table.grant.privilege=priv;
if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, 0)) if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
UINT_MAX, 0))
goto error; goto error;
} }
} }
...@@ -2397,10 +2387,11 @@ mysql_execute_command(THD *thd) ...@@ -2397,10 +2387,11 @@ mysql_execute_command(THD *thd)
old_list=table[0]; old_list=table[0];
new_list=table->next[0]; new_list=table->next[0];
old_list.next=new_list.next=0; old_list.next=new_list.next=0;
if (check_grant(thd,ALTER_ACL,&old_list,0,0) || if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
(!test_all_bits(table->next->grant.privilege, (!test_all_bits(table->next->grant.privilege,
INSERT_ACL | CREATE_ACL) && INSERT_ACL | CREATE_ACL) &&
check_grant(thd,INSERT_ACL | CREATE_ACL, &new_list,0,0))) check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
UINT_MAX, 0)))
goto error; goto error;
} }
} }
...@@ -2535,15 +2526,8 @@ mysql_execute_command(THD *thd) ...@@ -2535,15 +2526,8 @@ mysql_execute_command(THD *thd)
break; break;
} }
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
if (select_lex->item_list.elements != lex->value_list.elements) if (update_precheck(thd, tables))
{ break;
send_error(thd,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
}
if (check_db_used(thd,tables))
goto error;
if (check_one_table_access(thd, UPDATE_ACL, tables, 0))
goto error;
res= mysql_update(thd,tables, res= mysql_update(thd,tables,
select_lex->item_list, select_lex->item_list,
lex->value_list, lex->value_list,
...@@ -2570,17 +2554,9 @@ mysql_execute_command(THD *thd) ...@@ -2570,17 +2554,9 @@ mysql_execute_command(THD *thd)
case SQLCOM_REPLACE: case SQLCOM_REPLACE:
case SQLCOM_INSERT: case SQLCOM_INSERT:
{ {
my_bool update=(lex->value_list.elements ? UPDATE_ACL : 0); my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
ulong privilege= (lex->duplicates == DUP_REPLACE ? if ((res= insert_precheck(thd, tables, update)))
INSERT_ACL | DELETE_ACL : INSERT_ACL | update); break;
if (check_one_table_access(thd, privilege, tables, 0))
goto error;
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
}
res = mysql_insert(thd,tables,lex->field_list,lex->many_values, res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
select_lex->item_list, lex->value_list, select_lex->item_list, lex->value_list,
(update ? DUP_UPDATE : lex->duplicates)); (update ? DUP_UPDATE : lex->duplicates));
...@@ -2634,7 +2610,7 @@ mysql_execute_command(THD *thd) ...@@ -2634,7 +2610,7 @@ mysql_execute_command(THD *thd)
break; break;
} }
case SQLCOM_TRUNCATE: case SQLCOM_TRUNCATE:
if (check_one_table_access(thd, DELETE_ACL, tables, 0)) if (check_one_table_access(thd, DELETE_ACL, tables))
goto error; goto error;
/* /*
Don't allow this within a transaction because we want to use Don't allow this within a transaction because we want to use
...@@ -2649,10 +2625,8 @@ mysql_execute_command(THD *thd) ...@@ -2649,10 +2625,8 @@ mysql_execute_command(THD *thd)
break; break;
case SQLCOM_DELETE: case SQLCOM_DELETE:
{ {
if (check_one_table_access(thd, DELETE_ACL, tables, 0)) if ((res= delete_precheck(thd, tables)))
goto error; break;
// Set privilege for the WHERE clause
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
res = mysql_delete(thd,tables, select_lex->where, res = mysql_delete(thd,tables, select_lex->where,
&select_lex->order_list, &select_lex->order_list,
select_lex->select_limit, select_lex->options); select_lex->select_limit, select_lex->options);
...@@ -2664,10 +2638,9 @@ mysql_execute_command(THD *thd) ...@@ -2664,10 +2638,9 @@ mysql_execute_command(THD *thd)
{ {
TABLE_LIST *aux_tables= TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first; (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
TABLE_LIST *delete_tables= (TABLE_LIST*)select_lex->table_list.first;
TABLE_LIST *auxi; TABLE_LIST *target_tbl;
uint table_count= 0; uint table_count;
multi_delete *result; multi_delete *result;
if ((res= multi_delete_precheck(thd, tables, &table_count))) if ((res= multi_delete_precheck(thd, tables, &table_count)))
break; break;
...@@ -2685,9 +2658,11 @@ mysql_execute_command(THD *thd) ...@@ -2685,9 +2658,11 @@ mysql_execute_command(THD *thd)
if ((res=open_and_lock_tables(thd,tables))) if ((res=open_and_lock_tables(thd,tables)))
break; break;
/* Fix tables-to-be-deleted-from list to point at opened tables */ /* Fix tables-to-be-deleted-from list to point at opened tables */
for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) for (target_tbl= (TABLE_LIST*) aux_tables;
target_tbl;
target_tbl= target_tbl->next)
{ {
auxi->table= auxi->table_list->table; target_tbl->table= target_tbl->table_list->table;
/* /*
Multi-delete can't be constructed over-union => we always have Multi-delete can't be constructed over-union => we always have
single SELECT on top and have to check underlaying SELECTs of it single SELECT on top and have to check underlaying SELECTs of it
...@@ -2697,10 +2672,11 @@ mysql_execute_command(THD *thd) ...@@ -2697,10 +2672,11 @@ mysql_execute_command(THD *thd)
un= un->next_unit()) un= un->next_unit())
{ {
if (un->first_select()->linkage != DERIVED_TABLE_TYPE && if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
un->check_updateable(auxi->table_list->db, un->check_updateable(target_tbl->table_list->db,
auxi->table_list->real_name)) target_tbl->table_list->real_name))
{ {
my_error(ER_UPDATE_TABLE_USED, MYF(0), auxi->table_list->real_name); my_error(ER_UPDATE_TABLE_USED, MYF(0),
target_tbl->table_list->real_name);
res= -1; res= -1;
break; break;
} }
...@@ -2758,7 +2734,7 @@ mysql_execute_command(THD *thd) ...@@ -2758,7 +2734,7 @@ mysql_execute_command(THD *thd)
} }
break; break;
case SQLCOM_DROP_INDEX: case SQLCOM_DROP_INDEX:
if (check_one_table_access(thd, INDEX_ACL, tables, 0)) if (check_one_table_access(thd, INDEX_ACL, tables))
goto error; /* purecov: inspected */ goto error; /* purecov: inspected */
if (end_active_trans(thd)) if (end_active_trans(thd))
res= -1; res= -1;
...@@ -2873,7 +2849,7 @@ mysql_execute_command(THD *thd) ...@@ -2873,7 +2849,7 @@ mysql_execute_command(THD *thd)
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
&tables->grant.privilege, 0, 0)) &tables->grant.privilege, 0, 0))
goto error; /* purecov: inspected */ goto error; /* purecov: inspected */
if (grant_option && check_grant(thd,SELECT_ACL,tables,2,0)) if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
goto error; goto error;
res= mysqld_show_fields(thd,tables, res= mysqld_show_fields(thd,tables,
(lex->wild ? lex->wild->ptr() : NullS), (lex->wild ? lex->wild->ptr() : NullS),
...@@ -2893,7 +2869,7 @@ mysql_execute_command(THD *thd) ...@@ -2893,7 +2869,7 @@ mysql_execute_command(THD *thd)
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
&tables->grant.privilege, 0, 0)) &tables->grant.privilege, 0, 0))
goto error; /* purecov: inspected */ goto error; /* purecov: inspected */
if (grant_option && check_grant(thd,SELECT_ACL,tables,2,0)) if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
goto error; goto error;
res= mysqld_show_keys(thd,tables); res= mysqld_show_keys(thd,tables);
break; break;
...@@ -2921,7 +2897,7 @@ mysql_execute_command(THD *thd) ...@@ -2921,7 +2897,7 @@ mysql_execute_command(THD *thd)
send_error(thd,ER_NOT_ALLOWED_COMMAND); send_error(thd,ER_NOT_ALLOWED_COMMAND);
goto error; goto error;
} }
if (check_one_table_access(thd, privilege, tables, 0)) if (check_one_table_access(thd, privilege, tables))
goto error; goto error;
} }
res=mysql_load(thd, lex->exchange, tables, lex->field_list, res=mysql_load(thd, lex->exchange, tables, lex->field_list,
...@@ -3189,7 +3165,7 @@ mysql_execute_command(THD *thd) ...@@ -3189,7 +3165,7 @@ mysql_execute_command(THD *thd)
if (grant_option && check_grant(thd, if (grant_option && check_grant(thd,
(lex->grant | lex->grant_tot_col | (lex->grant | lex->grant_tot_col |
GRANT_ACL), GRANT_ACL),
tables,0,0)) tables, 0, UINT_MAX, 0))
goto error; goto error;
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
lex->grant, lex->grant,
...@@ -3406,32 +3382,26 @@ mysql_execute_command(THD *thd) ...@@ -3406,32 +3382,26 @@ mysql_execute_command(THD *thd)
thd Thread handler thd Thread handler
privilege requested privelage privilege requested privelage
tables table list of command tables table list of command
no_errors Don't send error to client
RETURN RETURN
0 - OK 0 - OK
1 - access denied, error is sent to client 1 - access denied, error is sent to client
*/ */
int check_one_table_access(THD *thd, ulong privilege, int check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables, bool no_errors) TABLE_LIST *tables)
{ {
if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
return 1; return 1;
// Show only 1 table for check_grant // Show only 1 table for check_grant
TABLE_LIST *subselects_tables= tables->next; if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
tables->next= 0;
if (grant_option && check_grant(thd, privilege, tables, 0, 0))
{
tables->next= subselects_tables;
return 1; return 1;
}
// check rights on tables of subselect (if exists) // check rights on tables of subselect (if exists)
if (subselects_tables) TABLE_LIST *subselects_tables;
if ((subselects_tables= tables->next))
{ {
tables->next= subselects_tables;
if ((check_table_access(thd, SELECT_ACL, subselects_tables,0))) if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
return 1; return 1;
} }
...@@ -3610,7 +3580,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, ...@@ -3610,7 +3580,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
} }
if (grant_option) if (grant_option)
return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
test(want_access & EXTRA_ACL), no_errors); test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
return FALSE; return FALSE;
} }
...@@ -4961,7 +4931,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) ...@@ -4961,7 +4931,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
thd - thread handler thd - thread handler
tables - global table list tables - global table list
RETURN RETURN VALUE
0 - OK 0 - OK
1 - error (message is sent to user) 1 - error (message is sent to user)
-1 - error (message is not sent to user) -1 - error (message is not sent to user)
...@@ -4986,27 +4956,30 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) ...@@ -4986,27 +4956,30 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
*/ */
for (table= update_list; table; table= table->next) for (table= update_list; table; table= table->next)
{ {
TABLE_LIST *save= table->next;
table->next= 0;
if ((check_access(thd, UPDATE_ACL, table->db, if ((check_access(thd, UPDATE_ACL, table->db,
&table->grant.privilege, 0, 1) || &table->grant.privilege, 0, 1) ||
grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1)) && grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
(check_access(thd, SELECT_ACL, table->db, (check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0) || &table->grant.privilege, 0, 0) ||
grant_option && check_grant(thd, SELECT_ACL, table, 0, 0))) grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
DBUG_RETURN(1); DBUG_RETURN(1);
table->next= save;
/*
We assign following flag only to copy of table, because it will
be checked only if query contains subqueries i.e. only if copy exists
*/
if (table->table_list) if (table->table_list)
table->table_list->checked= 1; table->table_list->table_in_update_from_clause= 1;
} }
// tables of subqueries /*
Is there tables of subqueries?
*/
if (&lex->select_lex != lex->all_selects_list) if (&lex->select_lex != lex->all_selects_list)
{ {
for (table= tables; table; table= table->next) for (table= tables; table; table= table->next)
{ {
if (table->checked) if (table->table_in_update_from_clause)
{ {
table->checked= 0;
/* /*
If we check table by local TABLE_LIST copy then we should copy If we check table by local TABLE_LIST copy then we should copy
grants to global table list, because it will be used for table grants to global table list, because it will be used for table
...@@ -5017,13 +4990,10 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) ...@@ -5017,13 +4990,10 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
} }
else else
{ {
TABLE_LIST *save= table->next;
table->next= 0;
if (check_access(thd, SELECT_ACL, table->db, if (check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0) || &table->grant.privilege, 0, 0) ||
grant_option && check_grant(thd, SELECT_ACL, table, 0, 0)) grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
DBUG_RETURN(1); DBUG_RETURN(1);
table->next= save;
} }
} }
} }
...@@ -5050,7 +5020,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) ...@@ -5050,7 +5020,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
tables - global table list tables - global table list
table_count - pointer to table counter table_count - pointer to table counter
RETURN RETURN VALUE
0 - OK 0 - OK
1 - error (message is sent to user) 1 - error (message is sent to user)
-1 - error (message is not sent to user) -1 - error (message is not sent to user)
...@@ -5062,7 +5032,9 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ...@@ -5062,7 +5032,9 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
TABLE_LIST *aux_tables= TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first; (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first; TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
TABLE_LIST *auxi; TABLE_LIST *target_tbl;
*table_count= 0;
/* sql_yacc guarantees that tables and aux_tables are not zero */ /* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0); DBUG_ASSERT(aux_tables != 0);
...@@ -5075,29 +5047,32 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ...@@ -5075,29 +5047,32 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0)); my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
for (auxi= aux_tables; auxi; auxi= auxi->next) for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
{ {
(*table_count)++; (*table_count)++;
/* All tables in aux_tables must be found in FROM PART */ /* All tables in aux_tables must be found in FROM PART */
TABLE_LIST *walk; TABLE_LIST *walk;
for (walk= delete_tables; walk; walk= walk->next) for (walk= delete_tables; walk; walk= walk->next)
{ {
if (!my_strcasecmp(table_alias_charset, auxi->alias, walk->alias) && if (!my_strcasecmp(table_alias_charset,
!strcmp(walk->db, auxi->db)) target_tbl->alias, walk->alias) &&
!strcmp(walk->db, target_tbl->db))
break; break;
} }
if (!walk) if (!walk)
{ {
my_error(ER_UNKNOWN_TABLE, MYF(0), auxi->real_name, "MULTI DELETE"); my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
"MULTI DELETE");
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
if (walk->derived) if (walk->derived)
{ {
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), auxi->real_name, "DELETE"); my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
"DELETE");
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
walk->lock_type= auxi->lock_type; walk->lock_type= target_tbl->lock_type;
auxi->table_list= walk; // Remember corresponding table target_tbl->table_list= walk; // Remember corresponding table
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -5111,7 +5086,7 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ...@@ -5111,7 +5086,7 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
thd - thread handler thd - thread handler
tables - global table list tables - global table list
RETURN RETURN VALUE
0 - OK 0 - OK
1 - error (message is sent to user) 1 - error (message is sent to user)
-1 - error (message is not sent to user) -1 - error (message is not sent to user)
...@@ -5125,7 +5100,122 @@ int insert_select_precheck(THD *thd, TABLE_LIST *tables) ...@@ -5125,7 +5100,122 @@ int insert_select_precheck(THD *thd, TABLE_LIST *tables)
*/ */
ulong privilege= (thd->lex->duplicates == DUP_REPLACE ? ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL); INSERT_ACL | DELETE_ACL : INSERT_ACL);
if (check_one_table_access(thd, privilege, tables, 0)) DBUG_RETURN(check_one_table_access(thd, privilege, tables) ? 1 : 0);
}
/*
simple UPDATE query pre-check
SYNOPSIS
update_precheck()
thd - thread handler
tables - global table list
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int update_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("update_precheck");
if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
{
my_error(ER_WRONG_VALUE_COUNT, MYF(0));
DBUG_RETURN(-1);
}
DBUG_RETURN((check_db_used(thd, tables) ||
check_one_table_access(thd, UPDATE_ACL, tables)) ? 1 : 0);
}
/*
simple DELETE query pre-check
SYNOPSIS
delete_precheck()
thd - thread handler
tables - global table list
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int delete_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("delete_precheck");
if (check_one_table_access(thd, DELETE_ACL, tables))
DBUG_RETURN(1); DBUG_RETURN(1);
// Set privilege for the WHERE clause
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/*
simple INSERT query pre-check
SYNOPSIS
insert_precheck()
thd - thread handler
tables - global table list
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int insert_precheck(THD *thd, TABLE_LIST *tables, bool update)
{
LEX *lex= thd->lex;
DBUG_ENTER("insert_precheck");
ulong privilege= (lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
if (check_one_table_access(thd, privilege, tables))
DBUG_RETURN(1);
if (lex->select_lex.item_list.elements != lex->value_list.elements)
{
my_error(ER_WRONG_VALUE_COUNT, MYF(0));
DBUG_RETURN(-1);
}
DBUG_RETURN(0);
}
/*
CREATE TABLE query pre-check
SYNOPSIS
create_table_precheck()
thd - thread handler
tables - global table list
create_table - table which will be created
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table)
{
LEX *lex= thd->lex;
DBUG_ENTER("create_table_precheck");
ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
CREATE_TMP_ACL : CREATE_ACL);
lex->create_info.alias= create_table->alias;
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege, 0, 0) ||
check_merge_table_access(thd, create_table->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
DBUG_RETURN(1);
DBUG_RETURN((grant_option && want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) ?
1 : 0)
}
...@@ -631,19 +631,21 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) ...@@ -631,19 +631,21 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
#endif /*!EMBEDDED_LIBRARY*/ #endif /*!EMBEDDED_LIBRARY*/
/* /*
Validate the following information for INSERT statement: Validate INSERT statement:
- field existence
- fields count
SYNOPSIS SYNOPSIS
mysql_test_insert_fields() mysql_test_insert()
stmt prepared statemen handler
tables list of tables queries
RETURN VALUE RETURN VALUE
0 ok 0 ok
1 error, sent to the client 1 error, sent to the client
-1 error, not sent to client -1 error, not sent to client
*/ */
static int mysql_test_insert(Prepared_statement *stmt,
static int mysql_test_insert_fields(Prepared_statement *stmt,
TABLE_LIST *table_list, TABLE_LIST *table_list,
List<Item> &fields, List<Item> &fields,
List<List_item> &values_list, List<List_item> &values_list,
...@@ -653,20 +655,16 @@ static int mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -653,20 +655,16 @@ static int mysql_test_insert_fields(Prepared_statement *stmt,
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
TABLE *table;
List_iterator_fast<List_item> its(values_list); List_iterator_fast<List_item> its(values_list);
List_item *values; List_item *values;
int res= -1;
TABLE_LIST *insert_table_list= TABLE_LIST *insert_table_list=
(TABLE_LIST*) lex->select_lex.table_list.first; (TABLE_LIST*) lex->select_lex.table_list.first;
my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
DBUG_ENTER("mysql_test_insert_fields"); DBUG_ENTER("mysql_test_insert_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS if ((res= insert_precheck(thd, table_list, update)))
ulong privilege= (lex->duplicates == DUP_REPLACE ? DBUG_RETURN(res);
INSERT_ACL | DELETE_ACL : INSERT_ACL);
if (check_one_table_access(thd, privilege, table_list, 0))
DBUG_RETURN(1);
#endif
/* /*
open temporary memory pool for temporary data allocated by derived open temporary memory pool for temporary data allocated by derived
...@@ -679,26 +677,15 @@ static int mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -679,26 +677,15 @@ static int mysql_test_insert_fields(Prepared_statement *stmt,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
table= table_list->table;
if ((values= its++)) if ((values= its++))
{ {
uint value_count; uint value_count;
ulong counter= 0; ulong counter= 0;
if (check_insert_fields(thd, table, fields, *values, 1) || if ((res= mysql_prepare_insert(thd, table_list, insert_table_list,
setup_tables(insert_table_list) || table_list->table, fields, values,
setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || update_fields, update_values, duplic)))
(duplic == DUP_UPDATE &&
(setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
goto error; goto error;
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
goto error;
}
value_count= values->elements; value_count= values->elements;
its.rewind(); its.rewind();
...@@ -717,50 +704,38 @@ static int mysql_test_insert_fields(Prepared_statement *stmt, ...@@ -717,50 +704,38 @@ static int mysql_test_insert_fields(Prepared_statement *stmt,
goto error; goto error;
} }
} }
lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(0);
res= 0;
error: error:
lex->unit.cleanup(); lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(-1); DBUG_RETURN(res);
} }
/* /*
Validate the following information: Validate UPDATE statement
UPDATE - set and where clause
DELETE - where clause
SYNOPSIS SYNOPSIS
mysql_test_upd_fields() mysql_test_delete()
stmt prepared statemen handler
tables list of tables queries
RETURN VALUE RETURN VALUE
0 success 0 success
1 error, sent to client 1 error, sent to client
-1 error, not sent to client -1 error, not sent to client
*/ */
static int mysql_test_update(Prepared_statement *stmt,
static int mysql_test_upd_fields(Prepared_statement *stmt, TABLE_LIST *table_list)
TABLE_LIST *table_list,
List<Item> &fields, List<Item> &values,
COND *conds, ulong privelege)
{ {
int res;
THD *thd= stmt->thd; THD *thd= stmt->thd;
SELECT_LEX *select_lex= &stmt->lex->select_lex; SELECT_LEX *select= &stmt->lex->select_lex;
TABLE_LIST *upd_table_list= DBUG_ENTER("mysql_test_update");
(TABLE_LIST*) select_lex->table_list.first;
List<Item> all_fields; if ((res= update_precheck(thd, table_list)))
uint order_num= select_lex->order_list.elements; DBUG_RETURN(res);
ORDER *order= (ORDER *) select_lex->order_list.first;
DBUG_ENTER("mysql_test_upd_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_one_table_access(thd, privelege, table_list, 0))
DBUG_RETURN(1);
// Set privilege for the WHERE clause
table_list->grant.want_privilege= (SELECT_ACL &
~table_list->grant.privilege);
#endif
/* /*
open temporary memory pool for temporary data allocated by derived open temporary memory pool for temporary data allocated by derived
...@@ -769,57 +744,97 @@ static int mysql_test_upd_fields(Prepared_statement *stmt, ...@@ -769,57 +744,97 @@ static int mysql_test_upd_fields(Prepared_statement *stmt,
thd->allocate_temporary_memory_pool_for_ps_preparing(); thd->allocate_temporary_memory_pool_for_ps_preparing();
if (open_and_lock_tables(thd, table_list)) if (open_and_lock_tables(thd, table_list))
goto err; res= -1;
if (setup_tables(upd_table_list) || else
setup_conds(thd, upd_table_list, &conds) ||
thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_fields(thd, 0, upd_table_list, fields, 1, 0, 0) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
upd_table_list, all_fields, all_fields, order) ||
thd->net.report_error)
{ {
stmt->lex->unit.cleanup(); TABLE_LIST *update_table_list= (TABLE_LIST *)select->table_list.first;
goto err; if (!(res= mysql_prepare_update(thd, table_list,
update_table_list,
&select->where,
select->order_list.elements,
(ORDER *) select->order_list.first)))
{
if (setup_fields(thd, 0, update_table_list,
select->item_list, 1, 0, 0) ||
setup_fields(thd, 0, update_table_list,
stmt->lex->value_list, 0, 0, 0))
res= -1;
} }
stmt->lex->unit.cleanup(); stmt->lex->unit.cleanup();
}
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
/* TODO: here we should send types of placeholders to the client. */ /* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(0); DBUG_RETURN(res);
err: }
/*
Validate DELETE statement
SYNOPSIS
mysql_test_delete()
stmt prepared statemen handler
tables list of tables queries
RETURN VALUE
0 success
1 error, sent to client
-1 error, not sent to client
*/
static int mysql_test_delete(Prepared_statement *stmt,
TABLE_LIST *table_list)
{
int res;
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
DBUG_ENTER("mysql_test_delete");
if ((res= delete_precheck(thd, table_list)))
DBUG_RETURN(res);
/*
open temporary memory pool for temporary data allocated by derived
tables & preparation procedure
*/
thd->allocate_temporary_memory_pool_for_ps_preparing();
if (open_and_lock_tables(thd, table_list))
res= -1;
else
{
res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
lex->unit.cleanup();
}
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(-1); /* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(res);
} }
/* /*
Validate the following information: Validate SELECT statement.
SELECT - column list
- where clause
- order clause
- having clause
- group by clause
- if no column spec i.e. '*', then setup all fields
In case of success, if this query is not EXPLAIN, send column list info In case of success, if this query is not EXPLAIN, send column list info
back to client. back to client.
SYNOPSIS SYNOPSIS
mysql_test_select_fields() mysql_test_select()
stmt prepared statemen handler
tables list of tables queries
RETURN VALUE RETURN VALUE
0 success 0 success
1 error, sent to client 1 error, sent to client
-1 error, not sent to client -1 error, not sent to client
*/ */
static int mysql_test_select_fields(Prepared_statement *stmt, static int mysql_test_select(Prepared_statement *stmt,
TABLE_LIST *tables, TABLE_LIST *tables)
List<Item> &fields)
{ {
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
SELECT_LEX_UNIT *unit= &lex->unit; SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_test_select");
DBUG_ENTER("mysql_test_select_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
...@@ -852,14 +867,15 @@ static int mysql_test_select_fields(Prepared_statement *stmt, ...@@ -852,14 +867,15 @@ static int mysql_test_select_fields(Prepared_statement *stmt,
{ {
thd->used_tables= 0; // Updated by setup_fields thd->used_tables= 0; // Updated by setup_fields
// JOIN::prepare calls
if (unit->prepare(thd, 0, 0)) if (unit->prepare(thd, 0, 0))
{ {
send_error(thd); send_error(thd);
goto err_prep; goto err_prep;
} }
if (send_prep_stmt(stmt, fields.elements) || if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
thd->protocol_simple.send_fields(&fields, 0) thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
|| net_flush(&thd->net) || net_flush(&thd->net)
#endif #endif
...@@ -964,11 +980,8 @@ static int mysql_test_set_fields(Prepared_statement *stmt, ...@@ -964,11 +980,8 @@ static int mysql_test_set_fields(Prepared_statement *stmt,
goto error; goto error;
} }
} }
stmt->lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(0);
error: error:
stmt->lex->unit.cleanup();
thd->free_temporary_memory_pool_for_ps_preparing(); thd->free_temporary_memory_pool_for_ps_preparing();
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -982,7 +995,7 @@ static int mysql_test_set_fields(Prepared_statement *stmt, ...@@ -982,7 +995,7 @@ static int mysql_test_set_fields(Prepared_statement *stmt,
stmt - prepared table handler stmt - prepared table handler
tables - global list of tables tables - global list of tables
RETURN RETURN VALUE
0 success 0 success
1 error, sent to client 1 error, sent to client
-1 error, not sent to client -1 error, not sent to client
...@@ -1004,6 +1017,7 @@ static int select_like_statement_test(Prepared_statement *stmt, ...@@ -1004,6 +1017,7 @@ static int select_like_statement_test(Prepared_statement *stmt,
thd->used_tables= 0; // Updated by setup_fields thd->used_tables= 0; // Updated by setup_fields
// JOIN::prepare calls
if (lex->unit.prepare(thd, 0, 0)) if (lex->unit.prepare(thd, 0, 0))
{ {
res= thd->net.report_error ? -1 : 1; res= thd->net.report_error ? -1 : 1;
...@@ -1036,36 +1050,22 @@ static int mysql_test_create_table(Prepared_statement *stmt, ...@@ -1036,36 +1050,22 @@ static int mysql_test_create_table(Prepared_statement *stmt,
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
int res= 0; int res= 0;
ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
CREATE_TMP_ACL : CREATE_ACL);
if (check_one_table_access(thd, want_priv, tables, 0) ||
check_merge_table_access(thd, tables->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
DBUG_RETURN(1);
/* Skip first table, which is the table we are creating */ /* Skip first table, which is the table we are creating */
TABLE_LIST *create_table, *create_table_local; TABLE_LIST *create_table, *create_table_local;
tables= lex->unlink_first_table(tables, &create_table, tables= lex->unlink_first_table(tables, &create_table,
&create_table_local); &create_table_local);
if (grant_option && want_priv != CREATE_TMP_ACL && if (!(res= create_table_precheck(thd, tables, create_table)) &&
check_grant(thd, want_priv, create_table,0,0)) lex->select_lex.item_list.elements)
{
res= 1;
goto end;
}
if (tables)
res= select_like_statement_test(stmt, tables); res= select_like_statement_test(stmt, tables);
end: /* put tables back for PS rexecuting */
// put tables back for PS rexecuting
tables= lex->link_first_table_back(tables, create_table, tables= lex->link_first_table_back(tables, create_table,
create_table_local); create_table_local);
DBUG_RETURN(res); DBUG_RETURN(res);
} }
/* /*
Validate and prepare for execution multy update statement Validate and prepare for execution multy update statement
...@@ -1110,7 +1110,7 @@ static int mysql_test_multidelete(Prepared_statement *stmt, ...@@ -1110,7 +1110,7 @@ static int mysql_test_multidelete(Prepared_statement *stmt,
if (add_item_to_list(stmt->thd, new Item_null())) if (add_item_to_list(stmt->thd, new Item_null()))
return -1; return -1;
uint fake_counter= 0; uint fake_counter;
if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter))) if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter)))
return res; return res;
return select_like_statement_test(stmt, tables); return select_like_statement_test(stmt, tables);
...@@ -1161,78 +1161,68 @@ static int mysql_test_insert_select(Prepared_statement *stmt, ...@@ -1161,78 +1161,68 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
*/ */
static int send_prepare_results(Prepared_statement *stmt) static int send_prepare_results(Prepared_statement *stmt)
{ {
DBUG_ENTER("send_prepare_results");
THD *thd= stmt->thd; THD *thd= stmt->thd;
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex; SELECT_LEX *select_lex= &lex->select_lex;
TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
enum enum_sql_command sql_command= lex->sql_command; enum enum_sql_command sql_command= lex->sql_command;
int res; int res= 0;
DBUG_ENTER("send_prepare_results");
DBUG_PRINT("enter",("command: %d, param_count: %ld", DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, stmt->param_count)); sql_command, stmt->param_count));
if (&lex->select_lex != lex->all_selects_list && if (select_lex != lex->all_selects_list &&
lex->unit.create_total_list(thd, lex, &tables)) lex->unit.create_total_list(thd, lex, &tables))
DBUG_RETURN(1); DBUG_RETURN(1);
switch (sql_command) { switch (sql_command) {
case SQLCOM_REPLACE: case SQLCOM_REPLACE:
case SQLCOM_INSERT: case SQLCOM_INSERT:
if ((res= res= mysql_test_insert(stmt, tables, lex->field_list,
mysql_test_insert_fields(stmt, tables, lex->field_list,
lex->many_values, lex->many_values,
select_lex->item_list, lex->value_list, select_lex->item_list, lex->value_list,
(lex->value_list.elements ? (lex->value_list.elements ?
DUP_UPDATE : lex->duplicates)))) DUP_UPDATE : lex->duplicates));
goto error;
break; break;
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
/* XXX: fallthrough */ res= mysql_test_update(stmt, tables);
break;
case SQLCOM_DELETE: case SQLCOM_DELETE:
if ((res= mysql_test_upd_fields(stmt, tables, select_lex->item_list, res= mysql_test_delete(stmt, tables);
lex->value_list, select_lex->where,
((sql_command == SQLCOM_DELETE)?
DELETE_ACL : UPDATE_ACL))))
goto error;
break; break;
case SQLCOM_SELECT: case SQLCOM_SELECT:
if ((res= mysql_test_select_fields(stmt, tables, select_lex->item_list))) if ((res= mysql_test_select(stmt, tables)))
goto error; goto error;
/* Statement and field info has already been sent */ /* Statement and field info has already been sent */
DBUG_RETURN(0); DBUG_RETURN(0);
case SQLCOM_CREATE_TABLE: case SQLCOM_CREATE_TABLE:
if ((res= mysql_test_create_table(stmt, tables))) res= mysql_test_create_table(stmt, tables);
goto error;
break; break;
case SQLCOM_DO: case SQLCOM_DO:
if ((res= mysql_test_do_fields(stmt, tables, lex->insert_list))) res= mysql_test_do_fields(stmt, tables, lex->insert_list);
goto error;
break; break;
case SQLCOM_SET_OPTION: case SQLCOM_SET_OPTION:
if ((res= mysql_test_set_fields(stmt, tables, &lex->var_list))) res= mysql_test_set_fields(stmt, tables, &lex->var_list);
goto error;
break; break;
case SQLCOM_DELETE_MULTI: case SQLCOM_DELETE_MULTI:
if ((res= mysql_test_multidelete(stmt, tables))) res= mysql_test_multidelete(stmt, tables);
goto error;
break; break;
case SQLCOM_UPDATE_MULTI: case SQLCOM_UPDATE_MULTI:
if ((res= mysql_test_multiupdate(stmt, tables))) res= mysql_test_multiupdate(stmt, tables);
goto error;
break; break;
case SQLCOM_INSERT_SELECT: case SQLCOM_INSERT_SELECT:
if ((res= mysql_test_insert_select(stmt, tables))) res= mysql_test_insert_select(stmt, tables);
goto error;
break; break;
case SQLCOM_SHOW_DATABASES: case SQLCOM_SHOW_DATABASES:
...@@ -1263,8 +1253,8 @@ static int send_prepare_results(Prepared_statement *stmt) ...@@ -1263,8 +1253,8 @@ static int send_prepare_results(Prepared_statement *stmt)
my_error(ER_UNSUPPORTED_PS, MYF(0)); my_error(ER_UNSUPPORTED_PS, MYF(0));
goto error; goto error;
} }
if (res == 0)
DBUG_RETURN(send_prep_stmt(stmt, 0)); DBUG_RETURN(send_prep_stmt(stmt, 0));
error: error:
if (res < 0) if (res < 0)
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
......
...@@ -436,7 +436,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, ...@@ -436,7 +436,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
table_list.db= (char*) db; table_list.db= (char*) db;
table_list.real_name=file->name; table_list.real_name=file->name;
table_list.grant.privilege=col_access; table_list.grant.privilege=col_access;
if (check_grant(thd,TABLE_ACLS,&table_list,1,1)) if (check_grant(thd, TABLE_ACLS, &table_list, 1, UINT_MAX, 1))
continue; continue;
} }
#endif #endif
......
...@@ -70,8 +70,6 @@ int mysql_update(THD *thd, ...@@ -70,8 +70,6 @@ int mysql_update(THD *thd,
READ_RECORD info; READ_RECORD info;
TABLE_LIST *update_table_list= ((TABLE_LIST*) TABLE_LIST *update_table_list= ((TABLE_LIST*)
thd->lex->select_lex.table_list.first); thd->lex->select_lex.table_list.first);
TABLE_LIST tables;
List<Item> all_fields;
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
LINT_INIT(used_index); LINT_INIT(used_index);
...@@ -86,30 +84,13 @@ int mysql_update(THD *thd, ...@@ -86,30 +84,13 @@ int mysql_update(THD *thd,
/* Calculate "table->used_keys" based on the WHERE */ /* Calculate "table->used_keys" based on the WHERE */
table->used_keys=table->keys_in_use; table->used_keys=table->keys_in_use;
table->quick_keys.clear_all(); table->quick_keys.clear_all();
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
want_privilege=table->grant.want_privilege; want_privilege= table->grant.want_privilege;
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
#endif #endif
if ((error= mysql_prepare_update(thd, table_list, update_table_list,
bzero((char*) &tables,sizeof(tables)); // For ORDER BY &conds, order_num, order)))
tables.table= table; DBUG_RETURN(error);
tables.alias= table_list->alias;
if (setup_tables(update_table_list) ||
setup_conds(thd,update_table_list,&conds) ||
thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
update_table_list, all_fields, all_fields, order) ||
setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1); /* purecov: inspected */
/* Check that we are not using table that we are updating in a sub select */
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
old_used_keys= table->used_keys; // Keys used in WHERE old_used_keys= table->used_keys; // Keys used in WHERE
/* /*
...@@ -406,6 +387,59 @@ int mysql_update(THD *thd, ...@@ -406,6 +387,59 @@ int mysql_update(THD *thd,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
/*
Prepare items in UPDATE statement
SYNOPSIS
mysql_prepare_update()
thd - thread handler
table_list - global table list
update_table_list - local table list of UPDATE SELECT_LEX
conds - conditions
order_num - number of ORDER BY list entries
order - ORDER BY clause list
RETURN VALUE
0 - OK
1 - error (message is sent to user)
-1 - error (message is not sent to user)
*/
int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *update_table_list,
Item **conds, uint order_num, ORDER *order)
{
TABLE *table= table_list->table;
TABLE_LIST tables;
List<Item> all_fields;
DBUG_ENTER("mysql_prepare_update");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
#endif
bzero((char*) &tables,sizeof(tables)); // For ORDER BY
tables.table= table;
tables.alias= table_list->alias;
if (setup_tables(update_table_list) ||
setup_conds(thd, update_table_list, conds) ||
thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
update_table_list, all_fields, all_fields, order) ||
setup_ftfuncs(&thd->lex->select_lex))
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,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
DBUG_RETURN(0);
}
/*************************************************************************** /***************************************************************************
Update multiple tables from join Update multiple tables from join
......
...@@ -194,7 +194,8 @@ typedef struct st_table_list ...@@ -194,7 +194,8 @@ typedef struct st_table_list
bool force_index; /* Prefer index over table scan */ bool force_index; /* Prefer index over table scan */
bool ignore_leaves; /* Preload only non-leaf nodes */ bool ignore_leaves; /* Preload only non-leaf nodes */
bool cacheable_table; /* stop PS caching */ bool cacheable_table; /* stop PS caching */
bool checked; /* used in multi-upd privelege check */ /* used in multi-upd privelege check */
bool table_in_update_from_clause;
} TABLE_LIST; } TABLE_LIST;
typedef struct st_changed_table_list typedef struct st_changed_table_list
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment