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

support of join view updateability (WL#1809)

parent f797298f
...@@ -380,4 +380,7 @@ ...@@ -380,4 +380,7 @@
#define ER_TRG_ON_VIEW_OR_TEMP_TABLE 1361 #define ER_TRG_ON_VIEW_OR_TEMP_TABLE 1361
#define ER_TRG_CANT_CHANGE_ROW 1362 #define ER_TRG_CANT_CHANGE_ROW 1362
#define ER_TRG_NO_SUCH_ROW_IN_TRG 1363 #define ER_TRG_NO_SUCH_ROW_IN_TRG 1363
#define ER_ERROR_MESSAGES 364 #define ER_VIEW_MULTIUPDATE 1364
#define ER_VIEW_NO_INSERT_FIELD_LIST 1365
#define ER_VIEW_DELETE_MERGE_VIEW 1366
#define ER_ERROR_MESSAGES 367
...@@ -1359,3 +1359,152 @@ a a b ...@@ -1359,3 +1359,152 @@ a a b
deallocate prepare stmt1; deallocate prepare stmt1;
drop view v4,v3,v2,v1; drop view v4,v3,v2,v1;
drop tables t1,t2,t3; drop tables t1,t2,t3;
create table t1 (a int, primary key (a), b int);
create table t2 (a int, primary key (a));
insert into t1 values (1,100), (2,200);
insert into t2 values (1), (3);
create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
update v3 set a= 10 where a=1;
select * from t1;
a b
10 100
2 200
select * from t2;
a
1
3
create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
update v2 set a= 10 where a=200;
ERROR HY000: The target table v2 of the UPDATE is not updatable
select * from v3;
a b
2 1
10 1
2 3
10 3
select * from v2;
a b
100 1
200 1
100 3
200 3
set @a= 10;
set @b= 100;
prepare stmt1 from "update v3 set a= ? where a=?";
execute stmt1 using @a,@b;
select * from v3;
a b
2 1
10 1
2 3
10 3
set @a= 300;
set @b= 10;
execute stmt1 using @a,@b;
select * from v3;
a b
2 1
300 1
2 3
300 3
deallocate prepare stmt1;
drop view v3,v2;
drop tables t1,t2;
create table t1 (a int, primary key (a), b int);
create table t2 (a int, primary key (a), b int);
insert into t2 values (1000, 2000);
create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
insert into v3 values (1,2);
ERROR HY000: Can not insert into join view 'test.v3' without fields list
insert into v3 select * from t2;
ERROR HY000: Can not insert into join view 'test.v3' without fields list
insert into v3(a,b) values (1,2);
ERROR HY000: Can not modify more than one base table through a join view 'test.v3'
insert into v3(a,b) select * from t2;
ERROR HY000: Can not modify more than one base table through a join view 'test.v3'
insert into v3(a) values (1);
insert into v3(b) values (10);
insert into v3(a) select a from t2;
insert into v3(b) select b from t2;
Warnings:
Warning 1263 Data truncated; NULL supplied to NOT NULL column 'a' at row 2
insert into v3(a) values (1) on duplicate key update a=a+10000+VALUES(a);
select * from t1;
a b
10002 NULL
10 NULL
1000 NULL
select * from t2;
a b
1000 2000
10 NULL
2000 NULL
0 NULL
create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
insert into v2(a) values (10);
ERROR HY000: The target table v2 of the INSERT is not updatable
select * from v3;
a b
10 1000
1000 1000
10002 1000
10 10
1000 10
10002 10
10 2000
1000 2000
10002 2000
10 0
1000 0
10002 0
select * from v2;
a b
NULL 1000
NULL 1000
NULL 1000
NULL 10
NULL 10
NULL 10
NULL 2000
NULL 2000
NULL 2000
NULL 0
NULL 0
NULL 0
delete from v3;
ERROR HY000: Can not delete from join view 'test.v3'
delete v3,t1 from v3,t1;
ERROR HY000: Can not delete from join view 'test.v3'
delete from t1;
prepare stmt1 from "insert into v3(a) values (?);";
set @a= 100;
execute stmt1 using @a;
set @a= 300;
execute stmt1 using @a;
deallocate prepare stmt1;
prepare stmt1 from "insert into v3(a) select ?;";
set @a= 101;
execute stmt1 using @a;
set @a= 301;
execute stmt1 using @a;
deallocate prepare stmt1;
select * from v3;
a b
100 1000
101 1000
300 1000
301 1000
100 10
101 10
300 10
301 10
100 2000
101 2000
300 2000
301 2000
100 0
101 0
300 0
301 0
drop view v3,v2;
drop tables t1,t2;
...@@ -1298,3 +1298,93 @@ execute stmt1; ...@@ -1298,3 +1298,93 @@ execute stmt1;
deallocate prepare stmt1; deallocate prepare stmt1;
drop view v4,v3,v2,v1; drop view v4,v3,v2,v1;
drop tables t1,t2,t3; drop tables t1,t2,t3;
#
# updating of join view
#
create table t1 (a int, primary key (a), b int);
create table t2 (a int, primary key (a));
insert into t1 values (1,100), (2,200);
insert into t2 values (1), (3);
# legal view for update
create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
update v3 set a= 10 where a=1;
select * from t1;
select * from t2;
# view without primary key
create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
-- error 1288
update v2 set a= 10 where a=200;
# just view selects
select * from v3;
select * from v2;
# prepare statement with updating join view
set @a= 10;
set @b= 100;
prepare stmt1 from "update v3 set a= ? where a=?";
execute stmt1 using @a,@b;
select * from v3;
set @a= 300;
set @b= 10;
execute stmt1 using @a,@b;
select * from v3;
deallocate prepare stmt1;
drop view v3,v2;
drop tables t1,t2;
#
# inserting/deleting join view
#
create table t1 (a int, primary key (a), b int);
create table t2 (a int, primary key (a), b int);
insert into t2 values (1000, 2000);
create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
# inserting into join view without field list
-- error 1365
insert into v3 values (1,2);
-- error 1365
insert into v3 select * from t2;
# inserting in several tables of join view
-- error 1364
insert into v3(a,b) values (1,2);
-- error 1364
insert into v3(a,b) select * from t2;
# correct inserts into join view
insert into v3(a) values (1);
insert into v3(b) values (10);
insert into v3(a) select a from t2;
insert into v3(b) select b from t2;
insert into v3(a) values (1) on duplicate key update a=a+10000+VALUES(a);
select * from t1;
select * from t2;
# view without primary key
create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
-- error 1288
insert into v2(a) values (10);
# just view selects
select * from v3;
select * from v2;
# try delete from join view
-- error 1366
delete from v3;
-- error 1366
delete v3,t1 from v3,t1;
# delete from t1 just to reduce result set size
delete from t1;
# prepare statement with insert join view
prepare stmt1 from "insert into v3(a) values (?);";
set @a= 100;
execute stmt1 using @a;
set @a= 300;
execute stmt1 using @a;
deallocate prepare stmt1;
prepare stmt1 from "insert into v3(a) select ?;";
set @a= 101;
execute stmt1 using @a;
set @a= 301;
execute stmt1 using @a;
deallocate prepare stmt1;
select * from v3;
drop view v3,v2;
drop tables t1,t2;
...@@ -611,7 +611,8 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list, ...@@ -611,7 +611,8 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list,
List<Item> *fields, List<Item> *values, List<Item> *fields, List<Item> *values,
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,
bool converted);
int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
List<Item> &fields, List_item *values, List<Item> &fields, List_item *values,
List<Item> &update_fields, List<Item> &update_fields,
......
...@@ -392,3 +392,6 @@ character-set=latin2 ...@@ -392,3 +392,6 @@ character-set=latin2
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -383,3 +383,6 @@ character-set=latin1 ...@@ -383,3 +383,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -392,3 +392,6 @@ character-set=latin1 ...@@ -392,3 +392,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=latin1 ...@@ -380,3 +380,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -385,3 +385,6 @@ character-set=latin7 ...@@ -385,3 +385,6 @@ character-set=latin7
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=latin1 ...@@ -380,3 +380,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -393,3 +393,6 @@ character-set=latin1 ...@@ -393,3 +393,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=greek ...@@ -380,3 +380,6 @@ character-set=greek
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -385,3 +385,6 @@ character-set=latin2 ...@@ -385,3 +385,6 @@ character-set=latin2
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=latin1 ...@@ -380,3 +380,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -384,3 +384,6 @@ character-set=ujis ...@@ -384,3 +384,6 @@ character-set=ujis
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=euckr ...@@ -380,3 +380,6 @@ character-set=euckr
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -382,3 +382,6 @@ character-set=latin1 ...@@ -382,3 +382,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -382,3 +382,6 @@ character-set=latin1 ...@@ -382,3 +382,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -385,3 +385,6 @@ character-set=latin2 ...@@ -385,3 +385,6 @@ character-set=latin2
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -382,3 +382,6 @@ character-set=latin1 ...@@ -382,3 +382,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -385,3 +385,6 @@ character-set=latin2 ...@@ -385,3 +385,6 @@ character-set=latin2
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -376,7 +376,7 @@ character-set=koi8r ...@@ -376,7 +376,7 @@ character-set=koi8r
"View SELECT '%-.64s'" "View SELECT '%-.64s'"
"View SELECT view " "View SELECT view "
" view ( )" " view ( )"
" view ()" " view () ()"
"View '%-.64s.%-.64s' " "View '%-.64s.%-.64s' "
"Can't drop a %s from within another stored routine" "Can't drop a %s from within another stored routine"
"GOTO is not allowed in a stored procedure handler" "GOTO is not allowed in a stored procedure handler"
...@@ -385,3 +385,6 @@ character-set=koi8r ...@@ -385,3 +385,6 @@ character-set=koi8r
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
" VIEW '%-.64s.%-.64s'"
" VIEW '%-.64s.%-.64s' "
" VIEW '%-.64s.%-.64s'"
...@@ -373,3 +373,6 @@ character-set=cp1250 ...@@ -373,3 +373,6 @@ character-set=cp1250
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -388,3 +388,6 @@ character-set=latin2 ...@@ -388,3 +388,6 @@ character-set=latin2
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -384,3 +384,6 @@ character-set=latin1 ...@@ -384,3 +384,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -380,3 +380,6 @@ character-set=latin1 ...@@ -380,3 +380,6 @@ character-set=latin1
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
"Can not insert into join view '%-.64s.%-.64s' without fields list"
"Can not delete from join view '%-.64s.%-.64s'"
...@@ -386,3 +386,6 @@ character-set=koi8u ...@@ -386,3 +386,6 @@ character-set=koi8u
"Trigger's '%-.64s' is view or temporary table" "Trigger's '%-.64s' is view or temporary table"
"Updating of %s row is not allowed in %strigger" "Updating of %s row is not allowed in %strigger"
"There is no %s row in %s trigger" "There is no %s row in %s trigger"
" ¦ VIEW '%-.64s.%-.64s', ͦԦ ˦ "
" VIEW '%-.64s.%-.64s', ͦ ˦ , æ"
" VIEW '%-.64s.%-.64s', ͦ ˦ "
...@@ -2296,7 +2296,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2296,7 +2296,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
bool allow_rowid= tables && !tables->next_local; // Only one table bool allow_rowid= tables && !tables->next_local; // Only one table
for (; tables ; tables= tables->next_local) for (; tables ; tables= tables->next_local)
{ {
if (!tables->table) if (!tables->table && !tables->ancestor)
{ {
if (report_error) if (report_error)
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
......
...@@ -1504,7 +1504,9 @@ public: ...@@ -1504,7 +1504,9 @@ public:
class multi_update :public select_result class multi_update :public select_result
{ {
TABLE_LIST *all_tables, *update_tables, *table_being_updated; TABLE_LIST *all_tables; /* query/update command tables */
TABLE_LIST *leaves; /* list of leves of join table tree */
TABLE_LIST *update_tables, *table_being_updated;
THD *thd; THD *thd;
TABLE **tmp_tables, *main_table, *table_to_update; TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param; TMP_TABLE_PARAM *tmp_table_param;
...@@ -1517,8 +1519,9 @@ class multi_update :public select_result ...@@ -1517,8 +1519,9 @@ class multi_update :public select_result
bool do_update, trans_safe, transactional_tables, log_delayed; bool do_update, trans_safe, transactional_tables, log_delayed;
public: public:
multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> *fields, multi_update(THD *thd_arg, TABLE_LIST *ut, TABLE_LIST *leaves_list,
List<Item> *values, enum_duplicates handle_duplicates); List<Item> *fields, List<Item> *values,
enum_duplicates handle_duplicates);
~multi_update(); ~multi_update();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_fields(List<Item> &list, uint flags) { return 0; }
......
...@@ -43,7 +43,14 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ...@@ -43,7 +43,14 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
if ((error= open_and_lock_tables(thd, table_list))) if ((error= open_and_lock_tables(thd, table_list)))
DBUG_RETURN(error); DBUG_RETURN(error);
table= table_list->table; if (!(table= table_list->table))
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(-1);
}
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;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "sql_acl.h" #include "sql_acl.h"
#include "sp_head.h" #include "sp_head.h"
#include "sql_trigger.h" #include "sql_trigger.h"
#include "sql_select.h"
static int check_null_fields(THD *thd,TABLE *entry); static int check_null_fields(THD *thd,TABLE *entry);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
...@@ -31,6 +32,7 @@ static void end_delayed_insert(THD *thd); ...@@ -31,6 +32,7 @@ static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg); extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table); static void unlink_blobs(register TABLE *table);
#endif #endif
static bool check_view_insertability(TABLE_LIST *view, ulong query_id);
/* Define to force use of my_malloc() if the allocated memory block is big */ /* Define to force use of my_malloc() if the allocated memory block is big */
...@@ -54,8 +56,22 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, ...@@ -54,8 +56,22 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
{ {
TABLE *table= table_list->table; TABLE *table= table_list->table;
if (!table_list->updatable)
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
return -1;
}
if (fields.elements == 0 && values.elements != 0) if (fields.elements == 0 && values.elements != 0)
{ {
if (!table)
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
table_list->view_db.str, table_list->view_name.str);
return -1;
}
if (values.elements != table->fields) if (values.elements != table->fields)
{ {
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
...@@ -97,6 +113,23 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, ...@@ -97,6 +113,23 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
thd->lex->select_lex.no_wrap_view_item= 0; thd->lex->select_lex.no_wrap_view_item= 0;
if (res) if (res)
return -1; return -1;
if (table == 0)
{
/* it is join view => we need to find table for update */
List_iterator_fast<Item> it(fields);
Item *item;
TABLE_LIST *tbl= 0;
table_map map= 0;
while (item= it++)
map|= item->used_tables();
if (table_list->check_single_table(&tbl, map) || tbl == 0)
{
my_error(ER_VIEW_MULTIUPDATE, MYF(0),
table_list->view_db.str, table_list->view_name.str);
return -1;
}
table_list->table= table= tbl->table;
}
if (check_unique && thd->dupp_field) if (check_unique && thd->dupp_field)
{ {
...@@ -111,6 +144,15 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, ...@@ -111,6 +144,15 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
#endif #endif
if (check_key_in_view(thd, table_list) ||
(table_list->view &&
check_view_insertability(table_list, thd->query_id)))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
return -1;
}
return 0; return 0;
} }
...@@ -134,7 +176,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -134,7 +176,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
ulong counter = 1; ulong counter = 1;
ulonglong id; ulonglong id;
COPY_INFO info; COPY_INFO info;
TABLE *table; TABLE *table= 0;
List_iterator_fast<List_item> its(values_list); List_iterator_fast<List_item> its(values_list);
List_item *values; List_item *values;
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
...@@ -201,17 +243,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -201,17 +243,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
if (res || thd->is_fatal_error) if (res || thd->is_fatal_error)
DBUG_RETURN(-1); DBUG_RETURN(-1);
table= table_list->table;
thd->proc_info="init"; thd->proc_info="init";
thd->used_tables=0; thd->used_tables=0;
values= its++; values= its++;
if (duplic == DUP_UPDATE && !table->insert_values) if (duplic == DUP_UPDATE)
{ {
/* it should be allocated before Item::fix_fields() */ /* it should be allocated before Item::fix_fields() */
table->insert_values= if (table_list->set_insert_values(&thd->mem_root))
(byte *)alloc_root(&thd->mem_root, table->rec_buff_length);
if (!table->insert_values)
goto abort; goto abort;
} }
...@@ -219,6 +258,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -219,6 +258,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
update_fields, update_values, duplic)) update_fields, update_values, duplic))
goto abort; goto abort;
/* mysql_prepare_insert set table_list->table if it was not set */
table= table_list->table;
// is table which we are changing used somewhere in other parts of query // is table which we are changing used somewhere in other parts of query
value_count= values->elements; value_count= values->elements;
while ((values= its++)) while ((values= its++))
...@@ -437,7 +479,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -437,7 +479,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
::send_ok(thd, (ulong) thd->row_count_func, id, buff); ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
} }
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0; table_list->clear_insert_values();
DBUG_RETURN(0); DBUG_RETURN(0);
abort: abort:
...@@ -446,7 +488,7 @@ abort: ...@@ -446,7 +488,7 @@ abort:
end_delayed_insert(thd); end_delayed_insert(thd);
#endif #endif
free_underlaid_joins(thd, &thd->lex->select_lex); free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0; table_list->clear_insert_values();
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
...@@ -542,11 +584,12 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) ...@@ -542,11 +584,12 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
where Pointer to where clause where Pointer to where clause
RETURN RETURN
0 ok 0 ok
1 ERROR 1 ERROR and message sent to client
-1 ERROR but message is not sent to client
*/ */
static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, static int mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
List<Item> &fields, COND **where) List<Item> &fields, COND **where)
{ {
bool insert_into_view= (table_list->view != 0); bool insert_into_view= (table_list->view != 0);
...@@ -554,22 +597,22 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, ...@@ -554,22 +597,22 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
if (setup_tables(thd, table_list, where, &thd->lex->select_lex.leaf_tables, if (setup_tables(thd, table_list, where, &thd->lex->select_lex.leaf_tables,
0)) 0))
DBUG_RETURN(1); DBUG_RETURN(thd->net.report_error ? -1 : 1);
if (insert_into_view && !fields.elements) if (insert_into_view && !fields.elements)
{ {
thd->lex->empty_field_list_on_rset= 1; thd->lex->empty_field_list_on_rset= 1;
insert_view_fields(&fields, table_list); if (!table_list->table)
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(-1);
}
DBUG_RETURN(insert_view_fields(&fields, table_list));
} }
if (!table_list->updatable ||
check_key_in_view(thd, table_list) ||
(insert_into_view &&
check_view_insertability(table_list, thd->query_id)))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
DBUG_RETURN(1);
}
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -598,8 +641,9 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, ...@@ -598,8 +641,9 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
int res; int res;
DBUG_ENTER("mysql_prepare_insert"); DBUG_ENTER("mysql_prepare_insert");
if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds)) if ((res= mysql_prepare_insert_check_table(thd, table_list,
DBUG_RETURN(-1); fields, &unused_conds)))
DBUG_RETURN(res);
if (check_insert_fields(thd, table_list, fields, *values, 1, if (check_insert_fields(thd, table_list, fields, *values, 1,
!insert_into_view) || !insert_into_view) ||
...@@ -1607,21 +1651,43 @@ bool delayed_insert::handle_inserts(void) ...@@ -1607,21 +1651,43 @@ bool delayed_insert::handle_inserts(void)
RETURN RETURN
0 OK 0 OK
-1 Error 1 Error sent to client
-1 Error is not sent to client
*/ */
int mysql_insert_select_prepare(THD *thd) int mysql_insert_select_prepare(THD *thd)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
TABLE_LIST* first_select_table=
(TABLE_LIST*)lex->select_lex.table_list.first;
TABLE_LIST* first_select_leaf_table;
int res;
DBUG_ENTER("mysql_insert_select_prepare"); DBUG_ENTER("mysql_insert_select_prepare");
if (mysql_prepare_insert_check_table(thd, lex->query_tables, if ((res= mysql_prepare_insert_check_table(thd, lex->query_tables,
lex->field_list, lex->field_list,
&lex->select_lex.where)) &lex->select_lex.where)))
DBUG_RETURN(-1); DBUG_RETURN(res);
/* exclude first table from leaf tables list, because it belong to INSERT */ /*
setup was done in mysql_insert_select_prepare, but we have to mark
first local table
*/
if (first_select_table)
first_select_table->setup_is_done= 1;
/*
exclude first table from leaf tables list, because it belong to
INSERT
*/
DBUG_ASSERT(lex->select_lex.leaf_tables); DBUG_ASSERT(lex->select_lex.leaf_tables);
lex->leaf_tables_insert= lex->select_lex.leaf_tables; lex->leaf_tables_insert= lex->select_lex.leaf_tables;
lex->select_lex.leaf_tables= lex->select_lex.leaf_tables->next_leaf; /* skip all leaf tables belonged to view where we are insert */
for (first_select_leaf_table= lex->select_lex.leaf_tables->next_leaf;
first_select_leaf_table &&
first_select_leaf_table->belong_to_view &&
first_select_leaf_table->belong_to_view ==
lex->leaf_tables_insert->belong_to_view;
first_select_leaf_table= first_select_leaf_table->next_leaf)
{}
lex->select_lex.leaf_tables= first_select_leaf_table;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -1635,6 +1701,23 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) ...@@ -1635,6 +1701,23 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (check_insert_fields(thd, table_list, *fields, values, 1, if (check_insert_fields(thd, table_list, *fields, values, 1,
!insert_into_view)) !insert_into_view))
DBUG_RETURN(1); DBUG_RETURN(1);
/*
if it is INSERT into join view then check_insert_fields already found
real table for insert
*/
table= table_list->table;
/*
Is table which we are changing used somewhere in other parts of
query
*/
if (!(thd->lex->current_select->options & OPTION_BUFFER_RESULT) &&
unique_table(table_list, table_list->next_independent()))
{
/* Using same table for INSERT and SELECT */
thd->lex->current_select->options|= OPTION_BUFFER_RESULT;
thd->lex->current_select->join->select_options|= OPTION_BUFFER_RESULT;
}
restore_record(table,default_values); // Get empty record restore_record(table,default_values); // Get empty record
table->next_number_field=table->found_next_number_field; table->next_number_field=table->found_next_number_field;
......
...@@ -2747,18 +2747,29 @@ unsent_create_error: ...@@ -2747,18 +2747,29 @@ unsent_create_error:
lex->duplicates); lex->duplicates);
if (thd->net.report_error) if (thd->net.report_error)
res= -1; res= -1;
break; if (res != 2)
break;
case SQLCOM_UPDATE_MULTI: case SQLCOM_UPDATE_MULTI:
{ {
bool converted= 0;
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= multi_update_precheck(thd, all_tables))) if (res != 2)
break; {
if ((res= multi_update_precheck(thd, all_tables)))
break;
}
else
{
res= 0;
converted= 1;
}
res= mysql_multi_update(thd, all_tables, res= mysql_multi_update(thd, all_tables,
&select_lex->item_list, &select_lex->item_list,
&lex->value_list, &lex->value_list,
select_lex->where, select_lex->where,
select_lex->options, select_lex->options,
lex->duplicates, unit, select_lex); lex->duplicates, unit, select_lex,
converted);
break; break;
} }
case SQLCOM_REPLACE: case SQLCOM_REPLACE:
...@@ -2796,31 +2807,26 @@ unsent_create_error: ...@@ -2796,31 +2807,26 @@ unsent_create_error:
if (!(res= open_and_lock_tables(thd, all_tables))) if (!(res= open_and_lock_tables(thd, all_tables)))
{ {
/* /* Skip first table, which is the table we are inserting in */
Is table which we are changing used somewhere in other parts of lex->select_lex.table_list.first= (byte*)first_table->next_local;
query
*/ res= mysql_insert_select_prepare(thd);
if (unique_table(first_table, all_tables->next_independent())) if (!res && (result= new select_insert(first_table, first_table->table,
&lex->field_list,
lex->duplicates)))
{ {
/* Using same table for INSERT and SELECT */ TABLE_LIST *first_select_table;
select_lex->options |= OPTION_BUFFER_RESULT;
}
if ((res= mysql_insert_select_prepare(thd)))
break;
if ((result= new select_insert(first_table, first_table->table,
&lex->field_list, lex->duplicates)))
/* Skip first table, which is the table we are inserting in */
lex->select_lex.table_list.first= (byte*) first_table->next_local;
/* /*
insert/replace from SELECT give its SELECT_LEX for SELECT, insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT and item_list belong to SELECT
*/ */
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
res= handle_select(thd, lex, result); res= handle_select(thd, lex, result);
/* revert changes for SP */
lex->select_lex.table_list.first= (byte*) first_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
}
/* revert changes for SP */
lex->select_lex.table_list.first= (byte*) first_table;
if (thd->net.report_error) if (thd->net.report_error)
res= -1; res= -1;
} }
...@@ -2884,6 +2890,16 @@ unsent_create_error: ...@@ -2884,6 +2890,16 @@ unsent_create_error:
if ((res= open_and_lock_tables(thd, all_tables))) if ((res= open_and_lock_tables(thd, all_tables)))
break; break;
if (!first_table->table)
{
DBUG_ASSERT(first_table->view &&
first_table->ancestor && first_table->ancestor->next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
first_table->view_db.str, first_table->view_name.str);
res= -1;
break;
}
if ((res= mysql_multi_delete_prepare(thd))) if ((res= mysql_multi_delete_prepare(thd)))
break; break;
......
...@@ -959,6 +959,7 @@ error: ...@@ -959,6 +959,7 @@ error:
RETURN VALUE RETURN VALUE
0 success 0 success
2 convert to multi_update
1 error, sent to client 1 error, sent to client
-1 error, not sent to client -1 error, not sent to client
*/ */
...@@ -975,6 +976,15 @@ static int mysql_test_update(Prepared_statement *stmt, ...@@ -975,6 +976,15 @@ static int mysql_test_update(Prepared_statement *stmt,
if (!(res=open_and_lock_tables(thd, table_list))) if (!(res=open_and_lock_tables(thd, table_list)))
{ {
if (table_list->table == 0)
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
stmt->lex->sql_command= SQLCOM_UPDATE_MULTI;
DBUG_PRINT("info", ("Switch to multi-update (command replaced)"));
/* convert to multiupdate */
return 2;
}
if (!(res= mysql_prepare_update(thd, table_list, if (!(res= mysql_prepare_update(thd, table_list,
&select->where, &select->where,
select->order_list.elements, select->order_list.elements,
...@@ -1027,6 +1037,15 @@ static int mysql_test_delete(Prepared_statement *stmt, ...@@ -1027,6 +1037,15 @@ static int mysql_test_delete(Prepared_statement *stmt,
if (!(res=open_and_lock_tables(thd, table_list))) if (!(res=open_and_lock_tables(thd, table_list)))
{ {
if (!table_list->table)
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(-1);
}
res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where); res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
lex->unit.cleanup(); lex->unit.cleanup();
} }
...@@ -1219,7 +1238,10 @@ static int select_like_statement_test(Prepared_statement *stmt, ...@@ -1219,7 +1238,10 @@ static int select_like_statement_test(Prepared_statement *stmt,
LEX *lex= stmt->lex; LEX *lex= stmt->lex;
int res= 0; int res= 0;
if (tables && (res= open_and_lock_tables(thd, tables))) /* check that tables was not opened during conversion from usual update */
if (tables &&
(!tables->table && !tables->view) &&
(res= open_and_lock_tables(thd, tables)))
goto end; goto end;
if (specific_prepare && (res= (*specific_prepare)(thd))) if (specific_prepare && (res= (*specific_prepare)(thd)))
...@@ -1285,6 +1307,7 @@ static int mysql_test_create_table(Prepared_statement *stmt) ...@@ -1285,6 +1307,7 @@ static int mysql_test_create_table(Prepared_statement *stmt)
mysql_test_multiupdate() mysql_test_multiupdate()
stmt prepared statemen handler stmt prepared statemen handler
tables list of tables queries tables list of tables queries
converted converted to multi-update from usual update
RETURN VALUE RETURN VALUE
0 success 0 success
...@@ -1292,10 +1315,11 @@ static int mysql_test_create_table(Prepared_statement *stmt) ...@@ -1292,10 +1315,11 @@ static int mysql_test_create_table(Prepared_statement *stmt)
-1 error, not sent to client -1 error, not sent to client
*/ */
static int mysql_test_multiupdate(Prepared_statement *stmt, static int mysql_test_multiupdate(Prepared_statement *stmt,
TABLE_LIST *tables) TABLE_LIST *tables,
bool converted)
{ {
int res; int res;
if ((res= multi_update_precheck(stmt->thd, tables))) if (!converted && (res= multi_update_precheck(stmt->thd, tables)))
return res; return res;
return select_like_statement_test(stmt, tables, &mysql_multi_update_prepare); return select_like_statement_test(stmt, tables, &mysql_multi_update_prepare);
} }
...@@ -1325,7 +1349,19 @@ static int mysql_test_multidelete(Prepared_statement *stmt, ...@@ -1325,7 +1349,19 @@ static int mysql_test_multidelete(Prepared_statement *stmt,
uint fake_counter; 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, &mysql_multi_delete_prepare); if ((res= select_like_statement_test(stmt, tables,
&mysql_multi_delete_prepare)))
return res;
if (!tables->table)
{
DBUG_ASSERT(tables->view &&
tables->ancestor && tables->ancestor->next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
tables->view_db.str, tables->view_name.str);
return -1;
}
return 0;
} }
...@@ -1404,6 +1440,11 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) ...@@ -1404,6 +1440,11 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
res= mysql_test_update(stmt, tables); res= mysql_test_update(stmt, tables);
if (res != 2)
break;
case SQLCOM_UPDATE_MULTI:
res= mysql_test_multiupdate(stmt, tables, res == 2);
break; break;
case SQLCOM_DELETE: case SQLCOM_DELETE:
...@@ -1431,10 +1472,6 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) ...@@ -1431,10 +1472,6 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
case SQLCOM_DELETE_MULTI: case SQLCOM_DELETE_MULTI:
res= mysql_test_multidelete(stmt, tables); res= mysql_test_multidelete(stmt, tables);
break; break;
case SQLCOM_UPDATE_MULTI:
res= mysql_test_multiupdate(stmt, tables);
break;
case SQLCOM_INSERT_SELECT: case SQLCOM_INSERT_SELECT:
res= mysql_test_insert_select(stmt, tables); res= mysql_test_insert_select(stmt, tables);
......
...@@ -87,6 +87,28 @@ static bool check_fields(THD *thd, List<Item> &items) ...@@ -87,6 +87,28 @@ static bool check_fields(THD *thd, List<Item> &items)
} }
/*
Process usual UPDATE
SYNOPSIS
mysql_update()
thd thread handler
fields fields for update
values values of fields for update
conds WHERE clause expression
order_num number of elemen in ORDER BY clause
order ORDER BY clause list
limit limit clause
handle_duplicates how to handle duplicates
RETURN
0 - OK
2 - privilege check and openning table passed, but we need to convert to
multi-update because of view substitution
1 - error and error sent to client
-1 - error and error is not sent to client
*/
int mysql_update(THD *thd, int mysql_update(THD *thd,
TABLE_LIST *table_list, TABLE_LIST *table_list,
List<Item> &fields, List<Item> &fields,
...@@ -96,7 +118,7 @@ int mysql_update(THD *thd, ...@@ -96,7 +118,7 @@ int mysql_update(THD *thd,
ha_rows limit, ha_rows limit,
enum enum_duplicates handle_duplicates) enum enum_duplicates handle_duplicates)
{ {
bool using_limit=limit != HA_POS_ERROR; bool using_limit=limit != HA_POS_ERROR;
bool safe_update= thd->options & OPTION_SAFE_UPDATES; bool safe_update= thd->options & OPTION_SAFE_UPDATES;
bool used_key_is_modified, transactional_table, log_delayed; bool used_key_is_modified, transactional_table, log_delayed;
int error=0; int error=0;
...@@ -117,6 +139,15 @@ int mysql_update(THD *thd, ...@@ -117,6 +139,15 @@ int mysql_update(THD *thd,
if ((error= open_and_lock_tables(thd, table_list))) if ((error= open_and_lock_tables(thd, table_list)))
DBUG_RETURN(error); DBUG_RETURN(error);
if (table_list->table == 0)
{
DBUG_ASSERT(table_list->view &&
table_list->ancestor && table_list->ancestor->next_local);
DBUG_PRINT("info", ("Switch to multi-update"));
/* convert to multiupdate */
return 2;
}
thd->proc_info="init"; thd->proc_info="init";
table= table_list->table; table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
...@@ -626,12 +657,30 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -626,12 +657,30 @@ int mysql_multi_update_prepare(THD *thd)
if (tables_for_update & readonly_tables) if (tables_for_update & readonly_tables)
{ {
// find readonly table/view which cause error // find readonly table/view which cause error
for (tl= leaves; tl; tl= tl->next_local) for (tl= leaves; tl; tl= tl->next_leaf)
{ {
if ((readonly_tables & tl->table->map) && if ((readonly_tables & tl->table->map) &&
(tables_for_update & tl->table->map)) (tables_for_update & tl->table->map))
{ {
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE"); TABLE_LIST *table= tl->belong_to_view ? tl->belong_to_view : tl;
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table->alias, "UPDATE");
DBUG_RETURN(-1);
}
}
}
/* check single table update for view compound from several tables */
for (tl= table_list; tl; tl= tl->next_local)
{
if (tl->table == 0)
{
DBUG_ASSERT(tl->view &&
tl->ancestor && tl->ancestor->next_local);
TABLE_LIST *for_update= 0;
if (tl->check_single_table(&for_update, tables_for_update))
{
my_error(ER_VIEW_MULTIUPDATE, MYF(0),
tl->view_db.str, tl->view_name.str);
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
} }
...@@ -647,19 +696,22 @@ int mysql_multi_update(THD *thd, ...@@ -647,19 +696,22 @@ int mysql_multi_update(THD *thd,
COND *conds, COND *conds,
ulong options, 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,
bool converted)
{ {
int res; int res= 0;
multi_update *result; multi_update *result;
DBUG_ENTER("mysql_multi_update"); DBUG_ENTER("mysql_multi_update");
if ((res= open_and_lock_tables(thd, table_list))) if (!converted && (res= open_and_lock_tables(thd, table_list)))
DBUG_RETURN(res); DBUG_RETURN(res);
if ((res= mysql_multi_update_prepare(thd))) if ((res= mysql_multi_update_prepare(thd)))
DBUG_RETURN(res); DBUG_RETURN(res);
if (!(result= new multi_update(thd, table_list, fields, values, if (!(result= new multi_update(thd, table_list,
thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates))) handle_duplicates)))
DBUG_RETURN(-1); DBUG_RETURN(-1);
...@@ -677,12 +729,14 @@ int mysql_multi_update(THD *thd, ...@@ -677,12 +729,14 @@ int mysql_multi_update(THD *thd,
multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
TABLE_LIST *leaves_list,
List<Item> *field_list, List<Item> *value_list, List<Item> *field_list, List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg) enum enum_duplicates handle_duplicates_arg)
:all_tables(table_list), update_tables(0), thd(thd_arg), tmp_tables(0), :all_tables(table_list), leaves(leaves_list), update_tables(0),
updated(0), found(0), fields(field_list), values(value_list), thd(thd_arg), tmp_tables(0), updated(0), found(0), fields(field_list),
table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), values(value_list), table_count(0), copy_field(0),
do_update(1), trans_safe(0), transactional_tables(1) handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0),
transactional_tables(1)
{} {}
...@@ -730,7 +784,7 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -730,7 +784,7 @@ int multi_update::prepare(List<Item> &not_used_values,
*/ */
update.empty(); update.empty();
for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local) for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
{ {
/* 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;
...@@ -800,7 +854,7 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -800,7 +854,7 @@ int multi_update::prepare(List<Item> &not_used_values,
which will cause an error when reading a row. which will cause an error when reading a row.
(This issue is mostly relevent for MyISAM tables) (This issue is mostly relevent for MyISAM tables)
*/ */
for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local) for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
{ {
TABLE *table=table_ref->table; TABLE *table=table_ref->table;
if (!(tables_to_update & table->map) && if (!(tables_to_update & table->map) &&
......
...@@ -465,23 +465,27 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, ...@@ -465,23 +465,27 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
if ((view->updatable_view= (can_be_merged && if ((view->updatable_view= (can_be_merged &&
view->algorithm != VIEW_ALGORITHM_TMPTABLE))) view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
{ {
if (thd->lex->select_lex.table_list.elements > 1) // TODO: change here when we will support UNIONs
view->updatable_view= 0; for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first;
else tbl;
tbl= tbl->next_local)
{ {
// TODO: change here when we will support UNIONs if (tbl->view && !tbl->updatable_view)
for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first;
tbl;
tbl= tbl->next_local)
{ {
if (tbl->view && !tbl->updatable_view) view->updatable_view= 0;
{ break;
view->updatable_view= 0; }
break; for (TABLE_LIST *up= tbl; up; up= up->embedding)
} {
if (up->outer_join)
{
view->updatable_view= 0;
goto loop_out;
}
} }
} }
} }
loop_out:
if (sql_create_definition_file(&dir, &file, view_file_type, if (sql_create_definition_file(&dir, &file, view_file_type,
(gptr)view, view_parameters, 3)) (gptr)view, view_parameters, 3))
{ {
...@@ -701,9 +705,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) ...@@ -701,9 +705,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
old_lex->can_use_merged()) && old_lex->can_use_merged()) &&
!old_lex->can_not_use_merged()) !old_lex->can_not_use_merged())
{ {
/*
TODO: support multi tables substitutions
*/
/* lex should contain at least one table */ /* lex should contain at least one table */
DBUG_ASSERT(view_tables != 0); DBUG_ASSERT(view_tables != 0);
...@@ -736,7 +737,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) ...@@ -736,7 +737,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
/* multi table view */ /* multi table view */
if (view_tables->next_local) if (view_tables->next_local)
{ {
table->updatable= 0;
/* make nested join structure for view tables */ /* make nested join structure for view tables */
NESTED_JOIN *nested_join; NESTED_JOIN *nested_join;
if (!(nested_join= table->nested_join= if (!(nested_join= table->nested_join=
...@@ -919,14 +919,16 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) ...@@ -919,14 +919,16 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
uint i, elements_in_view; uint i, elements_in_view;
DBUG_ENTER("check_key_in_view"); DBUG_ENTER("check_key_in_view");
if (!view->view) if (!view->view && !view->belong_to_view)
DBUG_RETURN(FALSE); /* it is normal table */ DBUG_RETURN(FALSE); /* it is normal table */
table= view->table; table= view->table;
if (view->belong_to_view)
view= view->belong_to_view;
trans= view->field_translation; trans= view->field_translation;
key_info_end= (key_info= table->key_info)+ table->keys; key_info_end= (key_info= table->key_info)+ table->keys;
elements_in_view= view->view->select_lex.item_list.elements; elements_in_view= view->view->select_lex.item_list.elements;
DBUG_ASSERT(view->table != 0 && view->field_translation != 0); DBUG_ASSERT(table != 0 && view->field_translation != 0);
/* Loop over all keys to see if a unique-not-null key is used */ /* Loop over all keys to see if a unique-not-null key is used */
for (;key_info != key_info_end ; key_info++) for (;key_info != key_info_end ; key_info++)
...@@ -1002,21 +1004,30 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) ...@@ -1002,21 +1004,30 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
insert_view_fields() insert_view_fields()
list list for insertion list list for insertion
view view for processing view view for processing
RETURN
0 - OK
-1 - error (is not sent to cliet)
*/ */
void insert_view_fields(List<Item> *list, TABLE_LIST *view) int insert_view_fields(List<Item> *list, TABLE_LIST *view)
{ {
uint elements_in_view= view->view->select_lex.item_list.elements; uint elements_in_view= view->view->select_lex.item_list.elements;
Field_translator *trans; Field_translator *trans;
DBUG_ENTER("insert_view_fields"); DBUG_ENTER("insert_view_fields");
if (!(trans= view->field_translation)) if (!(trans= view->field_translation))
DBUG_VOID_RETURN; DBUG_RETURN(0);
for (uint i= 0; i < elements_in_view; i++) for (uint i= 0; i < elements_in_view; i++)
{ {
if (trans[i].item->type() == Item::FIELD_ITEM) if (trans[i].item->type() == Item::FIELD_ITEM)
list->push_back(trans[i].item); list->push_back(trans[i].item);
else
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "INSERT");
DBUG_RETURN(-1);
}
} }
DBUG_VOID_RETURN; DBUG_RETURN(0);
} }
...@@ -25,7 +25,7 @@ int mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode); ...@@ -25,7 +25,7 @@ int mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode);
bool check_key_in_view(THD *thd, TABLE_LIST * view); bool check_key_in_view(THD *thd, TABLE_LIST * view);
void insert_view_fields(List<Item> *list, TABLE_LIST *view); int insert_view_fields(List<Item> *list, TABLE_LIST *view);
frm_type_enum mysql_frm_type(char *path); frm_type_enum mysql_frm_type(char *path);
......
...@@ -1744,6 +1744,95 @@ err: ...@@ -1744,6 +1744,95 @@ err:
} }
/*
Find table in underlaying tables by mask and check that only this
table sbelong to given mask
SYNOPSIS
st_table_list::check_single_table()
table reference on variable where to store found table
(should be 0 on call, to find table, or point to table for
unique test)
map bit mask of tables
RETURN
0 table not found or found only one
1 found several tables
*/
bool st_table_list::check_single_table(st_table_list **table, table_map map)
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
{
if (tbl->table)
{
if (tbl->table->map & map)
{
if (*table)
return 1;
else
*table= tbl;
}
}
else
if (tbl->check_single_table(table, map))
return 1;
}
}
/*
Set insert_values buffer
SYNOPSIS
set_insert_values()
mem_root memory pool for allocating
RETURN
FALSE - OK
TRUE - out of memory
*/
bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
{
if (table)
{
if (!table->insert_values &&
!(table->insert_values= (byte *)alloc_root(mem_root,
table->rec_buff_length)))
return TRUE;
}
else
{
DBUG_ASSERT(view && ancestor && ancestor->next_local);
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
return TRUE;
}
return FALSE;
}
/*
clear insert_values reference
SYNOPSIS
clear_insert_values()
*/
void st_table_list::clear_insert_values()
{
if (table)
table->insert_values= 0;
else
{
DBUG_ASSERT(view && ancestor && ancestor->next_local);
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
tbl->clear_insert_values();
}
}
void Field_iterator_view::set(TABLE_LIST *table) void Field_iterator_view::set(TABLE_LIST *table)
{ {
ptr= table->field_translation; ptr= table->field_translation;
......
...@@ -277,6 +277,9 @@ typedef struct st_table_list ...@@ -277,6 +277,9 @@ typedef struct st_table_list
void print(THD *thd, String *str); void print(THD *thd, String *str);
void save_and_clear_want_privilege(); void save_and_clear_want_privilege();
void restore_want_privilege(); void restore_want_privilege();
bool check_single_table(st_table_list **table, table_map map);
bool set_insert_values(MEM_ROOT *mem_root);
void clear_insert_values();
inline st_table_list *next_independent() inline st_table_list *next_independent()
{ {
if (view) if (view)
......
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