Commit 89a4d89c authored by gshchepa/uchum@gleb.loc's avatar gshchepa/uchum@gleb.loc

Merge gleb.loc:/home/uchum/work/bk/mysql-5.0-opt

into  gleb.loc:/home/uchum/work/bk/mysql-5.1-opt
parents 1fea1536 7f63aab2
...@@ -3369,6 +3369,23 @@ SHOW CREATE VIEW v1; ...@@ -3369,6 +3369,23 @@ SHOW CREATE VIEW v1;
View Create View View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select cast(1.23456789 as decimal(8,0)) AS `col` v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select cast(1.23456789 as decimal(8,0)) AS `col`
DROP VIEW v1; DROP VIEW v1;
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT, c INT DEFAULT 0);
INSERT INTO t1 (a) VALUES (1), (2);
INSERT INTO t2 (b) VALUES (1), (2);
CREATE VIEW v1 AS SELECT t2.b,t2.c FROM t1, t2
WHERE t1.a=t2.b AND t2.b < 3 WITH CHECK OPTION;
SELECT * FROM v1;
b c
1 0
2 0
UPDATE v1 SET c=1 WHERE b=1;
SELECT * FROM v1;
b c
1 1
2 0
DROP VIEW v1;
DROP TABLE t1,t2;
End of 5.0 tests. End of 5.0 tests.
DROP DATABASE IF EXISTS `d-1`; DROP DATABASE IF EXISTS `d-1`;
CREATE DATABASE `d-1`; CREATE DATABASE `d-1`;
......
...@@ -3054,7 +3054,6 @@ SELECT code, COUNT(DISTINCT country) FROM v1 GROUP BY code ORDER BY MAX(id); ...@@ -3054,7 +3054,6 @@ SELECT code, COUNT(DISTINCT country) FROM v1 GROUP BY code ORDER BY MAX(id);
DROP VIEW v1; DROP VIEW v1;
DROP TABLE t1; DROP TABLE t1;
#
# #
# Bug #28561: update on multi-table view with CHECK OPTION and # Bug #28561: update on multi-table view with CHECK OPTION and
# a subquery in WHERE condition # a subquery in WHERE condition
...@@ -3244,6 +3243,22 @@ CREATE VIEW v1 AS SELECT CAST(1.23456789 AS DECIMAL(8,0)) AS col; ...@@ -3244,6 +3243,22 @@ CREATE VIEW v1 AS SELECT CAST(1.23456789 AS DECIMAL(8,0)) AS col;
SHOW CREATE VIEW v1; SHOW CREATE VIEW v1;
DROP VIEW v1; DROP VIEW v1;
#
# Bug #28716: CHECK OPTION expression is evaluated over expired record buffers
# when VIEW is updated via temporary tables
#
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT, c INT DEFAULT 0);
INSERT INTO t1 (a) VALUES (1), (2);
INSERT INTO t2 (b) VALUES (1), (2);
CREATE VIEW v1 AS SELECT t2.b,t2.c FROM t1, t2
WHERE t1.a=t2.b AND t2.b < 3 WITH CHECK OPTION;
SELECT * FROM v1;
UPDATE v1 SET c=1 WHERE b=1;
SELECT * FROM v1;
DROP VIEW v1;
DROP TABLE t1,t2;
--echo End of 5.0 tests. --echo End of 5.0 tests.
# #
......
...@@ -2273,6 +2273,11 @@ class multi_update :public select_result_interceptor ...@@ -2273,6 +2273,11 @@ class multi_update :public select_result_interceptor
List <Item> *fields, *values; List <Item> *fields, *values;
List <Item> **fields_for_table, **values_for_table; List <Item> **fields_for_table, **values_for_table;
uint table_count; uint table_count;
/*
List of tables referenced in the CHECK OPTION condition of
the updated view excluding the updated table.
*/
List <TABLE> unupdated_check_opt_tables;
Copy_field *copy_field; Copy_field *copy_field;
enum enum_duplicates handle_duplicates; enum enum_duplicates handle_duplicates;
bool do_update, trans_safe; bool do_update, trans_safe;
......
...@@ -1093,6 +1093,7 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1093,6 +1093,7 @@ int multi_update::prepare(List<Item> &not_used_values,
List_iterator_fast<Item> field_it(*fields); List_iterator_fast<Item> field_it(*fields);
List_iterator_fast<Item> value_it(*values); List_iterator_fast<Item> value_it(*values);
uint i, max_fields; uint i, max_fields;
uint leaf_table_count= 0;
DBUG_ENTER("multi_update::prepare"); DBUG_ENTER("multi_update::prepare");
thd->count_cuted_fields= CHECK_FIELD_WARN; thd->count_cuted_fields= CHECK_FIELD_WARN;
...@@ -1126,6 +1127,7 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1126,6 +1127,7 @@ int multi_update::prepare(List<Item> &not_used_values,
{ {
/* TODO: add support of view of join support */ /* TODO: add support of view of join support */
TABLE *table=table_ref->table; TABLE *table=table_ref->table;
leaf_table_count++;
if (tables_to_update & table->map) if (tables_to_update & table->map)
{ {
TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref, TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref,
...@@ -1187,7 +1189,7 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1187,7 +1189,7 @@ int multi_update::prepare(List<Item> &not_used_values,
/* Allocate copy fields */ /* Allocate copy fields */
max_fields=0; max_fields=0;
for (i=0 ; i < table_count ; i++) for (i=0 ; i < table_count ; i++)
set_if_bigger(max_fields, fields_for_table[i]->elements); set_if_bigger(max_fields, fields_for_table[i]->elements + leaf_table_count);
copy_field= new Copy_field[max_fields]; copy_field= new Copy_field[max_fields];
DBUG_RETURN(thd->is_fatal_error != 0); DBUG_RETURN(thd->is_fatal_error != 0);
} }
...@@ -1278,13 +1280,22 @@ multi_update::initialize_tables(JOIN *join) ...@@ -1278,13 +1280,22 @@ multi_update::initialize_tables(JOIN *join)
trans_safe= transactional_tables= main_table->file->has_transactions(); trans_safe= transactional_tables= main_table->file->has_transactions();
table_to_update= 0; table_to_update= 0;
/* Any update has at least one pair (field, value) */
DBUG_ASSERT(fields->elements);
/*
Only one table may be modified by UPDATE of an updatable view.
For an updatable view first_table_for_update indicates this
table.
For a regular multi-update it refers to some updated table.
*/
TABLE *first_table_for_update= ((Item_field *) fields->head())->field->table;
/* Create a temporary table for keys to all tables, except main table */ /* Create a temporary table for keys to all tables, except main table */
for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local) for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
{ {
TABLE *table=table_ref->table; TABLE *table=table_ref->table;
uint cnt= table_ref->shared; uint cnt= table_ref->shared;
Item_field *ifield; List<Item> temp_fields;
List<Item> temp_fields= *fields_for_table[cnt];
ORDER group; ORDER group;
TMP_TABLE_PARAM *tmp_param; TMP_TABLE_PARAM *tmp_param;
...@@ -1301,27 +1312,56 @@ multi_update::initialize_tables(JOIN *join) ...@@ -1301,27 +1312,56 @@ multi_update::initialize_tables(JOIN *join)
} }
table->prepare_for_position(); table->prepare_for_position();
if (table == first_table_for_update && table_ref->check_option)
{
table_map unupdated_tables= table_ref->check_option->used_tables() &
~first_table_for_update->map;
for (TABLE_LIST *tbl_ref =leaves;
unupdated_tables && tbl_ref;
tbl_ref= tbl_ref->next_leaf)
{
if (unupdated_tables & tbl_ref->table->map)
unupdated_tables&= ~tbl_ref->table->map;
else
continue;
if (unupdated_check_opt_tables.push_back(tbl_ref->table))
DBUG_RETURN(1);
}
}
tmp_param= tmp_table_param+cnt; tmp_param= tmp_table_param+cnt;
/* /*
Create a temporary table to store all fields that are changed for this Create a temporary table to store all fields that are changed for this
table. The first field in the temporary table is a pointer to the table. The first field in the temporary table is a pointer to the
original row so that we can find and update it original row so that we can find and update it. For the updatable
VIEW a few following fields are rowids of tables used in the CHECK
OPTION condition.
*/ */
/* ok to be on stack as this is not referenced outside of this func */ List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables);
Field_string offset(table->file->ref_length, 0, "offset", TABLE *tbl= table;
&my_charset_bin); do
offset.init(table); {
/* Field_string *field= new Field_string(tbl->file->ref_length, 0,
The field will be converted to varstring when creating tmp table if tbl->alias, &my_charset_bin);
table to be updated was created by mysql 4.1. Deny this. if (!field)
*/ DBUG_RETURN(1);
offset.can_alter_field_type= 0; field->init(tbl);
if (!(ifield= new Item_field(((Field *) &offset)))) /*
DBUG_RETURN(1); The field will be converted to varstring when creating tmp table if
ifield->maybe_null= 0; table to be updated was created by mysql 4.1. Deny this.
if (temp_fields.push_front(ifield)) */
DBUG_RETURN(1); field->can_alter_field_type= 0;
Item_field *ifield= new Item_field((Field *) field);
if (!ifield)
DBUG_RETURN(1);
ifield->maybe_null= 0;
if (temp_fields.push_back(ifield))
DBUG_RETURN(1);
} while ((tbl= tbl_it++));
temp_fields.concat(fields_for_table[cnt]);
/* Make an unique key over the first field to avoid duplicated updates */ /* Make an unique key over the first field to avoid duplicated updates */
bzero((char*) &group, sizeof(group)); bzero((char*) &group, sizeof(group));
...@@ -1473,11 +1513,26 @@ bool multi_update::send_data(List<Item> &not_used_values) ...@@ -1473,11 +1513,26 @@ bool multi_update::send_data(List<Item> &not_used_values)
{ {
int error; int error;
TABLE *tmp_table= tmp_tables[offset]; TABLE *tmp_table= tmp_tables[offset];
table->file->position(table->record[0]); /*
fill_record(thd, tmp_table->field+1, *values_for_table[offset], 1); For updatable VIEW store rowid of the updated table and
/* Store pointer to row */ rowids of tables used in the CHECK OPTION condition.
memcpy((char*) tmp_table->field[0]->ptr, */
(char*) table->file->ref, table->file->ref_length); uint field_num= 0;
List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables);
TABLE *tbl= table;
do
{
tbl->file->position(tbl->record[0]);
memcpy((char*) tmp_table->field[field_num]->ptr,
(char*) tbl->file->ref, tbl->file->ref_length);
field_num++;
} while ((tbl= tbl_it++));
/* Store regular updated fields in the row. */
fill_record(thd,
tmp_table->field + 1 + unupdated_check_opt_tables.elements,
*values_for_table[offset], 1);
/* Write row, ignoring duplicated updates to a row */ /* Write row, ignoring duplicated updates to a row */
error= tmp_table->file->ha_write_row(tmp_table->record[0]); error= tmp_table->file->ha_write_row(tmp_table->record[0]);
if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE) if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)
...@@ -1527,9 +1582,10 @@ void multi_update::send_error(uint errcode,const char *err) ...@@ -1527,9 +1582,10 @@ void multi_update::send_error(uint errcode,const char *err)
int multi_update::do_updates(bool from_send_error) int multi_update::do_updates(bool from_send_error)
{ {
TABLE_LIST *cur_table; TABLE_LIST *cur_table;
int local_error; int local_error= 0;
ha_rows org_updated; ha_rows org_updated;
TABLE *table, *tmp_table; TABLE *table, *tmp_table;
List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables);
DBUG_ENTER("do_updates"); DBUG_ENTER("do_updates");
do_update= 0; // Don't retry this function do_update= 0; // Don't retry this function
...@@ -1537,8 +1593,8 @@ int multi_update::do_updates(bool from_send_error) ...@@ -1537,8 +1593,8 @@ int multi_update::do_updates(bool from_send_error)
DBUG_RETURN(0); DBUG_RETURN(0);
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{ {
byte *ref_pos;
bool can_compare_record; bool can_compare_record;
uint offset= cur_table->shared;
table = cur_table->table; table = cur_table->table;
if (table == table_to_update) if (table == table_to_update)
...@@ -1549,11 +1605,20 @@ int multi_update::do_updates(bool from_send_error) ...@@ -1549,11 +1605,20 @@ int multi_update::do_updates(bool from_send_error)
(void) table->file->ha_rnd_init(0); (void) table->file->ha_rnd_init(0);
table->file->extra(HA_EXTRA_NO_CACHE); table->file->extra(HA_EXTRA_NO_CACHE);
check_opt_it.rewind();
while(TABLE *tbl= check_opt_it++)
{
if (tbl->file->ha_rnd_init(1))
goto err;
tbl->file->extra(HA_EXTRA_CACHE);
}
/* /*
Setup copy functions to copy fields from temporary table Setup copy functions to copy fields from temporary table
*/ */
List_iterator_fast<Item> field_it(*fields_for_table[cur_table->shared]); List_iterator_fast<Item> field_it(*fields_for_table[offset]);
Field **field= tmp_table->field+1; // Skip row pointer Field **field= tmp_table->field +
1 + unupdated_check_opt_tables.elements; // Skip row pointers
Copy_field *copy_field_ptr= copy_field, *copy_field_end; Copy_field *copy_field_ptr= copy_field, *copy_field_end;
for ( ; *field ; field++) for ( ; *field ; field++)
{ {
...@@ -1570,7 +1635,6 @@ int multi_update::do_updates(bool from_send_error) ...@@ -1570,7 +1635,6 @@ int multi_update::do_updates(bool from_send_error)
bitmap_is_subset(table->write_set, bitmap_is_subset(table->write_set,
table->read_set)); table->read_set));
ref_pos= (byte*) tmp_table->field[0]->ptr;
for (;;) for (;;)
{ {
if (thd->killed && trans_safe) if (thd->killed && trans_safe)
...@@ -1583,8 +1647,20 @@ int multi_update::do_updates(bool from_send_error) ...@@ -1583,8 +1647,20 @@ int multi_update::do_updates(bool from_send_error)
continue; // May happen on dup key continue; // May happen on dup key
goto err; goto err;
} }
if ((local_error= table->file->rnd_pos(table->record[0], ref_pos)))
goto err; /* call rnd_pos() using rowids from temporary table */
check_opt_it.rewind();
TABLE *tbl= table;
uint field_num= 0;
do
{
if((local_error=
tbl->file->rnd_pos(tbl->record[0],
(byte *) tmp_table->field[field_num]->ptr)))
goto err;
field_num++;
} while((tbl= check_opt_it++));
table->status|= STATUS_UPDATED; table->status|= STATUS_UPDATED;
store_record(table,record[1]); store_record(table,record[1]);
...@@ -1635,6 +1711,10 @@ int multi_update::do_updates(bool from_send_error) ...@@ -1635,6 +1711,10 @@ int multi_update::do_updates(bool from_send_error)
} }
(void) table->file->ha_rnd_end(); (void) table->file->ha_rnd_end();
(void) tmp_table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end();
check_opt_it.rewind();
while (TABLE *tbl= check_opt_it++)
tbl->file->ha_rnd_end();
} }
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -1648,6 +1728,9 @@ err: ...@@ -1648,6 +1728,9 @@ err:
err2: err2:
(void) table->file->ha_rnd_end(); (void) table->file->ha_rnd_end();
(void) tmp_table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end();
check_opt_it.rewind();
while (TABLE *tbl= check_opt_it++)
tbl->file->ha_rnd_end();
if (updated != org_updated) if (updated != org_updated)
{ {
......
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