summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc210
1 files changed, 132 insertions, 78 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 963607f2d0e..591a2dc0807 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -48,6 +48,35 @@ static bool compare_record(TABLE *table, ulong query_id)
}
+/*
+ check that all fields are real fields
+
+ SYNOPSIS
+ check_fields()
+ items Items for check
+
+ RETURN
+ TRUE Items can't be used in UPDATE
+ FALSE Items are OK
+*/
+
+static bool check_fields(List<Item> &items)
+{
+ List_iterator_fast<Item> it(items);
+ Item *item;
+ while ((item= it++))
+ {
+ if (item->type() != Item::FIELD_ITEM)
+ {
+ /* as far as item comes from VIEW select list it has name */
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
int mysql_update(THD *thd,
TABLE_LIST *table_list,
List<Item> &fields,
@@ -71,8 +100,6 @@ int mysql_update(THD *thd,
TABLE *table;
SQL_SELECT *select;
READ_RECORD info;
- TABLE_LIST *update_table_list= ((TABLE_LIST*)
- thd->lex->select_lex.table_list.first);
DBUG_ENTER("mysql_update");
LINT_INIT(used_index);
@@ -89,10 +116,12 @@ int mysql_update(THD *thd,
table->quick_keys.clear_all();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- want_privilege= table->grant.want_privilege;
+ /* In case of view TABLE_LIST contain right privilages request */
+ want_privilege= (table_list->view ?
+ table_list->grant.want_privilege :
+ table->grant.want_privilege);
#endif
- if ((error= mysql_prepare_update(thd, table_list, update_table_list,
- &conds, order_num, order)))
+ if ((error= mysql_prepare_update(thd, table_list, &conds, order_num, order)))
DBUG_RETURN(error);
old_used_keys= table->used_keys; // Keys used in WHERE
@@ -108,10 +137,19 @@ int mysql_update(THD *thd,
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- table->grant.want_privilege=want_privilege;
+ table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
#endif
- if (setup_fields(thd, 0, update_table_list, fields, 1, 0, 0))
+ if (setup_fields(thd, 0, table_list, fields, 1, 0, 0))
DBUG_RETURN(-1); /* purecov: inspected */
+ if (check_fields(fields))
+ {
+ DBUG_RETURN(-1);
+ }
+ if (!table_list->updatable || check_key_in_view(thd, table_list))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
+ DBUG_RETURN(-1);
+ }
if (table->timestamp_field)
{
// Don't set timestamp column if this is modified
@@ -123,9 +161,10 @@ int mysql_update(THD *thd,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check values */
- table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
+ table_list->grant.want_privilege= table->grant.want_privilege=
+ (SELECT_ACL & ~table->grant.privilege);
#endif
- if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
+ if (setup_fields(thd, 0, table_list, values, 0, 0, 0))
{
free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); /* purecov: inspected */
@@ -228,7 +267,7 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->reset())
goto err;
init_read_record(&info,thd,table,select,0,1);
-
+
thd->proc_info="Searching rows for update";
uint tmp_limit= limit;
@@ -407,8 +446,7 @@ err:
SYNOPSIS
mysql_prepare_update()
thd - thread handler
- table_list - global table list
- update_table_list - local table list of UPDATE SELECT_LEX
+ table_list - global/local table list
conds - conditions
order_num - number of ORDER BY list entries
order - ORDER BY clause list
@@ -419,7 +457,6 @@ err:
-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;
@@ -429,34 +466,30 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
DBUG_ENTER("mysql_prepare_update");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
+ table_list->grant.want_privilege= 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) ||
+ if (setup_tables(thd, table_list, conds) ||
+ setup_conds(thd, table_list, conds) ||
select_lex->setup_ref_array(thd, order_num) ||
setup_order(thd, select_lex->ref_pointer_array,
- update_table_list, all_fields, all_fields, order) ||
+ table_list, all_fields, all_fields, order) ||
setup_ftfuncs(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,
+ if (find_real_table_in_list(table_list->next_global,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
- if (thd->current_arena && select_lex->first_execution)
- {
- select_lex->prep_where= select_lex->where;
- select_lex->first_execution= 0;
- }
-
+ select_lex->fix_prepare_information(thd, conds);
DBUG_RETURN(0);
}
@@ -469,32 +502,31 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
Setup multi-update handling and call SELECT to do the join
*/
-int mysql_multi_update(THD *thd,
- TABLE_LIST *table_list,
- List<Item> *fields,
- List<Item> *values,
- COND *conds,
- ulong options,
- enum enum_duplicates handle_duplicates,
- SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
-{
- int res;
- multi_update *result;
- TABLE_LIST *tl;
- TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
- table_map item_tables= 0, derived_tables= 0;
- DBUG_ENTER("mysql_multi_update");
+/*
+ make update specific preparation and checks after opening tables
- if ((res=open_and_lock_tables(thd,table_list)))
- DBUG_RETURN(res);
+ SYNOPSIS
+ mysql_multi_update_prepare()
+ thd thread handler
- select_lex->select_limit= HA_POS_ERROR;
+ RETURN
+ 0 OK
+ -1 Error
+*/
+int mysql_multi_update_prepare(THD *thd)
+{
+ LEX *lex= thd->lex;
+ TABLE_LIST *table_list= lex->query_tables;
+ List<Item> *fields= &lex->select_lex.item_list;
+ TABLE_LIST *tl;
+ table_map tables_for_update= 0, readonly_tables= 0;
+ DBUG_ENTER("mysql_multi_update_prepare");
/*
Ensure that we have update privilege for all tables and columns in the
SET part
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (tl= table_list; tl; tl= tl->next_local)
{
TABLE *table= tl->table;
/*
@@ -504,71 +536,93 @@ int mysql_multi_update(THD *thd,
"Target table ... is not updatable"
*/
if (!tl->derived)
- table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
+ tl->grant.want_privilege= table->grant.want_privilege=
+ (UPDATE_ACL & ~table->grant.privilege);
}
- if (thd->lex->derived_tables)
+ /*
+ setup_tables() need for VIEWs. JOIN::prepare() will not do it second
+ time.
+ */
+ if (setup_tables(thd, table_list, &lex->select_lex.where) ||
+ setup_fields(thd, 0, table_list, *fields, 1, 0, 0))
+ DBUG_RETURN(-1);
+ if (check_fields(*fields))
{
- // Assign table map values to check updatability of derived tables
- uint tablenr=0;
- for (TABLE_LIST *table_list= update_list;
- table_list;
- table_list= table_list->next, tablenr++)
- {
- table_list->table->map= (table_map) 1 << tablenr;
- }
- }
- if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
DBUG_RETURN(-1);
- if (thd->lex->derived_tables)
+ }
+
{
// Find tables used in items
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
{
- item_tables|= item->used_tables();
+ tables_for_update|= item->used_tables();
}
}
/*
Count tables and setup timestamp handling
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (tl= table_list; tl ; tl= tl->next_local)
{
TABLE *table= tl->table;
/* We only need SELECT privilege for columns in the values list */
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
+ tl->grant.want_privilege= table->grant.want_privilege=
+ (SELECT_ACL & ~table->grant.privilege);
// Only set timestamp column if this is not modified
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_on_update_now= 0;
-
- if (tl->derived)
- derived_tables|= table->map;
+
+ if (!tl->updatable || check_key_in_view(thd, tl))
+ readonly_tables|= table->map;
}
- if (thd->lex->derived_tables && (item_tables & derived_tables))
+ if (tables_for_update & readonly_tables)
{
- // find derived table which cause error
- for (tl= update_list; tl; tl= tl->next)
+ // find readonly table/view which cause error
+ for (tl= table_list; tl ; tl= tl->next_local)
{
- if (tl->derived && (item_tables & tl->table->map))
+ if ((readonly_tables & tl->table->map) &&
+ (tables_for_update & tl->table->map))
{
- my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
- MYF(0), tl->alias, "UPDATE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE");
DBUG_RETURN(-1);
}
}
}
+ DBUG_RETURN (0);
+}
+
+
+int mysql_multi_update(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> *fields,
+ List<Item> *values,
+ COND *conds,
+ ulong options,
+ enum enum_duplicates handle_duplicates,
+ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
+{
+ int res;
+ multi_update *result;
+ DBUG_ENTER("mysql_multi_update");
+
+ if ((res= open_and_lock_tables(thd, table_list)))
+ DBUG_RETURN(res);
+
+ if ((res= mysql_multi_update_prepare(thd)))
+ DBUG_RETURN(res);
- if (!(result=new multi_update(thd, update_list, fields, values,
- handle_duplicates)))
+ if (!(result= new multi_update(thd, table_list, fields, values,
+ handle_duplicates)))
DBUG_RETURN(-1);
List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
- select_lex->get_table_list(), select_lex->with_wild,
+ table_list, select_lex->with_wild,
total_list,
conds, 0, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL,
(ORDER *)NULL,
@@ -633,7 +687,7 @@ int multi_update::prepare(List<Item> &not_used_values,
*/
update.empty();
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (tables_to_update & table->map)
@@ -642,7 +696,7 @@ int multi_update::prepare(List<Item> &not_used_values,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
- update.link_in_list((byte*) tl, (byte**) &tl->next);
+ update.link_in_list((byte*) tl, (byte**) &tl->next_local);
tl->shared= table_count++;
table->no_keyread=1;
table->used_keys.clear_all();
@@ -702,7 +756,7 @@ int multi_update::prepare(List<Item> &not_used_values,
which will cause an error when reading a row.
(This issue is mostly relevent for MyISAM tables)
*/
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (!(tables_to_update & table->map) &&
@@ -737,7 +791,7 @@ multi_update::initialize_tables(JOIN *join)
table_to_update= 0;
/* Create a temporary table for keys to all tables, except main table */
- for (table_ref= update_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
uint cnt= table_ref->shared;
@@ -849,7 +903,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
multi_update::~multi_update()
{
TABLE_LIST *table;
- for (table= update_tables ; table; table= table->next)
+ for (table= update_tables ; table; table= table->next_local)
table->table->no_keyread= table->table->no_cache= 0;
if (tmp_tables)
@@ -876,7 +930,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
TABLE *table= cur_table->table;
/*
@@ -990,7 +1044,7 @@ int multi_update::do_updates(bool from_send_error)
do_update= 0; // Don't retry this function
if (!found)
DBUG_RETURN(0);
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
byte *ref_pos;