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,
tbl->alias, &my_charset_bin);
if (!field)
DBUG_RETURN(1);
field->init(tbl);
/* /*
The field will be converted to varstring when creating tmp table if The field will be converted to varstring when creating tmp table if
table to be updated was created by mysql 4.1. Deny this. table to be updated was created by mysql 4.1. Deny this.
*/ */
offset.can_alter_field_type= 0; field->can_alter_field_type= 0;
if (!(ifield= new Item_field(((Field *) &offset)))) Item_field *ifield= new Item_field((Field *) field);
if (!ifield)
DBUG_RETURN(1); DBUG_RETURN(1);
ifield->maybe_null= 0; ifield->maybe_null= 0;
if (temp_fields.push_front(ifield)) if (temp_fields.push_back(ifield))
DBUG_RETURN(1); 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)))
/* 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; 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