diff options
author | unknown <bell@sanja.is.com.ua> | 2004-07-16 01:15:55 +0300 |
---|---|---|
committer | unknown <bell@sanja.is.com.ua> | 2004-07-16 01:15:55 +0300 |
commit | 8790b1e65c3e1b2ae2ad9aedfe064c20d48bd096 (patch) | |
tree | 7a8e815ca26d9138de3bc77a619aa8103e037911 | |
parent | b0df20349d5e6ed2fc1527c3bd1db66a7d4ff311 (diff) | |
download | mariadb-git-8790b1e65c3e1b2ae2ad9aedfe064c20d48bd096.tar.gz |
VIEW
two TABLE_LIST copy eliminated
include/mysqld_error.h:
errors of view
libmysqld/Makefile.am:
new view file
mysql-test/r/connect.result:
SHOW TABLE show type of table
mysql-test/r/ctype_recoding.result:
SHOW TABLE show type of table
mysql-test/r/drop.result:
SHOW TABLE show type of table
mysql-test/r/grant.result:
new two privileges (CRETEA|SHOW VIEW)
mysql-test/r/lowercase_table.result:
SHOW TABLE show type of table
mysql-test/r/ps_1general.result:
SHOW TABLE show type of table
mysql-test/r/rename.result:
SHOW TABLE show type of table
mysql-test/r/rpl000009.result:
SHOW TABLE show type of table
mysql-test/r/rpl_error_ignored_table.result:
SHOW TABLE show type of table
mysql-test/r/select.result:
SHOW TABLE show type of table
mysql-test/r/system_mysql_db.result:
SHOW TABLE show type of table
new two privileges (CRETEA|SHOW VIEW)
mysql-test/t/system_mysql_db_fix.test:
removing all system tables
scripts/mysql_fix_privilege_tables.sql:
new two privileges (CRETEA|SHOW VIEW)
sql/Makefile.am:
new VIEW related file
sql/ha_myisammrg.cc:
two TABLE_LIST copy eliminated
sql/item.cc:
VIEW
sql/item.h:
VIEW
sql/item_subselect.cc:
VIEW
sql/item_subselect.h:
VIEW
sql/lex.h:
VIEW
sql/lock.cc:
VIEW
sql/mysql_priv.h:
VIEW
sql/mysqld.cc:
VIEW
new parameter - sql_updatable_view_key
sql/opt_sum.cc:
two TABLE_LIST copy eliminated
sql/set_var.cc:
new parameter - sql_updatable_view_key
sql/share/czech/errmsg.txt:
errors messages of views
sql/share/danish/errmsg.txt:
errors messages of views
sql/share/dutch/errmsg.txt:
errors messages of views
sql/share/english/errmsg.txt:
errors messages of views
sql/share/estonian/errmsg.txt:
errors messages of views
sql/share/french/errmsg.txt:
errors messages of views
sql/share/german/errmsg.txt:
errors messages of views
sql/share/greek/errmsg.txt:
errors messages of views
sql/share/hungarian/errmsg.txt:
errors messages of views
sql/share/italian/errmsg.txt:
errors messages of views
sql/share/japanese/errmsg.txt:
errors messages of views
sql/share/korean/errmsg.txt:
errors messages of views
sql/share/norwegian-ny/errmsg.txt:
errors messages of views
sql/share/norwegian/errmsg.txt:
errors messages of views
sql/share/polish/errmsg.txt:
errors messages of views
sql/share/portuguese/errmsg.txt:
errors messages of views
sql/share/romanian/errmsg.txt:
errors messages of views
sql/share/russian/errmsg.txt:
errors messages of views
sql/share/serbian/errmsg.txt:
errors messages of views
sql/share/slovak/errmsg.txt:
errors messages of views
sql/share/spanish/errmsg.txt:
errors messages of views
sql/share/swedish/errmsg.txt:
errors messages of views
sql/share/ukrainian/errmsg.txt:
errors messages of views
sql/slave.cc:
two TABLE_LIST copy eliminated
sql/sp.cc:
VIEW
sql/sql_acl.cc:
VIEW
sql/sql_acl.h:
VIEW
sql/sql_base.cc:
VIEW
sql/sql_cache.cc:
two TABLE_LIST copy eliminated
sql/sql_class.h:
VIEW
sql/sql_db.cc:
two TABLE_LIST copy eliminated
sql/sql_delete.cc:
VIEW
sql/sql_derived.cc:
VIEW
sql/sql_handler.cc:
two TABLE_LIST copy eliminated
sql/sql_help.cc:
two TABLE_LIST copy eliminated
sql/sql_insert.cc:
VIEW
sql/sql_lex.cc:
VIEW
sql/sql_lex.h:
VIEW
sql/sql_load.cc:
VIEW
sql/sql_olap.cc:
VIEW
sql/sql_parse.cc:
two TABLE_LIST copy eliminated
VIEW
sql/sql_prepare.cc:
VIEW
sql/sql_rename.cc:
two TABLE_LIST copy eliminated
sql/sql_select.cc:
VIEW
sql/sql_show.cc:
VIEW
sql/sql_table.cc:
VIEW
sql/sql_union.cc:
VIEW
sql/sql_update.cc:
VIEW
sql/sql_yacc.yy:
VIEW
sql/table.cc:
VIEW
sql/table.h:
VIEW
sql/tztime.cc:
two TABLE_LIST copy eliminated
sql/unireg.h:
VIEW
tests/client_test.c:
VIEW
85 files changed, 6093 insertions, 1217 deletions
diff --git a/include/mysqld_error.h b/include/mysqld_error.h index d4848150c3f..0075e51f72d 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -359,4 +359,15 @@ #define ER_FPARSER_EOF_IN_COMMENT 1340 #define ER_FPARSER_ERROR_IN_PARAMETER 1341 #define ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER 1342 -#define ER_ERROR_MESSAGES 343 +#define ER_VIEW_NO_EXPLAIN 1343 +#define ER_FRM_UNKNOWN_TYPE 1344 +#define ER_WRONG_OBJECT 1345 +#define ER_NONUPDATEABLE_COLUMN 1346 +#define ER_VIEW_SELECT_DERIVED 1347 +#define ER_VIEW_SELECT_PROCEDURE 1348 +#define ER_VIEW_SELECT_VARIABLE 1349 +#define ER_VIEW_SELECT_TMPTABLE 1350 +#define ER_VIEW_WRONG_LIST 1351 +#define ER_WARN_VIEW_MERGE 1352 +#define ER_WARN_VIEW_WITHOUT_KEY 1353 +#define ER_ERROR_MESSAGES 354 diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 390dab409b3..28f0d5111e6 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -59,7 +59,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ spatial.cc gstream.cc sql_help.cc tztime.cc protocol_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ - parse_file.cc + parse_file.cc sql_view.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index e0a6c0cd141..9523bdc22fd 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -1,67 +1,67 @@ show tables; -Tables_in_mysql -columns_priv -db -func -help_category -help_keyword -help_relation -help_topic -host -proc -tables_priv -time_zone -time_zone_leap_second -time_zone_name -time_zone_transition -time_zone_transition_type -user +Tables_in_mysql Type +columns_priv table +db table +func table +help_category table +help_keyword table +help_relation table +help_topic table +host table +proc table +tables_priv table +time_zone table +time_zone_leap_second table +time_zone_name table +time_zone_transition table +time_zone_transition_type table +user table show tables; -Tables_in_test +Tables_in_test Type grant ALL on *.* to test@localhost identified by "gambling"; grant ALL on *.* to test@127.0.0.1 identified by "gambling"; show tables; -Tables_in_mysql -columns_priv -db -func -help_category -help_keyword -help_relation -help_topic -host -proc -tables_priv -time_zone -time_zone_leap_second -time_zone_name -time_zone_transition -time_zone_transition_type -user +Tables_in_mysql Type +columns_priv table +db table +func table +help_category table +help_keyword table +help_relation table +help_topic table +host table +proc table +tables_priv table +time_zone table +time_zone_leap_second table +time_zone_name table +time_zone_transition table +time_zone_transition_type table +user table show tables; -Tables_in_test +Tables_in_test Type update mysql.user set password=old_password("gambling2") where user=_binary"test"; flush privileges; set password=old_password('gambling3'); show tables; -Tables_in_mysql -columns_priv -db -func -help_category -help_keyword -help_relation -help_topic -host -proc -tables_priv -time_zone -time_zone_leap_second -time_zone_name -time_zone_transition -time_zone_transition_type -user +Tables_in_mysql Type +columns_priv table +db table +func table +help_category table +help_keyword table +help_relation table +help_topic table +host table +proc table +tables_priv table +time_zone table +time_zone_leap_second table +time_zone_name table +time_zone_transition table +time_zone_transition_type table +user table show tables; -Tables_in_test +Tables_in_test Type delete from mysql.user where user=_binary"test"; flush privileges; diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index 805f731f0ec..215f7f28284 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -45,8 +45,8 @@ CREATE TABLE `ÔÁÂÌÉÃÁ` ÐÏÌÅ CHAR(32) CHARACTER SET koi8r NOT NULL COMMENT "ËÏÍÍÅÎÔÁÒÉÊ ÐÏÌÑ" ) COMMENT "ËÏÍÍÅÎÔÁÒÉÊ ÔÁÂÌÉÃÙ"; SHOW TABLES; -Tables_in_test -ÔÁÂÌÉÃÁ +Tables_in_test Type +ÔÁÂÌÉÃÁ table SHOW CREATE TABLE ÔÁÂÌÉÃÁ; Table Create Table ÔÁÂÌÉÃÁ CREATE TABLE `ÔÁÂÌÉÃÁ` ( @@ -57,8 +57,8 @@ Field Type Null Key Default Extra ÐÏÌÅ char(32) SET CHARACTER SET cp1251; SHOW TABLES; -Tables_in_test -òàáëèöà +Tables_in_test Type +òàáëèöà table SHOW CREATE TABLE òàáëèöà; Table Create Table òàáëèöà CREATE TABLE `òàáëèöà` ( @@ -69,8 +69,8 @@ Field Type Null Key Default Extra ïîëå char(32) SET CHARACTER SET utf8; SHOW TABLES; -Tables_in_test -таблица +Tables_in_test Type +таблица table SHOW CREATE TABLE таблица; Table Create Table таблица CREATE TABLE `таблица` ( @@ -93,14 +93,14 @@ SET CHARACTER SET koi8r; CREATE DATABASE ÔÅÓÔ; USE ÔÅÓÔ; SHOW TABLES; -Tables_in_ÔÅÓÔ +Tables_in_ÔÅÓÔ Type SHOW TABLES IN ÔÅÓÔ; -Tables_in_ÔÅÓÔ +Tables_in_ÔÅÓÔ Type SET CHARACTER SET cp1251; SHOW TABLES; -Tables_in_òåñò +Tables_in_òåñò Type SHOW TABLES IN òåñò; -Tables_in_òåñò +Tables_in_òåñò Type SET CHARACTER SET koi8r; DROP DATABASE ÔÅÓÔ; SET NAMES koi8r; diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 8b919964163..6ff1d683b9c 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -52,6 +52,6 @@ ERROR HY000: Can't execute the query because you have a conflicting read lock unlock tables; create table t1(n int); show tables; -Tables_in_test -t1 +Tables_in_test Type +t1 table drop table t1; diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index aa6c0c3f505..c9ae7411e45 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -10,8 +10,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' grant delete on mysqltest.* to mysqltest_1@localhost; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0 show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -62,7 +62,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost; show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION revoke all privileges on mysqltest.* from mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; flush privileges; diff --git a/mysql-test/r/lowercase_table.result b/mysql-test/r/lowercase_table.result index 8bf3db8cad0..f820591cfcf 100644 --- a/mysql-test/r/lowercase_table.result +++ b/mysql-test/r/lowercase_table.result @@ -24,9 +24,9 @@ RENAME TABLE T1 TO T2; ALTER TABLE T2 ADD new_col int not null; ALTER TABLE T2 RENAME T3; show tables like 't_'; -Tables_in_test (t_) -t3 -t4 +Tables_in_test (t_) Type +t3 table +t4 table drop table t3,t4; create table t1 (a int); select count(*) from T1; @@ -57,4 +57,4 @@ select C.a, c.a from t1 c, t2 C; ERROR 42000: Not unique table/alias: 'C' drop table t1, t2; show tables; -Tables_in_test +Tables_in_test Type diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result index 52b887e2795..f811eb9d320 100644 --- a/mysql-test/r/ps_1general.result +++ b/mysql-test/r/ps_1general.result @@ -252,8 +252,8 @@ mysql test prepare stmt4 from ' show tables from test like ''t2%'' '; execute stmt4; -Tables_in_test (t2%) -t2 +Tables_in_test (t2%) Type +t2 table prepare stmt4 from ' show columns from t2 from test like ''a%'' '; execute stmt4; Field Type Null Key Default Extra diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result index 9bcf1bc7f97..d55205892aa 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -19,10 +19,10 @@ Got one of the listed errors rename table t3 to t4, t2 to t3, t1 to t2, t4 to t2; Got one of the listed errors show tables like "t_"; -Tables_in_test (t_) -t1 -t2 -t3 +Tables_in_test (t_) Type +t1 table +t2 table +t3 table rename table t3 to t1, t2 to t3, t1 to t2, t4 to t1; Got one of the listed errors rename table t3 to t4, t5 to t3, t1 to t2, t4 to t1; @@ -44,12 +44,12 @@ CREATE TABLE t3 (a int); FLUSH TABLES WITH READ LOCK; RENAME TABLE t1 TO t2, t3 to t4; show tables; -Tables_in_test -t1 -t3 +Tables_in_test Type +t1 table +t3 table UNLOCK TABLES; show tables; -Tables_in_test -t2 -t4 +Tables_in_test Type +t2 table +t4 table drop table t2, t4; diff --git a/mysql-test/r/rpl000009.result b/mysql-test/r/rpl000009.result index bb82dcb1e6a..e3eff5af33e 100644 --- a/mysql-test/r/rpl000009.result +++ b/mysql-test/r/rpl000009.result @@ -73,25 +73,25 @@ mysqltest3 test use mysqltest2; show tables; -Tables_in_mysqltest2 -t1 -t3 +Tables_in_mysqltest2 Type +t1 table +t3 table select * from t1; n s 1 original foo.t1 use mysqltest3; show tables; -Tables_in_mysqltest3 -t1 +Tables_in_mysqltest3 Type +t1 table select * from t1; n s 1 original foo2.t1 use mysqltest; show tables; -Tables_in_mysqltest -t1 -t2 -t3 +Tables_in_mysqltest Type +t1 table +t2 table +t3 table select * from mysqltest.t1; n s 1 one test diff --git a/mysql-test/r/rpl_error_ignored_table.result b/mysql-test/r/rpl_error_ignored_table.result index 19c8ee2e8c7..f353e0aba54 100644 --- a/mysql-test/r/rpl_error_ignored_table.result +++ b/mysql-test/r/rpl_error_ignored_table.result @@ -11,7 +11,7 @@ show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master # 127.0.0.1 root MASTER_PORT 1 master-bin.000001 273 # # master-bin.000001 Yes Yes test.t3,test.t1,test.t2 0 0 273 # None 0 No # show tables like 't1'; -Tables_in_test (t1) +Tables_in_test (t1) Type drop table t1; select get_lock('crash_lock%20C', 10); get_lock('crash_lock%20C', 10) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 432c5171da5..fa64649dae3 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2021,15 +2021,15 @@ select period as "Nuvarande period" from t1 group by "Nuvarande period" limit 1; Nuvarande period 9410 show tables; -Tables_in_test -t1 -t2 -t3 -t4 +Tables_in_test Type +t1 table +t2 table +t3 table +t4 table show tables from test like "s%"; -Tables_in_test (s%) +Tables_in_test (s%) Type show tables from test like "t?"; -Tables_in_test (t?) +Tables_in_test (t?) Type show full columns from t2; Field Type Collation Null Key Default Extra Privileges Comment auto int(11) NULL PRI NULL auto_increment select,insert,update,references diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 4b4377915c7..f8269f902f5 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -1,21 +1,21 @@ show tables; -Tables_in_db -columns_priv -db -func -help_category -help_keyword -help_relation -help_topic -host -proc -tables_priv -time_zone -time_zone_leap_second -time_zone_name -time_zone_transition -time_zone_transition_type -user +Tables_in_db Type +columns_priv table +db table +func table +help_category table +help_keyword table +help_relation table +help_topic table +host table +proc table +tables_priv table +time_zone table +time_zone_leap_second table +time_zone_name table +time_zone_transition table +time_zone_transition_type table +user table show create table db; Table Create Table db CREATE TABLE `db` ( @@ -34,6 +34,8 @@ db CREATE TABLE `db` ( `Alter_priv` enum('N','Y') NOT NULL default 'N', `Create_tmp_table_priv` enum('N','Y') NOT NULL default 'N', `Lock_tables_priv` enum('N','Y') NOT NULL default 'N', + `Create_view_priv` enum('N','Y') NOT NULL default 'N', + `Show_view_priv` enum('N','Y') NOT NULL default 'N', PRIMARY KEY (`Host`,`Db`,`User`), KEY `User` (`User`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Database privileges' @@ -54,6 +56,8 @@ host CREATE TABLE `host` ( `Alter_priv` enum('N','Y') NOT NULL default 'N', `Create_tmp_table_priv` enum('N','Y') NOT NULL default 'N', `Lock_tables_priv` enum('N','Y') NOT NULL default 'N', + `Create_view_priv` enum('N','Y') NOT NULL default 'N', + `Show_view_priv` enum('N','Y') NOT NULL default 'N', PRIMARY KEY (`Host`,`Db`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='Host privileges; Merged with database privileges' show create table user; @@ -83,6 +87,8 @@ user CREATE TABLE `user` ( `Execute_priv` enum('N','Y') NOT NULL default 'N', `Repl_slave_priv` enum('N','Y') NOT NULL default 'N', `Repl_client_priv` enum('N','Y') NOT NULL default 'N', + `Create_view_priv` enum('N','Y') NOT NULL default 'N', + `Show_view_priv` enum('N','Y') NOT NULL default 'N', `ssl_type` enum('','ANY','X509','SPECIFIED') NOT NULL default '', `ssl_cipher` blob NOT NULL, `x509_issuer` blob NOT NULL, diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result new file mode 100644 index 00000000000..03b771de5d7 --- /dev/null +++ b/mysql-test/r/view.result @@ -0,0 +1,943 @@ +drop table if exists t1,t2; +drop view if exists v1,v2,v3,v4,v5,v6; +drop database if exists mysqltest; +use test; +create view v1 (c,d) as select a,b from t1; +ERROR 42S02: Table 'test.t1' doesn't exist +create temporary table t1 (a int, b int); +create view v1 (c) as select b+1 from t1; +ERROR HY000: View's SELECT contains a temporary table 't1' +drop table t1; +create table t1 (a int, b int); +insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); +create view v1 (c,d) as select a,b+@@global.max_user_connections from t1; +ERROR HY000: View's SELECT contains a variable or parameter +create view v1 (c) as select b+1 from t1; +select c from v1; +c +3 +4 +5 +6 +11 +create temporary table t1 (a int, b int); +select * from t1; +a b +select c from v1; +c +3 +4 +5 +6 +11 +show create table v1; +Table Create Table +v1 CREATE VIEW test.v1 AS select (test.t1.b + 1) AS `c` from test.t1 +show create view v1; +Table Create Table +v1 CREATE VIEW test.v1 AS select (test.t1.b + 1) AS `c` from test.t1 +show create view t1; +ERROR HY000: 'test.t1' is not VIEW +drop table t1; +select a from v1; +ERROR 42S22: Unknown column 'a' in 'field list' +select v1.a from v1; +ERROR 42S22: Unknown column 'v1.a' in 'field list' +select b from v1; +ERROR 42S22: Unknown column 'b' in 'field list' +select v1.b from v1; +ERROR 42S22: Unknown column 'v1.b' in 'field list' +explain extended select c from v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select (test.t1.b + 1) AS `c` from test.v1 +create algorithm=temptable view v2 (c) as select b+1 from t1; +show create table v2; +Table Create Table +v2 CREATE ALGORITHM=TMPTABLE VIEW test.v2 AS select (test.t1.b + 1) AS `c` from test.t1 +select c from v2; +c +3 +4 +5 +6 +11 +explain extended select c from v2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select v2.c AS `c` from test.v2 +create view v3 (c) as select a+1 from v1; +ERROR 42S22: Unknown column 'a' in 'field list' +create view v3 (c) as select b+1 from v1; +ERROR 42S22: Unknown column 'b' in 'field list' +create view v3 (c) as select c+1 from v1; +select c from v3; +c +4 +5 +6 +7 +12 +explain extended select c from v3; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select ((test.t1.b + 1) + 1) AS `c` from test.v3 +create algorithm=temptable view v4 (c) as select c+1 from v2; +select c from v4; +c +4 +5 +6 +7 +12 +explain extended select c from v4; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 +2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 5 +3 SUBQUERY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select v4.c AS `c` from test.v4 +create view v5 (c) as select c+1 from v2; +select c from v5; +c +4 +5 +6 +7 +12 +explain extended select c from v5; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived3> ALL NULL NULL NULL NULL 5 +3 SUBQUERY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select (v2.c + 1) AS `c` from test.v5 +create algorithm=temptable view v6 (c) as select c+1 from v1; +select c from v6; +c +4 +5 +6 +7 +12 +explain extended select c from v6; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 5 +Warnings: +Note 1003 select v6.c AS `c` from test.v6 +show tables; +Tables_in_test Type +t1 table +v1 view +v2 view +v3 view +v4 view +v5 view +v6 view +show table status; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Fixed 5 9 45 38654705663 1024 0 NULL # # NULL latin1_swedish_ci NULL +v1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +v2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +v3 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +v4 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +v5 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +v6 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view +drop view v1,v2,v3,v4,v5,v6; +create view v1 (c,d,e,f) as select a,b, +a in (select a+2 from t1), a = all (select a from t1) from t1; +create view v2 as select c, d from v1; +select * from v1; +c d e f +1 2 0 0 +1 3 0 0 +2 4 0 0 +2 5 0 0 +3 10 1 0 +select * from v2; +c d +1 2 +1 3 +2 4 +2 5 +3 10 +create view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1; +ERROR 42S01: Table 'v1' already exists +create or replace view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1; +drop view v2; +alter view v2 as select c, d from v1; +ERROR 42S02: Table 'test.v2' doesn't exist +create or replace view v2 as select c, d from v1; +alter view v1 (c,d) as select a,max(b) from t1 group by a; +select * from v1; +c d +1 3 +2 5 +3 10 +select * from v2; +c d +1 3 +2 5 +3 10 +grant create view on test.* to test@localhost; +show grants for test@localhost; +Grants for test@localhost +GRANT USAGE ON *.* TO 'test'@'localhost' +GRANT CREATE VIEW ON `test`.* TO 'test'@'localhost' +revoke create view on test.* from test@localhost; +show grants for test@localhost; +Grants for test@localhost +GRANT USAGE ON *.* TO 'test'@'localhost' +drop view v100; +ERROR 42S02: Unknown table 'test.v100' +drop view t1; +ERROR HY000: 'test.t1' is not VIEW +drop table v1; +ERROR 42S02: Unknown table 'v1' +drop view v1,v2; +drop table t1; +create table t1 (a int); +insert into t1 values (1), (2), (3); +create view v1 (a) as select a+1 from t1; +create view v2 (a) as select a-1 from t1; +select * from t1 natural left join v1; +a a +1 NULL +2 2 +3 3 +select * from v2 natural left join t1; +a a +0 NULL +1 1 +2 2 +select * from v2 natural left join v1; +a a +0 NULL +1 NULL +2 2 +drop view v1, v2; +drop table t1; +create database mysqltest; +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); +grant select on mysqltest.t1 to mysqltest_1@localhost; +grant create view,select on test.* to mysqltest_1@localhost; +create view v1 as select * from mysqltest.t1; +create view mysqltest.v2 as select * from mysqltest.t1; +ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v2' +create view v2 as select * from mysqltest.t2; +ERROR 42000: ANY command denied to user 'mysqltest_1'@'localhost' for table 't2' +revoke all privileges on mysqltest.t1 from mysqltest_1@localhost; +revoke all privileges on test.* from mysqltest_1@localhost; +drop database mysqltest; +drop view test.v1; +create database mysqltest; +create table mysqltest.t1 (a int, b int); +create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +grant select (c) on mysqltest.v1 to mysqltest_1@localhost; +select c from mysqltest.v1; +c +select d from mysqltest.v1; +ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'd' in table 'v1' +revoke all privileges on mysqltest.v1 from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; +create database mysqltest; +create table mysqltest.t1 (a int, b int); +create algorithm=temptable view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +grant select (c) on mysqltest.v1 to mysqltest_1@localhost; +select c from mysqltest.v1; +c +select d from mysqltest.v1; +ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'd' in table 'v1' +revoke all privileges on mysqltest.v1 from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; +create database mysqltest; +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); +create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +create algorithm=temptable view mysqltest.v2 (c,d) as select a+1,b+1 from mysqltest.t1; +create view mysqltest.v3 (c,d) as select a+1,b+1 from mysqltest.t2; +create algorithm=temptable view mysqltest.v4 (c,d) as select a+1,b+1 from mysqltest.t2; +grant select on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.v2 to mysqltest_1@localhost; +grant select on mysqltest.v3 to mysqltest_1@localhost; +grant select on mysqltest.v4 to mysqltest_1@localhost; +select c from mysqltest.v1; +c +select c from mysqltest.v2; +c +select c from mysqltest.v3; +c +select c from mysqltest.v4; +c +show columns from mysqltest.v1; +Field Type Null Key Default Extra +c bigint(20) YES NULL +d bigint(20) YES NULL +show columns from mysqltest.v2; +Field Type Null Key Default Extra +c bigint(20) YES NULL +d bigint(20) YES NULL +explain select c from mysqltest.v1; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v1; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +explain select c from mysqltest.v2; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v2; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +explain select c from mysqltest.v3; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v3; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +explain select c from mysqltest.v4; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v4; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +grant select on mysqltest.t1 to mysqltest_1@localhost; +explain select c from mysqltest.v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found +show create table mysqltest.v1; +Table Create Table +v1 CREATE VIEW mysqltest.v1 AS select (mysqltest.t1.a + 1) AS `c`,(mysqltest.t1.b + 1) AS `d` from mysqltest.t1 +explain select c from mysqltest.v2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +show create table mysqltest.v2; +Table Create Table +v2 CREATE ALGORITHM=TMPTABLE VIEW mysqltest.v2 AS select (mysqltest.t1.a + 1) AS `c`,(mysqltest.t1.b + 1) AS `d` from mysqltest.t1 +explain select c from mysqltest.v3; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v3; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +explain select c from mysqltest.v4; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +show create table mysqltest.v4; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +grant show view on mysqltest.* to mysqltest_1@localhost; +explain select c from mysqltest.v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found +show create table mysqltest.v1; +Table Create Table +v1 CREATE VIEW mysqltest.v1 AS select (mysqltest.t1.a + 1) AS `c`,(mysqltest.t1.b + 1) AS `d` from mysqltest.t1 +explain select c from mysqltest.v2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +show create table mysqltest.v2; +Table Create Table +v2 CREATE ALGORITHM=TMPTABLE VIEW mysqltest.v2 AS select (mysqltest.t1.a + 1) AS `c`,(mysqltest.t1.b + 1) AS `d` from mysqltest.t1 +explain select c from mysqltest.v3; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 system NULL NULL NULL NULL 0 const row not found +show create table mysqltest.v3; +Table Create Table +v3 CREATE VIEW mysqltest.v3 AS select (mysqltest.t2.a + 1) AS `c`,(mysqltest.t2.b + 1) AS `d` from mysqltest.t2 +explain select c from mysqltest.v4; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +show create table mysqltest.v4; +Table Create Table +v4 CREATE ALGORITHM=TMPTABLE VIEW mysqltest.v4 AS select (mysqltest.t2.a + 1) AS `c`,(mysqltest.t2.b + 1) AS `d` from mysqltest.t2 +revoke all privileges on mysqltest.* from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; +set GLOBAL query_cache_size=1355776; +flush status; +create table t1 (a int, b int); +create view v1 (c,d) as select sql_no_cache a,b from t1; +create view v2 (c,d) as select a+rand(),b from t1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from v1; +c d +select * from v2; +c d +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from v1; +c d +select * from v2; +c d +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +drop view v1,v2; +set query_cache_type=demand; +flush status; +create view v1 (c,d) as select sql_cache a,b from t1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from v1; +c d +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +a b +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from v1; +c d +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +select * from t1; +a b +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +drop view v1; +set query_cache_type=default; +drop table t1; +set GLOBAL query_cache_size=default; +create table t1 (a int); +insert into t1 values (1), (2), (3), (1), (2), (3); +create view v1 as select distinct a from t1; +select * from v1; +a +1 +2 +3 +explain select * from v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 3 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 6 Using temporary +select * from t1; +a +1 +2 +3 +1 +2 +3 +drop view v1; +drop table t1; +create table t1 (a int); +create view v1 as select distinct a from t1 WITH CHECK OPTION; +create view v2 as select distinct a from t1 WITH CASCADED CHECK OPTION; +create view v3 as select distinct a from t1 WITH LOCAL CHECK OPTION; +drop view v3 RESTRICT; +drop view v2 CASCADE; +drop view v1; +drop table t1; +create table t1 (a int, b int); +insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); +create view v1 (c) as select b+1 from t1; +select test.c from v1 test; +c +3 +4 +5 +6 +11 +create algorithm=temptable view v2 (c) as select b+1 from t1; +select test.c from v2 test; +c +3 +4 +5 +6 +11 +select test1.* from v1 test1, v2 test2 where test1.c=test2.c; +c +3 +4 +5 +6 +11 +select test2.* from v1 test1, v2 test2 where test1.c=test2.c; +c +3 +4 +5 +6 +11 +drop table t1; +drop view v1,v2; +create table t1 (a int); +insert into t1 values (1), (2), (3), (4); +create view v1 as select a+1 from t1 order by 1 desc limit 2; +select * from v1; +a+1 +5 +4 +explain select * from v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 +2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using filesort +drop view v1; +drop table t1; +create table t1 (a int); +insert into t1 values (1), (2), (3), (4); +create view v1 as select a+1 from t1; +create table t2 select * from v1; +show columns from t2; +Field Type Null Key Default Extra +a+1 bigint(17) YES NULL +select * from t2; +a+1 +2 +3 +4 +5 +drop view v1; +drop table t1,t2; +create table t1 (a int, b int, primary key(a)); +insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +update v1 set c=a+c; +ERROR HY000: Column 'c' is not updatable +update v2 set a=a+c; +ERROR HY000: The target table v2 of the UPDATE is not updatable +update v1 set a=a+c; +select * from v1; +a c +13 3 +24 4 +35 5 +46 6 +61 11 +select * from t1; +a b +13 2 +24 3 +35 4 +46 5 +61 10 +drop table t1; +drop view v1,v2; +create table t1 (a int, b int, primary key(a)); +insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create table t2 (x int); +insert into t2 values (10), (20); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +update t2,v1 set v1.c=v1.a+v1.c where t2.x=v1.a; +ERROR HY000: Column 'c' is not updatable +update t2,v2 set v2.a=v2.v2.a+c where t2.x=v2.a; +ERROR HY000: The target table v2 of the UPDATE is not updatable +update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.a; +select * from v1; +a c +13 3 +24 4 +30 5 +40 6 +50 11 +select * from t1; +a b +13 2 +24 3 +30 4 +40 5 +50 10 +drop table t1,t2; +drop view v1,v2; +create database mysqltest; +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create table mysqltest.t2 (x int); +insert into mysqltest.t2 values (3), (4), (5), (6); +create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1; +create view mysqltest.v3 (a,c) as select a, b+1 from mysqltest.t1; +grant update (a) on mysqltest.v2 to mysqltest_1@localhost; +grant update on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; +use mysqltest; +update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c; +select * from t1; +a b +13 2 +24 3 +35 4 +46 5 +50 10 +update v1 set a=a+c; +select * from t1; +a b +16 2 +28 3 +40 4 +52 5 +61 10 +update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c; +select * from t1; +a b +16 2 +31 3 +44 4 +57 5 +61 10 +update v2 set a=a+c; +select * from t1; +a b +18 2 +34 3 +48 4 +62 5 +71 10 +update t2,v2 set v2.c=v2.a+v2.c where t2.x=v2.c; +ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'c' in table 'v2' +update v2 set c=a+c; +ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'c' in table 'v2' +update t2,v3 set v3.a=v3.a+v3.c where t2.x=v3.c; +ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'a' in table 'v3' +update v3 set a=a+c; +ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 'v3' +use test; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; +create table t1 (a int, b int, primary key(b)); +insert into t1 values (1,20), (2,30), (3,40), (4,50), (5,100); +create view v1 (c) as select b from t1 where a<3; +select * from v1; +c +20 +30 +explain extended select * from v1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where +Warnings: +Note 1003 select test.t1.b AS `c` from test.v1 where (test.t1.a < 3) +update v1 set c=c+1; +select * from t1; +a b +1 21 +2 31 +3 40 +4 50 +5 100 +create view v2 (c) as select b from t1 where a>=3; +select * from v1, v2; +c c +21 40 +31 40 +21 50 +31 50 +21 100 +31 100 +drop view v1, v2; +drop table t1; +create table t1 (a int, b int, primary key(a)); +insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +delete from v2 where c < 4; +ERROR HY000: The target table v2 of the DELETE is not updatable +delete from v1 where c < 4; +select * from v1; +a c +2 4 +3 5 +4 6 +5 11 +select * from t1; +a b +2 3 +3 4 +4 5 +5 10 +drop table t1; +drop view v1,v2; +create table t1 (a int, b int, primary key(a)); +insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create table t2 (x int); +insert into t2 values (1), (2), (3), (4); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +delete v2 from t2,v2 where t2.x=v2.a; +ERROR HY000: The target table v2 of the DELETE is not updatable +delete v1 from t2,v1 where t2.x=v1.a; +select * from v1; +a c +5 11 +select * from t1; +a b +5 10 +drop table t1,t2; +drop view v1,v2; +create database mysqltest; +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create table mysqltest.t2 (x int); +insert into mysqltest.t2 values (3), (4), (5), (6); +create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b+1 from mysqltest.t1; +grant delete on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; +use mysqltest; +delete from v1 where c < 4; +select * from t1; +a b +2 3 +3 4 +4 5 +5 10 +delete v1 from t2,v1 where t2.x=v1.c; +select * from t1; +a b +5 10 +delete v2 from t2,v2 where t2.x=v2.c; +ERROR 42000: delete command denied to user 'mysqltest_1'@'localhost' for table 'v2' +delete from v2 where c < 4; +ERROR 42000: delete command denied to user 'mysqltest_1'@'localhost' for table 'v2' +use test; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2), (30,4,-3), (40,5,-4), (50,10,-5); +create view v1 (x,y) as select a, b from t1; +create view v2 (x,y) as select a, c from t1; +set sql_updatable_view_key=YES; +update v1 set x=x+1; +update v2 set x=x+1; +ERROR HY000: The target table v2 of the UPDATE is not updatable +set sql_updatable_view_key=LIMIT1; +update v1 set x=x+1; +update v2 set x=x+1; +Warnings: +Note 1353 View being update does not have complete key of underlying table in it +update v1 set x=x+1 limit 1; +update v2 set x=x+1 limit 1; +ERROR HY000: The target table v2 of the UPDATE is not updatable +set sql_updatable_view_key=NO; +update v1 set x=x+1 limit 1; +update v2 set x=x+1 limit 1; +Warnings: +Note 1353 View being update does not have complete key of underlying table in it +set sql_updatable_view_key=DEFAULT; +select * from t1; +a b c +16 2 -1 +23 3 -2 +33 4 -3 +43 5 -4 +53 10 -5 +drop table t1; +drop view v1,v2; +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2); +create view v1 (x,y,z) as select c, b, a from t1; +create view v2 (x,y) as select b, a from t1; +create view v3 (x,y,z) as select b, a, b from t1; +create view v4 (x,y,z) as select c+1, b, a from t1; +create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; +insert into v3 values (-60,4,30); +ERROR HY000: The target table v3 of the INSERT is not updatable +insert into v4 values (-60,4,30); +ERROR HY000: The target table v4 of the INSERT is not updatable +insert into v5 values (-60,4,30); +ERROR HY000: The target table v5 of the INSERT is not updatable +insert into v1 values (-60,4,30); +insert into v1 (z,y,x) values (50,6,-100); +insert into v2 values (5,40); +select * from t1; +a b c +10 2 -1 +20 3 -2 +30 4 -60 +50 6 -100 +40 5 NULL +drop table t1; +drop view v1,v2,v3,v4,v5; +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2); +create table t2 (a int, b int, c int, primary key(a,b)); +insert into t2 values (30,4,-60); +create view v1 (x,y,z) as select c, b, a from t1; +create view v2 (x,y) as select b, a from t1; +create view v3 (x,y,z) as select b, a, b from t1; +create view v4 (x,y,z) as select c+1, b, a from t1; +create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; +insert into v3 select c, b, a from t2; +ERROR HY000: The target table v3 of the INSERT is not updatable +insert into v4 select c, b, a from t2; +ERROR HY000: The target table v4 of the INSERT is not updatable +insert into v5 select c, b, a from t2; +ERROR HY000: The target table v5 of the INSERT is not updatable +insert into v1 select c, b, a from t2; +insert into v1 (z,y,x) select a+20,b+2,-100 from t2; +insert into v2 select b+1, a+10 from t2; +select * from t1; +a b c +10 2 -1 +20 3 -2 +30 4 -60 +50 6 -100 +40 5 NULL +drop table t1; +drop view v1,v2,v3,v4,v5; +create database mysqltest; +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (1,2), (2,3); +create table mysqltest.t2 (x int, y int); +insert into mysqltest.t2 values (3,4); +create view mysqltest.v1 (a,c) as select a, b from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1; +grant insert on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; +use mysqltest; +insert into v1 values (5,6); +select * from t1; +a b +1 2 +2 3 +5 6 +insert into v1 select x,y from t2; +select * from t1; +a b +1 2 +2 3 +5 6 +3 4 +insert into v2 values (5,6); +ERROR 42000: insert command denied to user 'mysqltest_1'@'localhost' for table 'v2' +insert into v2 select x,y from t2; +ERROR 42000: insert command denied to user 'mysqltest_1'@'localhost' for table 'v2' +use test; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3); +create view v1 (x) as select a from t1 where a > 1; +select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x); +a x +1 NULL +2 2 +3 3 +drop table t1; +drop view v1; +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3), (200); +create view v1 (x) as select a from t1 where a > 1; +create view v2 (y) as select x from v1 where x < 100; +select * from v2; +x +2 +3 +drop table t1; +drop view v1,v2; +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3), (200); +create ALGORITHM=TEMPTABLE view v1 (x) as select a from t1; +create view v2 (y) as select x from v1; +update v2 set y=10 where y=2; +ERROR HY000: The target table v2 of the UPDATE is not updatable +drop table t1; +drop view v1,v2; +create table t1 (a int not null auto_increment, b int not null, primary key(a), unique(b)); +create view v1 (x) as select b from t1; +insert into v1 values (1); +select last_insert_id(); +last_insert_id() +0 +insert into t1 (b) values (2); +select last_insert_id(); +last_insert_id() +2 +select * from t1; +a b +1 1 +2 2 +drop view v1; +drop table t1; +create database mysqltest; +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); +grant update on mysqltest.t1 to mysqltest_1@localhost; +grant update(b) on mysqltest.t2 to mysqltest_1@localhost; +grant create view,update on test.* to mysqltest_1@localhost; +create view v1 as select * from mysqltest.t1; +create view v2 as select b from mysqltest.t2; +create view mysqltest.v1 as select * from mysqltest.t1; +ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v1' +create view v3 as select a from mysqltest.t2; +ERROR 42000: ANY command denied to user 'mysqltest_1'@'localhost' for column 'a' in table 't2' +create table mysqltest.v3 (b int); +grant create view on mysqltest.v3 to mysqltest_1@localhost; +drop table mysqltest.v3; +create view mysqltest.v3 as select b from mysqltest.t2; +ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 'v3' +create table mysqltest.v3 (b int); +grant create view, update on mysqltest.v3 to mysqltest_1@localhost; +drop table mysqltest.v3; +create view mysqltest.v3 as select b from mysqltest.t2; +grant select(b) on mysqltest.v3 to mysqltest_1@localhost; +drop view mysqltest.v3; +create view mysqltest.v3 as select b from mysqltest.t2; +ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v3' +create view v4 as select b+1 from mysqltest.t2; +ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 't2' +grant create view,update,select on test.* to mysqltest_1@localhost; +create view v4 as select b+1 from mysqltest.t2; +ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 't2' +grant update,select(b) on mysqltest.t2 to mysqltest_1@localhost; +create view v4 as select b+1 from mysqltest.t2; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; +drop view v1,v2; diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test index 13c83fc9012..1f34b5be5fc 100644 --- a/mysql-test/t/system_mysql_db_fix.test +++ b/mysql-test/t/system_mysql_db_fix.test @@ -68,15 +68,6 @@ INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N',' -- disable_query_log -DROP TABLE db; -DROP TABLE host; -DROP TABLE user; -DROP TABLE func; -DROP TABLE tables_priv; -DROP TABLE columns_priv; -DROP TABLE help_category; -DROP TABLE help_keyword; -DROP TABLE help_relation; -DROP TABLE help_topic; +DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type; -- enable_query_log diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test new file mode 100644 index 00000000000..a37de0904c5 --- /dev/null +++ b/mysql-test/t/view.test @@ -0,0 +1,853 @@ +--disable_warnings +drop table if exists t1,t2; +drop view if exists v1,v2,v3,v4,v5,v6; +drop database if exists mysqltest; +--enable_warnings +use test; + +# +# some basic test of views and its functionality +# + +# create view on unexistence table +-- error 1146 +create view v1 (c,d) as select a,b from t1; + +create temporary table t1 (a int, b int); +#view on temporary table +-- error 1350 +create view v1 (c) as select b+1 from t1; +drop table t1; + +create table t1 (a int, b int); +insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); + +#view with variable +-- error 1349 +create view v1 (c,d) as select a,b+@@global.max_user_connections from t1; + +# simple view +create view v1 (c) as select b+1 from t1; +select c from v1; + +#tamporary table should not shade (hide) table of view +create temporary table t1 (a int, b int); +# this is empty +select * from t1; +# but this based on normal t1 +select c from v1; +show create table v1; +show create view v1; +-- error 1345 +show create view t1; +drop table t1; + +# try to use fields from underlaid table +-- error 1054 +select a from v1; +-- error 1054 +select v1.a from v1; +-- error 1054 +select b from v1; +-- error 1054 +select v1.b from v1; + +# view with different algorithms (explain out put are differ) +explain extended select c from v1; +create algorithm=temptable view v2 (c) as select b+1 from t1; +show create table v2; +select c from v2; +explain extended select c from v2; + +# try to use underlaid table fields in VIEW creation process +-- error 1054 +create view v3 (c) as select a+1 from v1; +-- error 1054 +create view v3 (c) as select b+1 from v1; + + +# VIEW on VIEW test with mixing different algorithms on different order +create view v3 (c) as select c+1 from v1; +select c from v3; +explain extended select c from v3; +create algorithm=temptable view v4 (c) as select c+1 from v2; +select c from v4; +explain extended select c from v4; +create view v5 (c) as select c+1 from v2; +select c from v5; +explain extended select c from v5; +create algorithm=temptable view v6 (c) as select c+1 from v1; +select c from v6; +explain extended select c from v6; + +# show table/table status test +show tables; +--replace_column 12 # 13 # +show table status; + +drop view v1,v2,v3,v4,v5,v6; + +# +# alter/create view test +# + +# view with subqueries of different types +create view v1 (c,d,e,f) as select a,b, +a in (select a+2 from t1), a = all (select a from t1) from t1; +create view v2 as select c, d from v1; +select * from v1; +select * from v2; + +# try to create VIEW with name of existing VIEW +-- error 1050 +create view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1; + +# 'or replace' should work in this case +create or replace view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1; + +# try to ALTER unexisting VIEW +drop view v2; +-- error 1146 +alter view v2 as select c, d from v1; + +# 'or replace' on unexisting view +create or replace view v2 as select c, d from v1; + +# alter view on existing view +alter view v1 (c,d) as select a,max(b) from t1 group by a; + +# check that created view works +select * from v1; +select * from v2; + +# simple test of grants +grant create view on test.* to test@localhost; +show grants for test@localhost; +revoke create view on test.* from test@localhost; +show grants for test@localhost; + +#try to drop unexisten VIEW +-- error 1051 +drop view v100; + +#try to drop table with DROP VIEW +-- error 1345 +drop view t1; + +#try to drop VIEW with DROP TABLE +-- error 1051 +drop table v1; + +#try to drop table with DROP VIEW + +drop view v1,v2; +drop table t1; + +# +# outer left join with merged views +# +create table t1 (a int); +insert into t1 values (1), (2), (3); + +create view v1 (a) as select a+1 from t1; +create view v2 (a) as select a-1 from t1; + +select * from t1 natural left join v1; +select * from v2 natural left join t1; +select * from v2 natural left join v1; + +drop view v1, v2; +drop table t1; + + +# +# grant create view test +# +connect (root,localhost,root,,test); +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); + +grant select on mysqltest.t1 to mysqltest_1@localhost; +grant create view,select on test.* to mysqltest_1@localhost; + +connect (user1,localhost,mysqltest_1,,test); +connection user1; + +create view v1 as select * from mysqltest.t1; +# no CRETE VIEW privilege +-- error 1142 +create view mysqltest.v2 as select * from mysqltest.t1; +# no SELECT privilege +-- error 1142 +create view v2 as select * from mysqltest.t2; + +connection root; +revoke all privileges on mysqltest.t1 from mysqltest_1@localhost; +revoke all privileges on test.* from mysqltest_1@localhost; + +drop database mysqltest; +drop view test.v1; + +# +# grants per columns +# +# MERGE algorithm +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int); +create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +grant select (c) on mysqltest.v1 to mysqltest_1@localhost; + +connection user1; +select c from mysqltest.v1; +# there are not privilege ob column 'd' +-- error 1143 +select d from mysqltest.v1; + +connection root; +revoke all privileges on mysqltest.v1 from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; + +# TEMPORARY TABLE algorithm +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int); +create algorithm=temptable view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +grant select (c) on mysqltest.v1 to mysqltest_1@localhost; + +connection user1; +select c from mysqltest.v1; +# there are not privilege ob column 'd' +-- error 1143 +select d from mysqltest.v1; + +connection root; +revoke all privileges on mysqltest.v1 from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; + +# +# EXPLAIN rights +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings +#prepare views and tables +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); +create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1; +create algorithm=temptable view mysqltest.v2 (c,d) as select a+1,b+1 from mysqltest.t1; +create view mysqltest.v3 (c,d) as select a+1,b+1 from mysqltest.t2; +create algorithm=temptable view mysqltest.v4 (c,d) as select a+1,b+1 from mysqltest.t2; +grant select on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.v2 to mysqltest_1@localhost; +grant select on mysqltest.v3 to mysqltest_1@localhost; +grant select on mysqltest.v4 to mysqltest_1@localhost; + +connection user1; +# all selects works +select c from mysqltest.v1; +select c from mysqltest.v2; +select c from mysqltest.v3; +select c from mysqltest.v4; +# test of show coluns +show columns from mysqltest.v1; +show columns from mysqltest.v2; +# but explain/show do not +-- error 1343 +explain select c from mysqltest.v1; +-- error 1343 +show create table mysqltest.v1; +-- error 1343 +explain select c from mysqltest.v2; +-- error 1343 +show create table mysqltest.v2; +-- error 1343 +explain select c from mysqltest.v3; +-- error 1343 +show create table mysqltest.v3; +-- error 1343 +explain select c from mysqltest.v4; +-- error 1343 +show create table mysqltest.v4; + +# allow to see one of underlaing table +connection root; +grant select on mysqltest.t1 to mysqltest_1@localhost; +connection user1; +# EXPLAIN of view on above table works +explain select c from mysqltest.v1; +show create table mysqltest.v1; +explain select c from mysqltest.v2; +show create table mysqltest.v2; +# but other EXPLAINs do not +-- error 1343 +explain select c from mysqltest.v3; +-- error 1343 +show create table mysqltest.v3; +-- error 1343 +explain select c from mysqltest.v4; +-- error 1343 +show create table mysqltest.v4; + +# allow to see any view in mysqltest database +connection root; +grant show view on mysqltest.* to mysqltest_1@localhost; +connection user1; +explain select c from mysqltest.v1; +show create table mysqltest.v1; +explain select c from mysqltest.v2; +show create table mysqltest.v2; +explain select c from mysqltest.v3; +show create table mysqltest.v3; +explain select c from mysqltest.v4; +show create table mysqltest.v4; + +connection root; +revoke all privileges on mysqltest.* from mysqltest_1@localhost; +delete from mysql.user where user='mysqltest_1'; +drop database mysqltest; + +# +# QUERY CHECHE options for VIEWs +# +set GLOBAL query_cache_size=1355776; +flush status; +create table t1 (a int, b int); + +# queries with following views should not be in query cache +create view v1 (c,d) as select sql_no_cache a,b from t1; +create view v2 (c,d) as select a+rand(),b from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from v1; +select * from v2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from v1; +select * from v2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +drop view v1,v2; + +# SQL_CACHE option +set query_cache_type=demand; +flush status; +# query with view will be cached, but direct acess to table will not +create view v1 (c,d) as select sql_cache a,b from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from v1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from v1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +drop view v1; +set query_cache_type=default; + +drop table t1; +set GLOBAL query_cache_size=default; + + +# +# DISTINCT option for VIEW +# +create table t1 (a int); +insert into t1 values (1), (2), (3), (1), (2), (3); +create view v1 as select distinct a from t1; +select * from v1; +explain select * from v1; +select * from t1; +drop view v1; +drop table t1; + +# +# syntax compatibility +# +create table t1 (a int); +create view v1 as select distinct a from t1 WITH CHECK OPTION; +create view v2 as select distinct a from t1 WITH CASCADED CHECK OPTION; +create view v3 as select distinct a from t1 WITH LOCAL CHECK OPTION; +drop view v3 RESTRICT; +drop view v2 CASCADE; +drop view v1; +drop table t1; + +# +# aliases +# +create table t1 (a int, b int); +insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); +create view v1 (c) as select b+1 from t1; +select test.c from v1 test; +create algorithm=temptable view v2 (c) as select b+1 from t1; +select test.c from v2 test; +select test1.* from v1 test1, v2 test2 where test1.c=test2.c; +select test2.* from v1 test1, v2 test2 where test1.c=test2.c; +drop table t1; +drop view v1,v2; + +# +# LIMIT clasuse test +# +create table t1 (a int); +insert into t1 values (1), (2), (3), (4); +create view v1 as select a+1 from t1 order by 1 desc limit 2; +select * from v1; +explain select * from v1; +drop view v1; +drop table t1; + +# +# CREATE ... SELECT view test +# +create table t1 (a int); +insert into t1 values (1), (2), (3), (4); +create view v1 as select a+1 from t1; +create table t2 select * from v1; +show columns from t2; +select * from t2; +drop view v1; +drop table t1,t2; + +# +# simple view + simple update +# +create table t1 (a int, b int, primary key(a)); +insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +# try to update expression +-- error 1346 +update v1 set c=a+c; +# try to update VIEW with forced TEMPORARY TABLE algorithm +-- error 1288 +update v2 set a=a+c; +# updatable field of updateable view +update v1 set a=a+c; +select * from v1; +select * from t1; +drop table t1; +drop view v1,v2; + +# +# simple view + simple multi-update +# +create table t1 (a int, b int, primary key(a)); +insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create table t2 (x int); +insert into t2 values (10), (20); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +# try to update expression +-- error 1346 +update t2,v1 set v1.c=v1.a+v1.c where t2.x=v1.a; +# try to update VIEW with forced TEMPORARY TABLE algorithm +-- error 1288 +update t2,v2 set v2.a=v2.v2.a+c where t2.x=v2.a; +# updatable field of updateable view +update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.a; +select * from v1; +select * from t1; +drop table t1,t2; +drop view v1,v2; + +# +# UPDATE privileges on VIEW columns and whole VIEW +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (10,2), (20,3), (30,4), (40,5), (50,10); +create table mysqltest.t2 (x int); +insert into mysqltest.t2 values (3), (4), (5), (6); +create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1; +create view mysqltest.v3 (a,c) as select a, b+1 from mysqltest.t1; + +grant update (a) on mysqltest.v2 to mysqltest_1@localhost; +grant update on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; + +connection user1; +use mysqltest; +# update with rights on VIEW column +update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c; +select * from t1; +update v1 set a=a+c; +select * from t1; +# update with rights on whole VIEW +update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c; +select * from t1; +update v2 set a=a+c; +select * from t1; +# no rights on column +-- error 1143 +update t2,v2 set v2.c=v2.a+v2.c where t2.x=v2.c; +-- error 1143 +update v2 set c=a+c; +# no rights for view +-- error 1143 +update t2,v3 set v3.a=v3.a+v3.c where t2.x=v3.c; +-- error 1142 +update v3 set a=a+c; + +use test; +connection root; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; + +# +# MEREGE VIEW with WHERE clause +# +create table t1 (a int, b int, primary key(b)); +insert into t1 values (1,20), (2,30), (3,40), (4,50), (5,100); +create view v1 (c) as select b from t1 where a<3; +# simple select and explaint to be sure that it is MERGE +select * from v1; +explain extended select * from v1; +# update test +update v1 set c=c+1; +select * from t1; +# join of such VIEWs test +create view v2 (c) as select b from t1 where a>=3; +select * from v1, v2; +drop view v1, v2; +drop table t1; + +# +# simple view + simple delete +# +create table t1 (a int, b int, primary key(a)); +insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +# try to update VIEW with forced TEMPORARY TABLE algorithm +-- error 1288 +delete from v2 where c < 4; +# updatable field of updateable view +delete from v1 where c < 4; +select * from v1; +select * from t1; +drop table t1; +drop view v1,v2; + +# +# simple view + simple multi-delete +# +create table t1 (a int, b int, primary key(a)); +insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create table t2 (x int); +insert into t2 values (1), (2), (3), (4); +create view v1 (a,c) as select a, b+1 from t1; +create algorithm=temptable view v2 (a,c) as select a, b+1 from t1; +# try to update VIEW with forced TEMPORARY TABLE algorithm +-- error 1288 +delete v2 from t2,v2 where t2.x=v2.a; +# updatable field of updateable view +delete v1 from t2,v1 where t2.x=v1.a; +select * from v1; +select * from t1; +drop table t1,t2; +drop view v1,v2; + +# +# DELETE privileges on VIEW +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (1,2), (2,3), (3,4), (4,5), (5,10); +create table mysqltest.t2 (x int); +insert into mysqltest.t2 values (3), (4), (5), (6); +create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b+1 from mysqltest.t1; + +grant delete on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; + +connection user1; +use mysqltest; +# update with rights on VIEW column +delete from v1 where c < 4; +select * from t1; +delete v1 from t2,v1 where t2.x=v1.c; +select * from t1; +# no rights for view +-- error 1142 +delete v2 from t2,v2 where t2.x=v2.c; +-- error 1142 +delete from v2 where c < 4; + +use test; +connection root; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; + +# +# key presence check +# +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2), (30,4,-3), (40,5,-4), (50,10,-5); +create view v1 (x,y) as select a, b from t1; +create view v2 (x,y) as select a, c from t1; +set sql_updatable_view_key=YES; +update v1 set x=x+1; +-- error 1288 +update v2 set x=x+1; +set sql_updatable_view_key=LIMIT1; +update v1 set x=x+1; +update v2 set x=x+1; +update v1 set x=x+1 limit 1; +-- error 1288 +update v2 set x=x+1 limit 1; +set sql_updatable_view_key=NO; +update v1 set x=x+1 limit 1; +update v2 set x=x+1 limit 1; +set sql_updatable_view_key=DEFAULT; +select * from t1; +drop table t1; +drop view v1,v2; + +# +# simple insert +# +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2); +create view v1 (x,y,z) as select c, b, a from t1; +create view v2 (x,y) as select b, a from t1; +create view v3 (x,y,z) as select b, a, b from t1; +create view v4 (x,y,z) as select c+1, b, a from t1; +create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; +# try insert to VIEW with fields duplicate +-- error 1288 +insert into v3 values (-60,4,30); +# try insert to VIEW with expression in SELECT list +-- error 1288 +insert into v4 values (-60,4,30); +# try insert to VIEW using temporary table algorithm +-- error 1288 +insert into v5 values (-60,4,30); +insert into v1 values (-60,4,30); +insert into v1 (z,y,x) values (50,6,-100); +insert into v2 values (5,40); +select * from t1; +drop table t1; +drop view v1,v2,v3,v4,v5; + +# +# insert ... select +# +create table t1 (a int, b int, c int, primary key(a,b)); +insert into t1 values (10,2,-1), (20,3,-2); +create table t2 (a int, b int, c int, primary key(a,b)); +insert into t2 values (30,4,-60); +create view v1 (x,y,z) as select c, b, a from t1; +create view v2 (x,y) as select b, a from t1; +create view v3 (x,y,z) as select b, a, b from t1; +create view v4 (x,y,z) as select c+1, b, a from t1; +create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; +# try insert to VIEW with fields duplicate +-- error 1288 +insert into v3 select c, b, a from t2; +# try insert to VIEW with expression in SELECT list +-- error 1288 +insert into v4 select c, b, a from t2; +# try insert to VIEW using temporary table algorithm +-- error 1288 +insert into v5 select c, b, a from t2; +insert into v1 select c, b, a from t2; +insert into v1 (z,y,x) select a+20,b+2,-100 from t2; +insert into v2 select b+1, a+10 from t2; +select * from t1; +drop table t1; +drop view v1,v2,v3,v4,v5; + +# +# insert privileges on VIEW +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int, primary key(a)); +insert into mysqltest.t1 values (1,2), (2,3); +create table mysqltest.t2 (x int, y int); +insert into mysqltest.t2 values (3,4); +create view mysqltest.v1 (a,c) as select a, b from mysqltest.t1; +create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1; + +grant insert on mysqltest.v1 to mysqltest_1@localhost; +grant select on mysqltest.* to mysqltest_1@localhost; + +connection user1; +use mysqltest; +# update with rights on VIEW column +insert into v1 values (5,6); +select * from t1; +insert into v1 select x,y from t2; +select * from t1; +# no rights for view +-- error 1142 +insert into v2 values (5,6); +-- error 1142 +insert into v2 select x,y from t2; + +use test; +connection root; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; + +# +# outer join based on VIEW with WHERE clause +# +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3); +create view v1 (x) as select a from t1 where a > 1; +select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x); +drop table t1; +drop view v1; + +# +# merging WHERE condition on VIEW on VIEW +# +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3), (200); +create view v1 (x) as select a from t1 where a > 1; +create view v2 (y) as select x from v1 where x < 100; +select * from v2; +drop table t1; +drop view v1,v2; + +# +# VIEW on non-updatable view +# +create table t1 (a int, primary key(a)); +insert into t1 values (1), (2), (3), (200); +create ALGORITHM=TEMPTABLE view v1 (x) as select a from t1; +create view v2 (y) as select x from v1; +-- error 1288 +update v2 set y=10 where y=2; +drop table t1; +drop view v1,v2; + +# +# auto_increment field out of VIEW +# +create table t1 (a int not null auto_increment, b int not null, primary key(a), unique(b)); +create view v1 (x) as select b from t1; +insert into v1 values (1); +select last_insert_id(); +insert into t1 (b) values (2); +select last_insert_id(); +select * from t1; +drop view v1; +drop table t1; + +# +# test of CREATE VIEW privileges if we have limited privileges +# +connection root; +--disable_warnings +create database mysqltest; +--enable_warnings + +create table mysqltest.t1 (a int, b int); +create table mysqltest.t2 (a int, b int); + +grant update on mysqltest.t1 to mysqltest_1@localhost; +grant update(b) on mysqltest.t2 to mysqltest_1@localhost; +grant create view,update on test.* to mysqltest_1@localhost; + +connection user1; + +create view v1 as select * from mysqltest.t1; +create view v2 as select b from mysqltest.t2; +# There are not rights on mysqltest.v1 +--error 1142 +create view mysqltest.v1 as select * from mysqltest.t1; +# There are not any rights on mysqltest.t2.a +-- error 1143 +create view v3 as select a from mysqltest.t2; + +# give CRETEA VIEW privileges but without any privileges for result colemn +connection root; +create table mysqltest.v3 (b int); +grant create view on mysqltest.v3 to mysqltest_1@localhost; +drop table mysqltest.v3; +connection user1; +-- error 1143 +create view mysqltest.v3 as select b from mysqltest.t2; + +# give UPDATE privileges -> create works +connection root; +create table mysqltest.v3 (b int); +grant create view, update on mysqltest.v3 to mysqltest_1@localhost; +drop table mysqltest.v3; +connection user1; +create view mysqltest.v3 as select b from mysqltest.t2; + + +# If give other privileges for VIEW then underlaying table have => +# creation prohibited +connection root; +grant select(b) on mysqltest.v3 to mysqltest_1@localhost; +drop view mysqltest.v3; +connection user1; +-- error 1142 +create view mysqltest.v3 as select b from mysqltest.t2; + +# Expression need select privileges +-- error 1143 +create view v4 as select b+1 from mysqltest.t2; + +connection root; +grant create view,update,select on test.* to mysqltest_1@localhost; +connection user1; +-- error 1143 +create view v4 as select b+1 from mysqltest.t2; + +connection root; +grant update,select(b) on mysqltest.t2 to mysqltest_1@localhost; +connection user1; +create view v4 as select b+1 from mysqltest.t2; + +connection root; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost; +drop database mysqltest; +drop view v1,v2; + diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 83b802ee567..ff872338c0f 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -144,6 +144,19 @@ alter table user comment='Users and global privileges'; alter table func comment='User defined functions'; alter table tables_priv comment='Table privileges'; alter table columns_priv comment='Column privileges'; +# +# Create VIEWs privrlages (v5.0) +# +ALTER TABLE db ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Lock_tables_priv; +ALTER TABLE host ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Lock_tables_priv; +ALTER TABLE user ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Repl_client_priv; + +# +# Show VIEWs privrlages (v5.0) +# +ALTER TABLE db ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv; +ALTER TABLE host ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv; +ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv; # # Create some possible missing tables diff --git a/sql/Makefile.am b/sql/Makefile.am index a1eada6e3d2..175cc3786cf 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,7 +61,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ spatial.h gstream.h client_settings.h tzfile.h \ tztime.h examples/ha_example.h examples/ha_archive.h \ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ - parse_file.h + parse_file.h sql_view.h mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -90,6 +90,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ slave.cc sql_repl.cc sql_union.cc sql_derived.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc \ + sql_olap.cc sql_view.cc \ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ tztime.cc my_time.c \ examples/ha_example.cc examples/ha_archive.cc \ diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 9aa6d039efb..895d025d6fc 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -347,7 +347,7 @@ void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) create_info->merge_list.elements++; (*create_info->merge_list.next) = (byte*) ptr; - create_info->merge_list.next= (byte**) &ptr->next; + create_info->merge_list.next= (byte**) &ptr->next_local; } *create_info->merge_list.next=0; } @@ -375,7 +375,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form, if (!(table_names= (char**) thd->alloc((create_info->merge_list.elements+1)* sizeof(char*)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); - for (pos=table_names ; tables ; tables=tables->next) + for (pos= table_names; tables; tables= tables->next_local) { char *table_name; TABLE **tbl= 0; diff --git a/sql/item.cc b/sql/item.cc index 8dfb8fb5587..0ca557a8fc4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -23,6 +23,7 @@ #include <m_ctype.h> #include "my_dir.h" #include "sp_rcontext.h" +#include "sql_acl.h" static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, @@ -350,7 +351,8 @@ bool DTCollation::aggregate(DTCollation &dt) } Item_field::Item_field(Field *f) - :Item_ident(NullS, f->table_name, f->field_name) + :Item_ident(NullS, f->table_name, f->field_name), + have_privileges(0), any_privileges(0) { set_field(f); collation.set(DERIVATION_IMPLICIT); @@ -359,7 +361,8 @@ Item_field::Item_field(Field *f) Item_field::Item_field(THD *thd, Field *f) :Item_ident(NullS, thd->strdup(f->table_name), - thd->strdup(f->field_name)) + thd->strdup(f->field_name)), + have_privileges(0), any_privileges(0) { set_field(f); collation.set(DERIVATION_IMPLICIT); @@ -370,7 +373,9 @@ Item_field::Item_field(THD *thd, Field *f) Item_field::Item_field(THD *thd, Item_field *item) :Item_ident(thd, item), field(item->field), - result_field(item->result_field) + result_field(item->result_field), + have_privileges(item->have_privileges), + any_privileges(item->any_privileges) { collation.set(DERIVATION_IMPLICIT); } @@ -1223,10 +1228,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { - TABLE_LIST *where= 0; bool upward_lookup= 0; Field *tmp= (Field *)not_found_field; - if ((tmp= find_field_in_tables(thd, this, tables, &where, 0)) == + if ((tmp= find_field_in_tables(thd, this, tables, ref, 0, + !any_privileges)) == not_found_field) { /* @@ -1259,18 +1264,19 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) { // it is primary INSERT st_select_lex => skip first table resolving - table_list= table_list->next; + table_list= table_list->next_local; } Item_subselect *prev_subselect_item= prev_unit->item; if ((tmp= find_field_in_tables(thd, this, - table_list, &where, - 0)) != not_found_field) + table_list, ref, + 0, 1)) != not_found_field) { - if (!tmp) - return -1; - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; + if (tmp && tmp != view_ref_found) + { + prev_subselect_item->used_tables_cache|= tmp->table->map; + prev_subselect_item->const_item_cache= 0; + } break; } if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && @@ -1310,7 +1316,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) else { // Call to report error - find_field_in_tables(thd, this, tables, &where, 1); + find_field_in_tables(thd, this, tables, ref, 1, 1); } return -1; } @@ -1348,8 +1354,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { Item_ref *rf; *ref= rf= new Item_ref(ref, *ref, - (where->db[0]?where->db:0), - (char *)where->alias, + (cached_table->db[0]?cached_table->db:0), + (char *)cached_table->alias, (char *)field_name); if (!rf) return 1; @@ -1364,7 +1370,20 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) else if (!tmp) return -1; - set_field(tmp); + /* + if it is not expression from merged VIEW we will set this field. + + We can leave expression substituted from view for next PS/SP rexecution + (i.e. do not register this substitution for reverting on cleupup() + (register_item_tree_changing())), because this subtree will be + fix_field'ed during setup_tables()->setup_ancestor() (i.e. before + all other expressions of query, and references on tables which do + not present in query will not make problems. + + Also we suppose that view can't be changed during PS/SP life. + */ + if (tmp != view_ref_found) + set_field(tmp); } else if (thd->set_query_id && field->query_id != thd->query_id) { @@ -1374,6 +1393,36 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) table->used_fields++; table->used_keys.intersect(field->part_of_key); } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (any_privileges) + { + char *db, *tab; + if (cached_table->view) + { + db= cached_table->view_db.str; + tab= cached_table->view_name.str; + } + else + { + db= cached_table->db; + tab= cached_table->real_name; + } + if (!(have_privileges= (get_column_grant(thd, &field->table->grant, + db, tab, field_name) & + VIEW_ANY_ACL))) + { + my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, + ER(ER_COLUMNACCESS_DENIED_ERROR), + MYF(0), + "ANY", + thd->priv_user, + thd->host_or_ip, + field_name, + tab); + return 1; + } + } +#endif fixed= 1; return 0; } @@ -1870,13 +1919,13 @@ bool Item_field::send(Protocol *protocol, String *buffer) Find field in select list having the same name */ -bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) +bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { DBUG_ASSERT(fixed == 0); uint counter; if (!ref) { - TABLE_LIST *where= 0, *table_list; + TABLE_LIST *table_list; bool upward_lookup= 0; SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit(); SELECT_LEX *sl= prev_unit->outer_select(); @@ -1929,14 +1978,17 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) { // it is primary INSERT st_select_lex => skip first table resolving - table_list= table_list->next; + table_list= table_list->next_local; } if ((tmp= find_field_in_tables(thd, this, - table_list, &where, - 0)) != not_found_field) + table_list, reference, + 0, 1)) != not_found_field) { - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; + if (tmp && tmp != view_ref_found) + { + prev_subselect_item->used_tables_cache|= tmp->table->map; + prev_subselect_item->const_item_cache= 0; + } break; } @@ -1975,12 +2027,26 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) else if (tmp != not_found_field) { ref= 0; // To prevent "delete *ref;" on ~Item_erf() of this item - Item_field* fld; - if (!((*reference)= fld= new Item_field(tmp))) - return 1; - register_item_tree_changing(reference); - mark_as_dependent(thd, last, thd->lex->current_select, fld); - return 0; + if (tmp != view_ref_found) + { + Item_field* fld; + if (!((*reference)= fld= new Item_field(tmp))) + return 1; + mark_as_dependent(thd, last, thd->lex->current_select, fld); + return 0; + register_item_tree_changing(reference); + } + /* + We can leave expression substituted from view for next PS/SP + rexecution (i.e. do not register this substitution for reverting + on cleupup() (register_item_tree_changing())), because this + subtree will be fix_field'ed during + setup_tables()->setup_ancestor() (i.e. before all other + expressions of query, and references on tables which do not + present in query will not make problems. + + Also we suppose that view can't be changed during PS/SP life. + */ } else { diff --git a/sql/item.h b/sql/item.h index 3a2cb4bf3f7..758429c0994 100644 --- a/sql/item.h +++ b/sql/item.h @@ -430,6 +430,10 @@ public: void register_item_tree_changing(Item **ref) { changed_during_fix_field= ref; } bool remove_dependence_processor(byte * arg); + + friend bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, + const char *table_name, List_iterator<Item> *it, + bool any_privileges); }; @@ -438,11 +442,18 @@ class Item_field :public Item_ident void set_field(Field *field); public: Field *field,*result_field; + /* + if any_privileges set to TRUE then here real effective privileges will + be stored + */ + uint have_privileges; + /* field need any privileges (for VIEW creation) */ + bool any_privileges; Item_field(const char *db_par,const char *table_name_par, const char *field_name_par) :Item_ident(db_par,table_name_par,field_name_par), - field(0), result_field(0) + field(0), result_field(0), have_privileges(0), any_privileges(0) { collation.set(DERIVATION_IMPLICIT); } // Constructor need to process subselect with temporary tables (see Item) Item_field(THD *thd, Item_field *item); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 91a31502780..33e661ce46e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -990,6 +990,23 @@ Item_in_subselect::select_transformer(JOIN *join) } +Item_subselect::trans_res +Item_in_subselect::no_select_transform() +{ + DBUG_ENTER("Item_in_subselect::no_select_transform"); + // We have execute fix_fields() for left expression + SELECT_LEX *current= thd->lex->current_select, *up; + thd->lex->current_select= up= current->return_after_parsing(); + if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) + { + thd->lex->current_select= current; + DBUG_RETURN(RES_ERROR); + } + thd->lex->current_select= current; + DBUG_RETURN(RES_OK); +} + + void Item_in_subselect::print(String *str) { if (transformed) @@ -1383,7 +1400,7 @@ void subselect_uniquesubquery_engine::exclude() table_map subselect_engine::calc_const_tables(TABLE_LIST *table) { table_map map= 0; - for (; table; table= table->next) + for(; table; table= table->next_local) { TABLE *tbl= table->table; if (tbl && tbl->const_table) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 019d43e4819..d50688e0b58 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -84,6 +84,7 @@ public: null_value= 1; } virtual trans_res select_transformer(JOIN *join); + virtual trans_res no_select_transform() { return RES_OK; } bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -209,7 +210,6 @@ public: Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), abort_on_null(0), transformed(0), upper_not(0) - {} subs_type substype() { return IN_SUBS; } @@ -220,6 +220,7 @@ public: was_null= 0; } trans_res select_transformer(JOIN *join); + trans_res no_select_transform(); trans_res single_value_transformer(JOIN *join, Comp_creator *func); trans_res row_value_transformer(JOIN * join); diff --git a/sql/lex.h b/sql/lex.h index 24a6431adf5..023c4752720 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -65,6 +65,7 @@ static SYMBOL symbols[] = { { "AGAINST", SYM(AGAINST)}, { "AGGREGATE", SYM(AGGREGATE_SYM)}, { "ALL", SYM(ALL)}, + { "ALGORITHM", SYM(ALGORITHM_SYM)}, { "ALTER", SYM(ALTER)}, { "ANALYZE", SYM(ANALYZE_SYM)}, { "AND", SYM(AND_SYM)}, @@ -96,6 +97,7 @@ static SYMBOL symbols[] = { { "CACHE", SYM(CACHE_SYM)}, { "CALL", SYM(CALL_SYM)}, { "CASCADE", SYM(CASCADE)}, + { "CASCADED", SYM(CASCADED)}, { "CASE", SYM(CASE_SYM)}, { "CHANGE", SYM(CHANGE)}, { "CHANGED", SYM(CHANGED)}, @@ -301,6 +303,7 @@ static SYMBOL symbols[] = { { "MEDIUMBLOB", SYM(MEDIUMBLOB)}, { "MEDIUMINT", SYM(MEDIUMINT)}, { "MEDIUMTEXT", SYM(MEDIUMTEXT)}, + { "MERGE", SYM(MERGE_SYM)}, { "MICROSECOND", SYM(MICROSECOND_SYM)}, { "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */ { "MINUTE", SYM(MINUTE_SYM)}, @@ -451,6 +454,7 @@ static SYMBOL symbols[] = { { "TABLES", SYM(TABLES)}, { "TABLESPACE", SYM(TABLESPACE)}, { "TEMPORARY", SYM(TEMPORARY)}, + { "TEMPTABLE", SYM(TEMPTABLE_SYM)}, { "TERMINATED", SYM(TERMINATED)}, { "TEXT", SYM(TEXT_SYM)}, { "THEN", SYM(THEN_SYM)}, @@ -498,6 +502,7 @@ static SYMBOL symbols[] = { { "WHEN", SYM(WHEN_SYM)}, { "WHERE", SYM(WHERE)}, { "WHILE", SYM(WHILE_SYM)}, + { "VIEW", SYM(VIEW_SYM)}, { "WITH", SYM(WITH)}, { "WORK", SYM(WORK_SYM)}, { "WRITE", SYM(WRITE_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 723469b255e..bcf61fde1a6 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -578,7 +578,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list) static bool locked_named_table(THD *thd, TABLE_LIST *table_list) { - for (; table_list ; table_list=table_list->next) + for (; table_list ; table_list=table_list->next_local) { if (table_list->table && table_is_used(table_list->table,0)) return 1; @@ -632,7 +632,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) bool got_all_locks=1; TABLE_LIST *lock_table; - for (lock_table=table_list ; lock_table ; lock_table=lock_table->next) + for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { int got_lock; if ((got_lock=lock_table_name(thd,lock_table)) < 0) @@ -675,7 +675,9 @@ end: void unlock_table_names(THD *thd, TABLE_LIST *table_list, TABLE_LIST *last_table) { - for (TABLE_LIST *table=table_list ; table != last_table ; table=table->next) + for (TABLE_LIST *table= table_list; + table != last_table; + table= table->next_local) unlock_table_name(thd,table); pthread_cond_broadcast(&COND_refresh); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3ad89df3de6..ba19ee15ce6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -404,6 +404,9 @@ bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); int multi_update_precheck(THD *thd, TABLE_LIST *tables); int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); +int mysql_multi_update_prepare(THD *thd); +int mysql_multi_delete_prepare(THD *thd); +int mysql_insert_select_prepare(THD *thd); int insert_select_precheck(THD *thd, TABLE_LIST *tables); int update_precheck(THD *thd, TABLE_LIST *tables); int delete_precheck(THD *thd, TABLE_LIST *tables); @@ -462,7 +465,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags); int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, my_bool drop_temporary); int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool log_query); + bool drop_temporary, bool drop_view, bool log_query); int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool log_query); @@ -541,7 +544,6 @@ int mysql_select(THD *thd, Item ***rref_pointer_array, select_result *result, SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex); void free_underlaid_joins(THD *thd, SELECT_LEX *select); -void fix_tables_pointers(SELECT_LEX *select_lex); int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result); int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type, @@ -562,7 +564,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, List<create_field> &fields, List<Key> &keys, bool tmp_table, bool no_log, uint select_field_count); TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - const char *db, const char *name, + TABLE_LIST *create_table, List<create_field> *extra_fields, List<Key> *keys, List<Item> *items, @@ -588,7 +590,6 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys); int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info); int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - TABLE_LIST *update_table_list, Item **conds, uint order_num, ORDER *order); int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, List<Item> &values,COND *conds, @@ -599,8 +600,7 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list, COND *conds, ulong options, enum enum_duplicates handle_duplicates, SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex); -int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, - TABLE_LIST *insert_table_list, TABLE *table, +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, List<Item> &fields, List_item *values, List<Item> &update_fields, List<Item> &update_values, enum_duplicates duplic); @@ -612,7 +612,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order, ha_rows rows, ulong options); int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); -TABLE *open_table(THD *thd,const char *db,const char *table,const char *alias, +TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool *refresh); TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table); TABLE *find_locked_table(THD *thd, const char *db,const char *table_name); @@ -628,10 +628,14 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); extern const Field *not_found_field; +extern const Field *view_ref_found; Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, - TABLE_LIST **where, bool report_error); -Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, - bool check_grant,bool allow_rowid, + Item **ref, bool report_error, + bool check_privileges); +Field *find_field_in_table(THD *thd, TABLE_LIST *tables, const char *name, + uint length, Item **ref, + bool check_grant_table, bool check_grant_view, + bool allow_rowid, uint *cached_field_index_ptr); #ifdef HAVE_OPENSSL #include <openssl/des.h> @@ -693,8 +697,6 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name); void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); -int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, - List<Item> &values, ulong counter); void reset_stmt_for_execute(THD *thd, LEX *lex); /* sql_error.cc */ @@ -740,8 +742,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, - List_iterator<Item> *it); -bool setup_tables(TABLE_LIST *tables); + List_iterator<Item> *it, bool any_privileges); +bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); int setup_fields(THD *thd, Item** ref_pointer_array, TABLE_LIST *tables, @@ -762,11 +764,12 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -TABLE_LIST * find_table_in_list(TABLE_LIST *table, - const char *db_name, const char *table_name); TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name); +TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, + const char *db_name, + const char *table_name); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); void close_temporary(TABLE *table, bool delete_table=1); @@ -1148,6 +1151,8 @@ extern int yyparse(void *thd); SQL_CRYPT *get_crypt_for_frm(void); #endif +#include "sql_view.h" + /* Some inline functions for more speed */ inline bool add_item_to_list(THD *thd, Item *item) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d1c60773fec..682b368e6a9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4045,7 +4045,8 @@ enum options_mysqld OPT_LOG_QUERIES_NOT_USING_INDEXES, OPT_DEFAULT_TIME_ZONE, OPT_OPTIMIZER_SEARCH_DEPTH, - OPT_OPTIMIZER_PRUNE_LEVEL + OPT_OPTIMIZER_PRUNE_LEVEL, + OPT_SQL_UPDATABLE_VIEW_KEY }; @@ -5122,6 +5123,11 @@ The minimum value for this variable is 4096.", (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME], (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME], 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sql_updatable_view_key", OPT_SQL_UPDATABLE_VIEW_KEY, + "0 = NO = Don't check presence of key in updateable VIEW. 1 = YES = Prohibit update of VIEW which do not contain key of underlaying table. 2 = LIMIT1 = Same as YES but prohibited only operation with LIMIT 1 (usually get from GUI tools).", + (gptr*) &global_system_variables.sql_updatable_view_key, + (gptr*) &max_system_variables.sql_updatable_view_key, + 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index f4c39462d0c..314decb7041 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -89,7 +89,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) where_tables= conds->used_tables(); /* Don't replace expression on a table that is part of an outer join */ - for (TABLE_LIST *tl=tables; tl ; tl= tl->next) + for (TABLE_LIST *tl= tables; tl; tl= tl->next_local) { if (tl->on_expr) { @@ -128,7 +128,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) { longlong count= 1; TABLE_LIST *table; - for (table=tables ; table ; table=table->next) + for (table= tables; table; table= table->next_local) { if (outer_tables || (table->table->file->table_flags() & HA_NOT_EXACT_COUNT)) diff --git a/sql/set_var.cc b/sql/set_var.cc index 07ab6620e4b..ea17bde6f10 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -326,6 +326,10 @@ sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", &SV::sortbuff_size); sys_var_thd_sql_mode sys_sql_mode("sql_mode", &SV::sql_mode); +sys_var_thd_enum sys_sql_updatable_view_key("sql_updatable_view_key", + &SV::sql_updatable_view_key, + &sql_updatable_view_key_typelib); + sys_var_thd_table_type sys_table_type("table_type", &SV::table_type); sys_var_thd_storage_engine sys_storage_engine("storage_engine", @@ -583,6 +587,7 @@ sys_var *sys_variables[]= &sys_sql_low_priority_updates, &sys_sql_max_join_size, &sys_sql_mode, + &sys_sql_updatable_view_key, &sys_sql_warnings, &sys_storage_engine, #ifdef HAVE_REPLICATION @@ -808,6 +813,8 @@ struct show_var_st init_vars[]= { #endif {sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS}, {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS}, + {sys_sql_updatable_view_key.name, + (char*) &sys_sql_updatable_view_key, SHOW_SYS}, {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, #ifdef HAVE_REPLICATION {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS}, diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 8ca63ce2105..81bc1e733c6 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -355,3 +355,14 @@ character-set=latin2 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 4c61f0449ab..f16d0700be1 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -349,3 +349,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 673678543b1..e4a7d2ffe02 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -357,3 +357,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 4e0d7392947..c13eee75392 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -346,3 +346,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 6438934cdda..4ff08a9c8c7 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -351,3 +351,14 @@ character-set=latin7 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index c4957433a8c..a1d0448ebf1 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -346,3 +346,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 0d6eb180abd..ac7cf16b7b4 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -358,3 +358,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 6a1c6b99c25..f505766bbe9 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -346,3 +346,14 @@ character-set=greek "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index f9391613018..4f328d07825 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -348,3 +348,14 @@ character-set=latin2 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 525fd7eced9..af5ebc4c3e6 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -346,3 +346,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index fdff7cff4e3..2a230bdf17d 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -348,3 +348,14 @@ character-set=ujis "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index ee038dd9ec6..ed1751bbc18 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -346,3 +346,14 @@ character-set=euckr "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 10cb75fdb7b..70951dfc1f4 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -348,3 +348,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index d17c12c13e3..d6b0fa9303b 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -348,3 +348,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 38d707d9174..6914661f188 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -350,3 +350,14 @@ character-set=latin2 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 391c10c8bbc..3aa6071ff5b 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -347,3 +347,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 8cc218f3831..87691de7ce9 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -350,3 +350,14 @@ character-set=latin2 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b317265f9ac..55116a24b4a 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -348,3 +348,14 @@ character-set=koi8r "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.64s'" "ïÛÉÂËÁ ÐÒÉ ÒÁÓÐÏÚÎÁ×ÁÎÉÉ ÐÁÒÁÍÅÔÒÁ '%-.64s' (ÓÔÒÏËÁ: '%-.64s')" "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ ÐÒÉ ÐÒÏÐÕÓËÅ ÎÅÉÚ×ÅÓÔÎÏÇÏ ÐÁÒÁÍÅÔÒÁ '%-.64s'" +"EXPLAIN/SHOW ÎÅ ÍÏÖÅÔ ÂÙÔØ ×ÙÐÏÌÎÅÎÎÏ; ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÎÁ ÔÁËÂÌÉÃÙ ÚÁÐÒÏÓÁ" +"æÁÊÌ '%-.64s' ÓÏÄÅÒÖÉÔ ÎÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ '%-.64s' × ÚÁÇÏÌÏ×ËÅ" +"'%-.64s.%-.64s' - ÎÅ %s" +"óÔÏÌÂÅà '%-.64s' ÎÅ ÏÂÎÏ×ÌÑÅÍÙÊ" +"View SELECT ÓÏÄÅÒÖÉÔ ÐÏÄÚÁÐÒÏÓ × ËÏÎÓÔÒÕËÃÉÉ FROM" +"View SELECT ÓÏÄÅÒÖÉÔ ËÏÎÓÔÒÕËÃÉÀ PROCEDURE" +"View SELECT ÓÏÄÅÒÖÉÔ ÐÅÒÅÍÅÎÎÕÀ ÉÌÉ ÐÁÒÁÍÅÔÒ" +"View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'" +"View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×" +"áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)" +"ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÏÊ × ÎÅÍ ÔÁÂÌÉÃ(Ù)" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 6a5b24666c3..a08e65149ef 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -352,3 +352,14 @@ character-set=cp1250 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index ba851763bf6..222461bab51 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -354,3 +354,14 @@ character-set=latin2 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index fe19bdbb7ac..791dcacfcc2 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -348,3 +348,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 3a616479dba..0c562525ffa 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -346,3 +346,14 @@ character-set=latin1 "Unexpected end of file while parsing comment '%-.64s'" "Error while parsing parameter '%-.64s' (line: '%-.64s')" "Unexpected end of file while skipping unknown parameter '%-.64s'" +"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table" +"File '%-.64s' has unknown type '%-.64s' in its header" +"'%-.64s.%-.64s' is not %s" +"Column '%-.64s' is not updatable" +"View's SELECT contains a subquery in the FROM clause" +"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a variable or parameter" +"View's SELECT contains a temporary table '%-.64s'" +"View's SELECT and view's field list have different column counts" +"View merge algorithm can't be used here for now (assumed undefined algorithm)" +"View being update does not have complete key of underlying table in it" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 1aeba953e8f..cb483e3ed99 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -351,3 +351,14 @@ character-set=koi8u "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.64s'" "ðÏÍÉÌËÁ × ÒÏÓЦÚÎÁ×ÁÎΦ ÐÁÒÁÍÅÔÒÕ '%-.64s' (ÒÑÄÏË: '%-.64s')" "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ÓÐÒϦ ÐÒÏÍÉÎÕÔÉ ÎÅצÄÏÍÉÊ ÐÁÒÁÍÅÔÒ '%-.64s'" +"EXPLAIN/SHOW ÎÅ ÍÏÖÅ ÂÕÔÉ ×¦ËÏÎÁÎÏ; ÎÅÍÁ¤ ÐÒÁ× ÎÁ ÔÉÂÌÉæ ÚÁÐÉÔÕ" +"æÁÊÌ '%-.64s' ÍÁ¤ ÎÅצÄÏÍÉÊ ÔÉÐ '%-.64s' Õ ÚÁÇÏÌÏ×ËÕ" +"'%-.64s.%-.64s' ÎÅ ¤ %s" +"óÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÚÍÉÎÅÎÉÊ" +"View SELECT ÍÁ¤ ЦÄÚÁÐÉÔ Õ ËÏÎÓÔÒÕËæ§ FROM" +"View SELECT ÍÁ¤ ËÏÎÓÔÒÕËæÀ PROCEDURE" +"View SELECT ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ" +"View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.64s'" +"View SELECT ¦ ÐÅÒÅÌ¦Ë ÓÔÏ×ÂÃ¦× view ÍÁÀÔØ Ò¦ÚÎÕ Ë¦ÌØ˦ÓÔØ ÓËÏ×Âæ×" +"áÌÇÏÒÉÔÍ ÚÌÉ×ÁÎÎÑ view ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ ÚÁÒÁÚ (ÁÌÇÏÒÉÔÍ ÂÕÄÅ ÎÅ×ÉÚÎÁÞÅÎÉÊ)" +"View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ" diff --git a/sql/slave.cc b/sql/slave.cc index 5bc2599211e..8899635e2c6 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -897,7 +897,7 @@ int tables_ok(THD* thd, TABLE_LIST* tables) bool some_tables_updating= 0; DBUG_ENTER("tables_ok"); - for (; tables; tables = tables->next) + for (; tables; tables= tables->next_global) { char hash_key[2*NAME_LEN+2]; char *end; diff --git a/sql/sp.cc b/sql/sp.cc index 9ae7e6bef9e..408d676aebe 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -570,17 +570,20 @@ db_show_routine_status(THD *thd, int type, const char *wild) goto err_case; } - /* Init fields */ - setup_tables(&tables); + /* + Init fields + + tables is not VIEW for sure => we can pass 0 as condition + */ + setup_tables(thd, &tables, 0); for (used_field= &used_fields[0]; used_field->field_name; used_field++) { - TABLE_LIST *not_used; Item_field *field= new Item_field("mysql", "proc", used_field->field_name); if (!(used_field->field= find_field_in_tables(thd, field, &tables, - ¬_used, TRUE))) + 0, TRUE, 1))) { res= SP_INTERNAL_ERROR; goto err_case1; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 810312dea16..fcd51eb2273 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -36,6 +36,8 @@ #ifndef NO_EMBEDDED_ACCESS_CHECKS +#define FIRST_NON_YN_FIELD 26 + class acl_entry :public hash_filo_element { public: @@ -168,8 +170,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) tables[0].alias=tables[0].real_name=(char*) "host"; tables[1].alias=tables[1].real_name=(char*) "user"; tables[2].alias=tables[2].real_name=(char*) "db"; - tables[0].next=tables+1; - tables[1].next=tables+2; + tables[0].next_local= tables[0].next_global= tables+1; + tables[1].next_local= tables[1].next_global= tables+2; tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].db=tables[1].db=tables[2].db=thd->db; @@ -303,9 +305,14 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); + if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ { - char *ssl_type=get_field(&mem, table->field[24]); + uint base_field= 24; + if (table->fields > 31) /* Starting from 5.1 we have more privileges */ + base_field= 26; + + char *ssl_type=get_field(&mem, table->field[base_field]); if (!ssl_type) user.ssl_type=SSL_TYPE_NONE; else if (!strcmp(ssl_type, "ANY")) @@ -315,15 +322,15 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) else /* !strcmp(ssl_type, "SPECIFIED") */ user.ssl_type=SSL_TYPE_SPECIFIED; - user.ssl_cipher= get_field(&mem, table->field[25]); - user.x509_issuer= get_field(&mem, table->field[26]); - user.x509_subject= get_field(&mem, table->field[27]); + user.ssl_cipher= get_field(&mem, table->field[base_field+1]); + user.x509_issuer= get_field(&mem, table->field[base_field+2]); + user.x509_subject= get_field(&mem, table->field[base_field+3]); - char *ptr = get_field(&mem, table->field[28]); + char *ptr = get_field(&mem, table->field[base_field+4]); user.user_resource.questions=atoi(ptr); - ptr = get_field(&mem, table->field[29]); + ptr = get_field(&mem, table->field[base_field+5]); user.user_resource.updates=atoi(ptr); - ptr = get_field(&mem, table->field[30]); + ptr = get_field(&mem, table->field[base_field+6]); user.user_resource.connections=atoi(ptr); if (user.user_resource.questions || user.user_resource.updates || user.user_resource.connections) @@ -2265,6 +2272,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, LEX_USER *Str; TABLE_LIST tables[3]; bool create_new_users=0; + char *db_name, *real_name; DBUG_ENTER("mysql_table_grant"); if (!initialized) @@ -2281,17 +2289,18 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (columns.elements && !revoke_grant) { - TABLE *table; class LEX_COLUMN *column; List_iterator <LEX_COLUMN> column_iter(columns); + int res; - if (!(table=open_ltable(thd,table_list,TL_READ))) - DBUG_RETURN(-1); + if ((res= open_and_lock_tables(thd, table_list))) + DBUG_RETURN(res); + while ((column = column_iter++)) { uint unused_field_idx= NO_CACHED_FIELD_INDEX; - if (!find_field_in_table(thd,table,column->column.ptr(), - column->column.length(),0,0, + if (!find_field_in_table(thd, table_list, column->column.ptr(), + column->column.length(), 0, 0, 0, 0, &unused_field_idx)) { my_error(ER_BAD_FIELD_ERROR, MYF(0), @@ -2321,11 +2330,13 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].alias=tables[0].real_name= (char*) "user"; tables[1].alias=tables[1].real_name= (char*) "tables_priv"; tables[2].alias=tables[2].real_name= (char*) "columns_priv"; - tables[0].next=tables+1; + tables[0].next_local= tables[0].next_global= tables+1; /* Don't open column table if we don't need it ! */ - tables[1].next=((column_priv || - (revoke_grant && ((rights & COL_ACLS) || columns.elements))) - ? tables+2 : 0); + tables[1].next_local= + tables[1].next_global= ((column_priv || + (revoke_grant && + ((rights & COL_ACLS) || columns.elements))) + ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; @@ -2381,10 +2392,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, continue; // Add next user } + db_name= (table_list->view_db.length ? + table_list->view_db.str : + table_list->db); + real_name= (table_list->view_name.length ? + table_list->view_name.str : + table_list->real_name); + /* Find/create cached table grant */ - grant_table= table_hash_search(Str->host.str,NullS,table_list->db, - Str->user.str, - table_list->real_name,1); + grant_table= table_hash_search(Str->host.str, NullS, db_name, + Str->user.str, real_name, 1); if (!grant_table) { if (revoke_grant) @@ -2394,9 +2411,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, result= -1; continue; } - grant_table = new GRANT_TABLE (Str->host.str,table_list->db, - Str->user.str, - table_list->real_name, + grant_table = new GRANT_TABLE (Str->host.str, db_name, + Str->user.str, real_name, rights, column_priv); if (!grant_table) // end of memory @@ -2441,19 +2457,17 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* update table and columns */ - if (replace_table_table(thd,grant_table,tables[1].table,*Str, - table_list->db, - table_list->real_name, + if (replace_table_table(thd, grant_table, tables[1].table, *Str, + db_name, real_name, rights, column_priv, revoke_grant)) { // Crashend table ?? result= -1; /* purecov: deadcode */ } else if (tables[2].table) { - if ((replace_column_table(grant_table,tables[2].table, *Str, + if ((replace_column_table(grant_table, tables[2].table, *Str, columns, - table_list->db, - table_list->real_name, + db_name, real_name, rights, revoke_grant))) { result= -1; @@ -2497,11 +2511,9 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, bzero((char*) &tables,sizeof(tables)); tables[0].alias=tables[0].real_name=(char*) "user"; tables[1].alias=tables[1].real_name=(char*) "db"; - tables[0].next=tables+1; - tables[1].next=0; + tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - tables[0].table=tables[1].table=0; #ifdef HAVE_REPLICATION /* @@ -2618,7 +2630,7 @@ my_bool grant_init(THD *org_thd) bzero((char*) &tables, sizeof(tables)); tables[0].alias=tables[0].real_name= (char*) "tables_priv"; tables[1].alias=tables[1].real_name= (char*) "columns_priv"; - tables[0].next=tables+1; + tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_READ; tables[0].db=tables[1].db=thd->db; @@ -2752,11 +2764,15 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, return 0; // ok rw_rdlock(&LOCK_grant); - for (table= tables; table && number--; table= table->next) + for (table= tables; table && number--; table= table->next_global) { if (!(~table->grant.privilege & want_access) || table->derived) { - table->grant.want_privilege=0; + /* + It is subquery in the FROM clause. VIEW set table->derived after + table opening, but this function always called before table opening. + */ + table->grant.want_privilege= 0; continue; // Already checked } GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, @@ -2811,6 +2827,10 @@ err: command= "index"; else if (want_access & GRANT_ACL) command= "grant"; + else if (want_access & CREATE_VIEW_ACL) + command= "create view"; + else if (want_access & SHOW_VIEW_ACL) + command= "show view"; net_printf(thd,ER_TABLEACCESS_DENIED_ERROR, command, thd->priv_user, @@ -2821,13 +2841,14 @@ err: } -bool check_grant_column(THD *thd,TABLE *table, const char *name, - uint length, uint show_tables) +bool check_grant_column(THD *thd, GRANT_INFO *grant, + char*db_name, char *table_name, + const char *name, uint length, uint show_tables) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; - ulong want_access=table->grant.want_privilege; + ulong want_access= grant->want_privilege & ~grant->privilege; if (!want_access) return 0; // Already checked @@ -2835,15 +2856,15 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, /* reload table if someone has modified any grants */ - if (table->grant.version != grant_version) + if (grant->version != grant_version) { - table->grant.grant_table= - table_hash_search(thd->host, thd->ip, table->table_cache_key, + grant->grant_table= + table_hash_search(thd->host, thd->ip, db_name, thd->priv_user, - table->real_name, 0); /* purecov: inspected */ - table->grant.version=grant_version; /* purecov: inspected */ + table_name, 0); /* purecov: inspected */ + grant->version= grant_version; /* purecov: inspected */ } - if (!(grant_table=table->grant.grant_table)) + if (!(grant_table= grant->grant_table)) goto err; /* purecov: deadcode */ grant_column=column_hash_search(grant_table, name, length); @@ -2853,7 +2874,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, return 0; } #ifdef NOT_USED - if (show_tables && (grant_column || table->grant.privilege & COL_ACLS)) + if (show_tables && (grant_column || grant->privilege & COL_ACLS)) { rw_unlock(&LOCK_grant); /* purecov: deadcode */ return 0; /* purecov: deadcode */ @@ -2874,47 +2895,47 @@ err: thd->priv_user, thd->host_or_ip, name, - table ? table->real_name : "unknown"); + table_name); } return 1; } -bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) +bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, + char* db_name, char *table_name, + Field_iterator *fields) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; Field *field=0,**ptr; - want_access &= ~table->grant.privilege; + want_access &= ~grant->privilege; if (!want_access) return 0; // Already checked if (!grant_option) - { - field= table->field[0]; // To give a meaningful error message goto err2; - } rw_rdlock(&LOCK_grant); /* reload table if someone has modified any grants */ - if (table->grant.version != grant_version) + if (grant->version != grant_version) { - table->grant.grant_table= - table_hash_search(thd->host, thd->ip, table->table_cache_key, + grant->grant_table= + table_hash_search(thd->host, thd->ip, db_name, thd->priv_user, - table->real_name,0); /* purecov: inspected */ - table->grant.version=grant_version; /* purecov: inspected */ + table_name, 0); /* purecov: inspected */ + grant->version= grant_version; /* purecov: inspected */ } /* The following should always be true */ - if (!(grant_table=table->grant.grant_table)) + if (!(grant_table= grant->grant_table)) goto err; /* purecov: inspected */ - for (ptr=table->field; (field= *ptr) ; ptr++) + for (; fields->end(); fields->next()) { - grant_column=column_hash_search(grant_table, field->field_name, - (uint) strlen(field->field_name)); + const char *field_name= fields->name(); + grant_column= column_hash_search(grant_table, field_name, + (uint) strlen(field_name)); if (!grant_column || (~grant_column->rights & want_access)) goto err; } @@ -2936,8 +2957,8 @@ err2: command, thd->priv_user, thd->host_or_ip, - field ? field->field_name : "unknown", - table->real_name); + fields->name(), + table_name); return 1; } @@ -3004,7 +3025,9 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) } -ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) +ulong get_column_grant(THD *thd, GRANT_INFO *grant, + const char *db_name, const char *table_name, + const char *field_name) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; @@ -3012,30 +3035,31 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) rw_rdlock(&LOCK_grant); /* reload table if someone has modified any grants */ - if (table->grant.version != grant_version) + if (grant->version != grant_version) { - table->grant.grant_table= - table_hash_search(thd->host, thd->ip, table->db, + grant->grant_table= + table_hash_search(thd->host, thd->ip, db_name, thd->priv_user, - table->real_name,0); /* purecov: inspected */ - table->grant.version=grant_version; /* purecov: inspected */ + table_name, 0); /* purecov: inspected */ + grant->version= grant_version; /* purecov: inspected */ } - if (!(grant_table=table->grant.grant_table)) - priv=table->grant.privilege; + if (!(grant_table= grant->grant_table)) + priv= grant->privilege; else { - grant_column=column_hash_search(grant_table, field->field_name, - (uint) strlen(field->field_name)); + grant_column= column_hash_search(grant_table, field_name, + (uint) strlen(field_name)); if (!grant_column) - priv=table->grant.privilege; + priv= grant->privilege; else - priv=table->grant.privilege | grant_column->rights; + priv= grant->privilege | grant_column->rights; } rw_unlock(&LOCK_grant); return priv; } + /* Help function for mysql_show_grants */ static void add_user_option(String *grant, ulong value, const char *name) @@ -3053,15 +3077,16 @@ static void add_user_option(String *grant, ulong value, const char *name) static const char *command_array[]= { - "SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP", "RELOAD","SHUTDOWN", - "PROCESS","FILE","GRANT","REFERENCES","INDEX", "ALTER", "SHOW DATABASES", - "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", - "REPLICATION SLAVE", "REPLICATION CLIENT", + "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD", + "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX", + "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", + "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", + "CREATE VIEW", "SHOW VIEW" }; static uint command_lengths[]= { - 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18 + 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9 }; @@ -3460,10 +3485,9 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+1)->alias= (tables+1)->real_name= (char*) "db"; (tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv"; (tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv"; - tables->next= tables+1; - (tables+1)->next= tables+2; - (tables+2)->next= tables+3; - (tables+3)->next= 0; + tables->next_local= tables->next_global= tables+1; + (tables+1)->next_local= (tables+1)->next_global= tables+2; + (tables+2)->next_local= (tables+2)->next_global= tables+3; tables->lock_type= (tables+1)->lock_type= (tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql"; @@ -3797,3 +3821,39 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr) DBUG_RETURN (*str != '\0'); } +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/* + fill effective privileges for table + + SYNOPSIS + get_effectlige_privileges() + thd thread handleg + grant grants table descriptor + db db name + table table name +*/ + +void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, + const char *db, const char *table) +{ + /* global privileges */ + grant->privilege= thd->master_access; + /* db privileges */ + grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0); + /* table privileges */ + rw_rdlock(&LOCK_grant); + if (grant->version != grant_version) + { + grant->grant_table= + table_hash_search(thd->host, thd->ip, db, + thd->priv_user, + table, 0); /* purecov: inspected */ + grant->version= grant_version; /* purecov: inspected */ + } + if (grant->grant_table != 0) + { + grant->privilege|= grant->grant_table->privs; + } + rw_unlock(&LOCK_grant); +} +#endif diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 2ea3b8f5628..77702cc375a 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -35,7 +35,8 @@ #define EXECUTE_ACL (1L << 18) #define REPL_SLAVE_ACL (1L << 19) #define REPL_CLIENT_ACL (1L << 20) - +#define CREATE_VIEW_ACL (1L << 21) +#define SHOW_VIEW_ACL (1L << 22) /* don't forget to update static struct show_privileges_st sys_privileges[] @@ -45,11 +46,13 @@ #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ - GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | LOCK_TABLES_ACL) + GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ + LOCK_TABLES_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ - GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) + GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \ + SHOW_VIEW_ACL) #define COL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) @@ -59,7 +62,7 @@ RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \ REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ - EXECUTE_ACL) + EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) #define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) @@ -72,13 +75,21 @@ /* Continius bit-segments that needs to be shifted */ #define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL) #define DB_REL2 (GRANT_ACL | REFERENCES_ACL) +#define DB_REL3 (INDEX_ACL | ALTER_ACL) /* Privileges that needs to be reallocated (in continous chunks) */ #define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) #define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL) - -#define fix_rights_for_db(A) (((A) & 63) | (((A) & DB_REL1) << 4) | (((A) & DB_REL2) << 6)) -#define get_rights_for_db(A) (((A) & 63) | (((A) & DB_CHUNK1) >> 4) | (((A) & DB_CHUNK2) >> 6)) +#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) + +#define fix_rights_for_db(A) (((A) & 63) | \ + (((A) & DB_REL1) << 4) | \ + (((A) & DB_REL2) << 6) | \ + (((A) & DB_REL3) << 9)) +#define get_rights_for_db(A) (((A) & 63) | \ + (((A) & DB_CHUNK1) >> 4) | \ + (((A) & DB_CHUNK2) >> 6) | \ + (((A) & DB_CHUNK3) >> 9)) #define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4)) #define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4)) #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) @@ -156,17 +167,24 @@ void grant_free(void); void grant_reload(THD *thd); bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_command, uint number, bool dont_print_error); -bool check_grant_column (THD *thd,TABLE *table, const char *name, uint length, - uint show_command=0); -bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table); +bool check_grant_column (THD *thd, GRANT_INFO *grant, + char *db_name, char *table_name, + const char *name, uint length, uint show_command=0); +bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, + char* db_name, char *table_name, + Field_iterator *fields); bool check_grant_db(THD *thd,const char *db); ulong get_table_grant(THD *thd, TABLE_LIST *table); -ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field); +ulong get_column_grant(THD *thd, GRANT_INFO *grant, + const char *db_name, const char *table_name, + const char *field_name); int mysql_show_grants(THD *thd, LEX_USER *user); void get_privilege_desc(char *to, uint max_length, ulong access); void get_mqh(const char *user, const char *host, USER_CONN *uc); int mysql_drop_user(THD *thd, List <LEX_USER> &list); int mysql_revoke_all(THD *thd, List <LEX_USER> &list); +void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, + const char *db, const char *table); #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 954cf7a5a6d..781e7273e7a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -32,11 +32,19 @@ TABLE *unused_tables; /* Used by mysql_test */ HASH open_cache; /* Used by mysql_test */ HASH assign_cache; -static int open_unireg_entry(THD *thd,TABLE *entry,const char *db, - const char *name, const char *alias); +static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, + const char *name, const char *alias, + TABLE_LIST *table_list, MEM_ROOT *mem_root); static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); - +static my_bool open_new_frm(const char *path, const char *alias, + uint db_stat, uint prgflag, + uint ha_open_flags, TABLE *outparam, + TABLE_LIST *table_desc, MEM_ROOT *mem_root); +static Field *find_field_in_real_table(THD *thd, TABLE *table, + const char *name, uint length, + bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) @@ -277,7 +285,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, else { bool found=0; - for (TABLE_LIST *table=tables ; table ; table=table->next) + for (TABLE_LIST *table= tables; table; table= table->next_local) { if (remove_table_from_cache(thd, table->db, table->real_name, 1)) found=1; @@ -328,7 +336,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, result=reopen_tables(thd,1,1); thd->in_lock_tables=0; /* Set version for table */ - for (TABLE *table=thd->open_tables; table ; table=table->next) + for (TABLE *table=thd->open_tables; table ; table= table->next) table->version=refresh_version; } VOID(pthread_mutex_unlock(&LOCK_open)); @@ -540,6 +548,7 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } +#ifdef UNUSED /* Find first suitable table by alias in given list. @@ -564,9 +573,10 @@ TABLE_LIST * find_table_in_list(TABLE_LIST *table, break; return table; } +#endif /*UNUSED*/ /* - Find real table in given list. + Find real table in given global list. SYNOPSIS find_real_table_in_list() @@ -583,13 +593,40 @@ TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name) { - for (; table; table= table->next) + for (; table; table= table->next_global) if (!strcmp(table->db, db_name) && !strcmp(table->real_name, table_name)) break; return table; } + +/* + Find real table in given local list. + + SYNOPSIS + find_real_table_in_local_list() + table - pointer to table list + db_name - data base name + table_name - table name + + RETURN VALUES + NULL Table not found + # Pointer to found table. +*/ + +TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) +{ + for (; table; table= table->next_local) + if (!strcmp(table->db, db_name) && + !strcmp(table->real_name, table_name)) + break; + return table; +} + + TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; @@ -749,7 +786,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; pthread_mutex_lock(&LOCK_open); - if (open_unireg_entry(thd, table, db, table_name, table_name) || + if (open_unireg_entry(thd, table, db, table_name, table_name, 0, 0) || !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, key_length))) { @@ -787,12 +824,13 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) ******************************************************************************/ -TABLE *open_table(THD *thd,const char *db,const char *table_name, - const char *alias,bool *refresh) +TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, + bool *refresh) { reg1 TABLE *table; char key[MAX_DBKEY_LENGTH]; uint key_length; + char *alias= table_list->alias; DBUG_ENTER("open_table"); /* find a unused table in the open table cache */ @@ -800,25 +838,29 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, *refresh=0; if (thd->killed) DBUG_RETURN(0); - key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1; + key_length= (uint) (strmov(strmov(key, table_list->db)+1, + table_list->real_name)-key)+1; int4store(key + key_length, thd->server_id); int4store(key + key_length + 4, thd->variables.pseudo_thread_id); - for (table=thd->temporary_tables; table ; table=table->next) + if (!table_list->skip_temporary) { - if (table->key_length == key_length+8 && - !memcmp(table->table_cache_key,key,key_length+8)) + for (table= thd->temporary_tables; table ; table=table->next) { - if (table->query_id == thd->query_id) + if (table->key_length == key_length + 8 && + !memcmp(table->table_cache_key, key, key_length + 8)) { - my_printf_error(ER_CANT_REOPEN_TABLE, - ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name); - DBUG_RETURN(0); + if (table->query_id == thd->query_id) + { + my_printf_error(ER_CANT_REOPEN_TABLE, + ER(ER_CANT_REOPEN_TABLE), MYF(0), table->table_name); + DBUG_RETURN(0); + } + table->query_id= thd->query_id; + table->clear_query_id= 1; + thd->tmp_table_used= 1; + goto reset; } - table->query_id=thd->query_id; - table->clear_query_id=1; - thd->tmp_table_used= 1; - goto reset; } } @@ -868,7 +910,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, if (table->in_use != thd) wait_for_refresh(thd); else + { VOID(pthread_mutex_unlock(&LOCK_open)); + } if (refresh) *refresh=1; DBUG_RETURN(0); @@ -898,15 +942,23 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (open_unireg_entry(thd, table,db,table_name,alias) || - !(table->table_cache_key=memdup_root(&table->mem_root,(char*) key, - key_length))) + if (open_unireg_entry(thd, table, table_list->db, table_list->real_name, + alias, table_list, mem_root) || + (!table_list->view && + !(table->table_cache_key= memdup_root(&table->mem_root, (char*) key, + key_length)))) { table->next=table->prev=table; free_cache_entry(table); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } + if (table_list->view) + { + my_free((gptr)table, MYF(0)); + VOID(pthread_mutex_unlock(&LOCK_open)); + DBUG_RETURN(0); // VIEW + } table->key_length=key_length; table->version=refresh_version; table->flush_version=flush_version; @@ -916,7 +968,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->in_use=thd; check_unused(); // Debugging call - + VOID(pthread_mutex_unlock(&LOCK_open)); if (refresh) { @@ -946,6 +998,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->used_keys= table->keys_for_keyread; if (table->timestamp_field) table->timestamp_field->set_timestamp_offsets(); + table_list->updatable= 1; // It is not derived table nor non-updatable VIEW DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -992,7 +1045,8 @@ bool reopen_table(TABLE *table,bool locked) VOID(pthread_mutex_lock(&LOCK_open)); safe_mutex_assert_owner(&LOCK_open); - if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name)) + if (open_unireg_entry(current_thd, &tmp, db, table_name, + table->table_name, 0, 0)) goto end; free_io_cache(table); @@ -1309,6 +1363,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) db Database name name Table name alias Alias name + table_desc TABLE_LIST descriptor + mem_root temporary mem_root for parsing NOTES Extra argument for open is taken from thd->open_options @@ -1317,21 +1373,32 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) 0 ok # Error */ - static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, - const char *name, const char *alias) + const char *name, const char *alias, + TABLE_LIST *table_desc, MEM_ROOT *mem_root) { char path[FN_REFLEN]; int error; + // we support new format only is have all parameters for it + uint new_frm_flag= (table_desc && mem_root) ? NO_ERR_ON_NEW_FRM : 0; uint discover_retry_count= 0; DBUG_ENTER("open_unireg_entry"); strxmov(path, mysql_data_home, "/", db, "/", name, NullS); - while (openfrm(path,alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | - HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - thd->open_options, entry)) + while ((error= openfrm(path, alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | HA_TRY_READ_ONLY | + new_frm_flag), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + thd->open_options, entry)) && + (error != 5 || + fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME), + open_new_frm(path, alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + thd->open_options, entry, table_desc, mem_root))) + { if (!entry->crashed) { @@ -1398,6 +1465,10 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, goto err; break; } + + if (error == 5) + DBUG_RETURN(0); // we have just opened VIEW + /* If we are here, there was no fatal error (but error may be still unitialized). @@ -1457,12 +1528,18 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) bool refresh; int result=0; DBUG_ENTER("open_tables"); + MEM_ROOT new_frm_mem; + /* + temporary mem_root for new .frm parsing. + TODO: variables for size + */ + init_alloc_root(&new_frm_mem, 8024, 8024); thd->current_tablenr= 0; restart: *counter= 0; thd->proc_info="Opening tables"; - for (tables=start ; tables ; tables=tables->next) + for (tables= start; tables ;tables= tables->next_global) { /* Ignore placeholders for derived tables. After derived tables @@ -1472,11 +1549,15 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) continue; (*counter)++; if (!tables->table && - !(tables->table= open_table(thd, - tables->db, - tables->real_name, - tables->alias, &refresh))) + !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh))) { + free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); + if (tables->view) + { + (*counter)--; + continue; //VIEW placeholder + } + if (refresh) // Refresh in progress { /* close all 'old' tables used by this thread */ @@ -1486,7 +1567,7 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) thd->version=refresh_version; TABLE **prev_table= &thd->open_tables; bool found=0; - for (TABLE_LIST *tmp=start ; tmp ; tmp=tmp->next) + for (TABLE_LIST *tmp= start; tmp; tmp= tmp->next_global) { /* Close normal (not temporary) changed tables */ if (tmp->table && ! tmp->table->tmp_table) @@ -1514,11 +1595,15 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) result= -1; // Fatal error break; } + else + free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); + if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) tables->table->reginfo.lock_type=tables->lock_type; tables->table->grant= tables->grant; } thd->proc_info=0; + free_root(&new_frm_mem, MYF(0)); DBUG_RETURN(result); } @@ -1586,9 +1671,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) thd->proc_info="Opening table"; thd->current_tablenr= 0; - while (!(table=open_table(thd,table_list->db, - table_list->real_name,table_list->alias, - &refresh)) && refresh) ; + while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh) ; if (table) { @@ -1670,7 +1753,6 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) uint counter; if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter)) DBUG_RETURN(-1); /* purecov: inspected */ - fix_tables_pointers(thd->lex->all_selects_list); DBUG_RETURN(mysql_handle_derived(thd->lex)); } @@ -1706,9 +1788,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) TABLE **start,**ptr; if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count))) return -1; - for (table = tables ; table ; table=table->next) + for (table= tables; table; table= table->next_global) { - if (!table->derived) + if (!table->placeholder()) *(ptr++)= table->table; } if (!(thd->lock=mysql_lock_tables(thd,start,count))) @@ -1716,9 +1798,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) } else { - for (table = tables ; table ; table=table->next) + for (table= tables; table; table= table->next_global) { - if (!table->derived && + if (!table->placeholder() && check_lock_and_start_stmt(thd, table->table, table->lock_type)) { ha_rollback_stmt(thd); @@ -1818,12 +1900,109 @@ bool rm_temporary_table(enum db_type base, char *path) ** return unique field ******************************************************************************/ +// Special Field pointers for find_field_in_tables returning +const Field *not_found_field= (Field*) 0x1; +const Field *view_ref_found= (Field*) 0x2; + #define WRONG_GRANT (Field*) -1 -Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, - bool check_grants, bool allow_rowid, + +/* + Find field in table or view + + SYNOPSIS + find_field_in_table() + thd thread handler + table_list table where to find + name name of field + length length of name + ref expression substituted in VIEW should be + passed using this reference (return + view_ref_found) + check_grants_table do check columns grants for table? + check_grants_view do check columns grants for view? + allow_rowid do allow finding of "_rowid" field? + cached_field_index_ptr cached position in field list (used to + speedup prepared tables field finding) + + RETURN + 0 field is not found + view_ref_found found value in VIEW (real result is in *ref) + # pointer to field +*/ + +Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, + const char *name, uint length, Item **ref, + bool check_grants_table, bool check_grants_view, + bool allow_rowid, uint *cached_field_index_ptr) { + Field *fld; + if (table_list->field_translation) + { + DBUG_ASSERT(ref != 0 && table_list->view != 0); + uint num= table_list->view->select_lex.item_list.elements; + Item **trans= table_list->field_translation; + for (uint i= 0; i < num; i ++) + { + if (strcmp(trans[i]->name, name) == 0) + { + *ref= trans[i]; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_grants_view && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + return WRONG_GRANT; +#endif + return (Field*) view_ref_found; + } + } + return 0; + } + fld= find_field_in_real_table(thd, table_list->table, name, length, + check_grants_table, allow_rowid, + cached_field_index_ptr); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* check for views with temporary table algorithm */ + if (check_grants_view && table_list->view && + fld && fld != WRONG_GRANT && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + { + return WRONG_GRANT; + } +#endif + return fld; +} + +/* + Find field in table + + SYNOPSIS + find_field_in_real_table() + thd thread handler + table_list table where to find + name name of field + length length of name + check_grants do check columns grants? + allow_rowid do allow finding of "_rowid" field? + cached_field_index_ptr cached position in field list (used to + speedup prepared tables field finding) + + RETURN + 0 field is not found + # pointer to field +*/ + +static Field *find_field_in_real_table(THD *thd, TABLE *table, + const char *name, uint length, + bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr) +{ Field **field_ptr, *field; uint cached_field_index= *cached_field_index_ptr; @@ -1869,7 +2048,9 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, thd->dupp_field=field; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_grants && check_grant_column(thd,table,name,length)) + if (check_grants && check_grant_column(thd, &table->grant, + table->table_cache_key, + table->real_name, name, length)) return WRONG_GRANT; #endif return field; @@ -1884,25 +2065,25 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, thd Pointer to current thread structure item Field item that should be found tables Tables for scanning - where Table where field found will be returned via - this parameter + ref if view field is found, pointer to view item will + be returned via this parameter report_error If FALSE then do not report error if item not found and return not_found_field + check_privileges need to check privileges RETURN VALUES 0 Field is not found or field is not unique- error message is reported not_found_field Function was called with report_error == FALSE and field was not found. no error message reported. + view_ref_found view field is found, item passed through ref parameter found field */ -// Special Field pointer for find_field_in_tables returning -const Field *not_found_field= (Field*) 0x1; - Field * find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, - TABLE_LIST **where, bool report_error) + Item **ref, bool report_error, + bool check_privileges) { Field *found=0; const char *db=item->db_name; @@ -1923,14 +2104,15 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, field makes some prepared query ambiguous and so erronous, but we accept this trade off. */ - found= find_field_in_table(thd, item->cached_table->table, name, length, - test(item->cached_table-> - table->grant.want_privilege), - 1, &(item->cached_field_index)); + found= find_field_in_real_table(thd, item->cached_table->table, + name, length, + test(item->cached_table-> + table->grant.want_privilege) && + check_privileges, + 1, &(item->cached_field_index)); if (found) { - (*where)= tables; if (found == WRONG_GRANT) return (Field*) 0; return found; @@ -1952,19 +2134,22 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, if (table_name && table_name[0]) { /* Qualified field */ bool found_table=0; - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_local) { if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) && (!db || !tables->db || !tables->db[0] || !strcmp(db,tables->db))) { found_table=1; - Field *find=find_field_in_table(thd,tables->table,name,length, - test(tables->table->grant. - want_privilege), - 1, &(item->cached_field_index)); + Field *find= find_field_in_table(thd, tables, name, length, ref, + (test(tables->table->grant. + want_privilege) && + check_privileges), + (test(tables->grant.want_privilege) && + check_privileges), + 1, &(item->cached_field_index)); if (find) { - (*where)= item->cached_table= tables; + item->cached_table= tables; if (!tables->cacheable_table) item->cached_table= 0; if (find == WRONG_GRANT) @@ -2007,8 +2192,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) not_found_field; return (Field*) 0; } - bool allow_rowid= tables && !tables->next; // Only one table - for (; tables ; tables=tables->next) + bool allow_rowid= tables && !tables->next_local; // Only one table + for (; tables ; tables= tables->next_local) { if (!tables->table) { @@ -2018,14 +2203,18 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) not_found_field; } - Field *field=find_field_in_table(thd,tables->table,name,length, - test(tables->table->grant.want_privilege), - allow_rowid, &(item->cached_field_index)); + Field *field= find_field_in_table(thd, tables, name, length, ref, + (test(tables->table->grant. + want_privilege) && + check_privileges), + (test(tables->grant.want_privilege) && + check_privileges), + allow_rowid, &(item->cached_field_index)); if (field) { if (field == WRONG_GRANT) return (Field*) 0; - (*where)= item->cached_table= tables; + item->cached_table= tables; if (!tables->cacheable_table) item->cached_table= 0; if (found) @@ -2184,12 +2373,13 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, reg2 Item *item; List_iterator<Item> it(fields); while ( wild_num && (item= it++)) - { + { if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name && ((Item_field*) item)->field_name[0] == '*' && !((Item_field*) item)->field) { uint elem= fields.elements; + bool any_privileges= ((Item_field *) item)->any_privileges; Item_subselect *subsel= thd->lex->current_select->master_unit()->item; if (subsel && subsel->substype() == Item_subselect::EXISTS_SUBS) @@ -2202,7 +2392,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, it.replace(new Item_int("Not_used", (longlong) 1, 21)); } else if (insert_fields(thd,tables,((Item_field*) item)->db_name, - ((Item_field*) item)->table_name, &it)) + ((Item_field*) item)->table_name, &it, + any_privileges)) { if (arena) thd->restore_backup_item_arena(arena, &backup); @@ -2270,7 +2461,9 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, SYNOPSIS setup_tables() + thd - thread handler tables - tables list + conds - condition of current SELECT (can be changed by VIEW) RETURN 0 ok; In this case *map will includes the choosed index @@ -2283,14 +2476,20 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, This has to be called for all tables that are used by items, as otherwise table->map is not set and all Item_field will be regarded as const items. + + if tables do not contain VIEWs it is OK to pass 0 as conds */ -bool setup_tables(TABLE_LIST *tables) +bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds) { DBUG_ENTER("setup_tables"); + if (!tables || tables->setup_is_done) + DBUG_RETURN(0); + tables->setup_is_done= 1; uint tablenr=0; - for (TABLE_LIST *table_list=tables ; table_list ; - table_list=table_list->next,tablenr++) + for (TABLE_LIST *table_list= tables; + table_list; + table_list= table_list->next_local, tablenr++) { TABLE *table= table_list->table; setup_table_map(table, table_list, tablenr); @@ -2312,6 +2511,8 @@ bool setup_tables(TABLE_LIST *tables) table->keys_in_use_for_query.subtract(map); } table->used_keys.intersect(table->keys_in_use_for_query); + if (table_list->ancestor) + table_list->setup_ancestor(thd, conds); } if (tablenr > MAX_TABLES) { @@ -2366,14 +2567,15 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, ****************************************************************************/ bool -insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, - const char *table_name, List_iterator<Item> *it) +insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, + const char *table_name, List_iterator<Item> *it, + bool any_privileges) { uint found; DBUG_ENTER("insert_fields"); found=0; - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_local) { TABLE *table=tables->table; if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, @@ -2382,18 +2584,41 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, { #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Ensure that we have access right to all columns */ - if (!(table->grant.privilege & SELECT_ACL) && - check_grant_all_columns(thd,SELECT_ACL,table)) - DBUG_RETURN(-1); + if (!(table->grant.privilege & SELECT_ACL) && !any_privileges) + { + if (tables->view) + { + Field_iterator_view fields; + fields.set(tables); + if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant, + tables->view_db.str, + tables->view_name.str, + &fields)) + DBUG_RETURN(1); + } + else + { + Field_iterator_table fields; + fields.set(tables); + if (check_grant_all_columns(thd, SELECT_ACL, &table->grant, + table->table_cache_key, table->real_name, + &fields)) + DBUG_RETURN(1); + } + } #endif - Field **ptr=table->field,*field; - TABLE *natural_join_table= 0; + /* allocate 2 variables on stack to avoid pool alloaction */ + Field_iterator_table table_iter; + Field_iterator_view view_iter; + Field_iterator *iterator; + TABLE_LIST *natural_join_table= 0; + Field *field; thd->used_tables|=table->map; TABLE_LIST *embedded= tables; TABLE_LIST *last= embedded; TABLE_LIST *embedding; - + while ((embedding= embedded->embedding) && embedding->join_list->elements != 1) { @@ -2405,9 +2630,9 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, if (last != tables) break; embedded= embedding; - } - - if (tables == last && + } + + if (tables == last && !embedded->outer_join && embedded->natural_join && !embedded->natural_join->outer_join) @@ -2415,32 +2640,96 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, embedding= embedded->natural_join; while (embedding->nested_join) embedding= embedding->nested_join->join_list.head(); - natural_join_table= embedding->table; - } + natural_join_table= embedding; + } + if (tables->field_translation) + iterator= &view_iter; + else + iterator= &table_iter; + iterator->set(tables); - while ((field = *ptr++)) + for (; iterator->end(); iterator->next()) { + Item *not_used_item; uint not_used_field_index= NO_CACHED_FIELD_INDEX; + const char *field_name= iterator->name(); /* Skip duplicate field names if NATURAL JOIN is used */ if (!natural_join_table || - !find_field_in_table(thd, natural_join_table, field->field_name, - strlen(field->field_name), 0, 0, + !find_field_in_table(thd, natural_join_table, field_name, + strlen(field_name), ¬_used_item, 0, 0, 0, ¬_used_field_index)) { - Item_field *item= new Item_field(thd, field); + Item *item= iterator->item(thd); if (!found++) (void) it->replace(item); // Replace '*' else it->after(item); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (any_privileges) + { + /* + In time of view creation MEGRGE algorithm for underlying + VIEWs can't be used => it should be Item_field + */ + DBUG_ASSERT(item->type() == Item::FIELD_ITEM); + Item_field *fld= (Item_field*)item; + char *db, *tab; + if (tables->view) + { + db= tables->view_db.str; + tab= tables->view_name.str; + } + else + { + db= tables->db; + tab= tables->real_name; + } + if (!(fld->have_privileges= (get_column_grant(thd, + &table->grant, + db, + tab, + fld->field_name) & + VIEW_ANY_ACL))) + { + my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, + ER(ER_COLUMNACCESS_DENIED_ERROR), + MYF(0), + "ANY", + thd->priv_user, + thd->host_or_ip, + fld->field_name, + tab); + /* TODO: should be removed to have only one send_error */ + send_error(thd); + DBUG_RETURN(1); + } + } +#endif } - /* - Mark if field used before in this select. - Used by 'insert' to verify if a field name is used twice - */ - if (field->query_id == thd->query_id) - thd->dupp_field=field; - field->query_id=thd->query_id; - table->used_keys.intersect(field->part_of_key); + if ((field= iterator->field())) + { + /* + Mark if field used before in this select. + Used by 'insert' to verify if a field name is used twice + */ + if (field->query_id == thd->query_id) + thd->dupp_field=field; + field->query_id=thd->query_id; + table->used_keys.intersect(field->part_of_key); + } + else if (thd->current_arena && + thd->lex->current_select->first_execution) + { + Item_field *item= new Item_field(thd->strdup(tables->view_db.str), + thd->strdup(tables->view_name.str), + thd->strdup(field_name)); + /* + during cleunup() this item will be put in list to replace + expression from VIEW + */ + item->changed_during_fix_field= it->ref(); + } + } /* All fields are used */ table->used_fields=table->fields; @@ -2452,6 +2741,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, my_error(ER_NO_TABLES_USED,MYF(0)); else my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name); + send_error(thd); } DBUG_RETURN(!found); } @@ -2473,7 +2763,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) DBUG_ENTER("setup_conds"); thd->set_query_id=1; - + select_lex->cond_count= 0; if (*conds) { @@ -2485,18 +2775,19 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) /* Check if we are using outer joins */ - for (TABLE_LIST *table=tables ; table ; table=table->next) + for (TABLE_LIST *table= tables; table; table= table->next_local) { TABLE_LIST *embedded; TABLE_LIST *embedding= table; do - { + { embedded= embedding; if (embedded->on_expr) { /* Make a join an a expression */ thd->where="on clause"; - if (embedded->on_expr->fix_fields(thd, tables, &embedded->on_expr) || + if (!embedded->on_expr->fixed && + embedded->on_expr->fix_fields(thd, tables, &embedded->on_expr) || embedded->on_expr->check_cols(1)) DBUG_RETURN(1); select_lex->cond_count++; @@ -2543,33 +2834,59 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) thd->set_n_backup_item_arena(arena, &backup); TABLE *t1=tab1->table; TABLE *t2=tab2->table; + /* allocate 2 variables on stack to avoid pool alloaction */ + Field_iterator_table table_iter; + Field_iterator_view view_iter; + Field_iterator *iterator; Item_cond_and *cond_and=new Item_cond_and(); if (!cond_and) // If not out of memory DBUG_RETURN(1); cond_and->top_level_item(); - Field **t1_field, *t2_field; - for (t1_field= t1->field; (*t1_field); t1_field++) + if (table->field_translation) { - const char *t1_field_name= (*t1_field)->field_name; + iterator= &view_iter; + view_iter.set(tab1); + } + else + { + iterator= &table_iter; + table_iter.set(tab1); + } + + Field *t1_field, *t2_field; + Item *item_t2; + for (; iterator->end(); iterator->next()) + { + const char *t1_field_name= iterator->name(); uint not_used_field_index= NO_CACHED_FIELD_INDEX; - if ((t2_field= find_field_in_table(thd, t2, t1_field_name, - strlen(t1_field_name), 0, 0, + if ((t2_field= find_field_in_table(thd, tab2, t1_field_name, + strlen(t1_field_name), &item_t2, + 0, 0, 0, ¬_used_field_index))) { - Item_func_eq *tmp=new Item_func_eq(new Item_field(*t1_field), - new Item_field(t2_field)); + if (t2_field != view_ref_found) + { + if (!(item_t2= new Item_field(t2_field))) + goto err; + /* Mark field used for table cache */ + t2_field->query_id= thd->query_id; + t2->used_keys.intersect(t2_field->part_of_key); + } + if ((t1_field= iterator->field())) + { + /* Mark field used for table cache */ + t1_field->query_id= thd->query_id; + t1->used_keys.intersect(t1_field->part_of_key); + } + Item_func_eq *tmp= new Item_func_eq(iterator->item(thd), + item_t2); if (!tmp) goto err; - /* Mark field used for table cache */ - (*t1_field)->query_id= t2_field->query_id= thd->query_id; cond_and->list.push_back(tmp); - t1->used_keys.intersect((*t1_field)->part_of_key); - t2->used_keys.intersect(t2_field->part_of_key); } } - cond_and->used_tables_cache= t1->map | t2->map; select_lex->cond_count+= cond_and->list.elements; // to prevent natural join processing during PS re-execution @@ -2856,3 +3173,54 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) } return 0; } + + +/* + open new .frm format table + + SYNOPSIS + open_new_frm() + path path to .frm + alias alias for table + db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..) + can be 0 (example in ha_example_table) + prgflag READ_ALL etc.. + ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. + outparam result table + table_desc TABLE_LIST descriptor + mem_root temporary MEM_ROOT for parsing +*/ +static my_bool +open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, + uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, + MEM_ROOT *mem_root) +{ + DBUG_ENTER("open_new_frm"); + LEX_STRING pathstr; + pathstr.str= (char *)path; + pathstr.length= strlen(path); + File_parser *parser= sql_parse_prepare(&pathstr, mem_root, 1); + if (parser) + { + if (!strncmp("VIEW", parser->type()->str, parser->type()->length)) + { + if (mysql_make_view(parser, table_desc)) + { + bzero(outparam, sizeof(*outparam)); // do not run repair + DBUG_RETURN(1); + } + } + else + { + /* only VIEWs are supported now */ + my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str); + bzero(outparam, sizeof(outparam)); // do not run repair + DBUG_RETURN(1); + } + } + else + { + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 68b4bc601fe..8ab7106978c 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1123,7 +1123,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, using_transactions = using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - for (; tables_used; tables_used=tables_used->next) + for (; tables_used; tables_used= tables_used->next_local) { DBUG_ASSERT(!using_transactions || tables_used->table!=0); if (tables_used->derived) @@ -1155,7 +1155,7 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) if (query_cache_size > 0) { DUMP(this); - for (; tables_used; tables_used=tables_used->next) + for (; tables_used; tables_used= tables_used->next) { invalidate_table((byte*) tables_used->key, tables_used->key_length); DBUG_PRINT("qcache", (" db %s, table %s", tables_used->key, @@ -1188,7 +1188,7 @@ void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) if (query_cache_size > 0) { DUMP(this); - for (; tables_used; tables_used= tables_used->next) + for (; tables_used; tables_used= tables_used->next_local) { if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE)) invalidate_table(tables_used->table); @@ -2075,7 +2075,9 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, Query_cache_block_table *block_table = block->table(0); - for (n=0; tables_used; tables_used=tables_used->next, n++, block_table++) + for (n= 0; + tables_used; + tables_used= tables_used->next_global, n++, block_table++) { DBUG_PRINT("qcache", ("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx", @@ -2622,7 +2624,7 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, lex->select_lex.options, (int) thd->variables.query_cache_type)); - for (; tables_used; tables_used= tables_used->next) + for (; tables_used; tables_used= tables_used->next_global) { table_count++; DBUG_PRINT("qcache", ("table %s, db %s, type %u", @@ -2689,7 +2691,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, { DBUG_ENTER("Query_cache::ask_handler_allowance"); - for (; tables_used; tables_used= tables_used->next) + for (; tables_used; tables_used= tables_used->next_global) { TABLE *table= tables_used->table; if (!ha_caching_allowed(thd, table->table_cache_key, diff --git a/sql/sql_class.h b/sql/sql_class.h index 08f73309a85..00d32543da0 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -403,6 +403,8 @@ struct system_variables ulong tx_isolation; /* Determines which non-standard SQL behaviour should be enabled */ ulong sql_mode; + /* check of key presence in updatable view */ + ulong sql_updatable_view_key; ulong default_week_format; ulong max_seeks_for_key; ulong range_alloc_block_size; @@ -633,7 +635,7 @@ private: a thread/connection descriptor */ -class THD :public ilink, +class THD :public ilink, public Statement { public: @@ -1165,14 +1167,18 @@ public: class select_insert :public select_result { public: + TABLE_LIST *table_list; TABLE *table; List<Item> *fields; ulonglong last_insert_id; COPY_INFO info; + bool insert_into_view; - select_insert(TABLE *table_par, List<Item> *fields_par, - enum_duplicates duplic) - :table(table_par), fields(fields_par), last_insert_id(0) + select_insert(TABLE_LIST *table_list_par, TABLE *table_par, + List<Item> *fields_par, enum_duplicates duplic) + :table_list(table_list_par), table(table_par), fields(fields_par), + last_insert_id(0), + insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); info.handle_duplicates=duplic; @@ -1189,22 +1195,21 @@ class select_insert :public select_result { class select_create: public select_insert { ORDER *group; - const char *db; - const char *name; + TABLE_LIST *create_table; List<create_field> *extra_fields; List<Key> *keys; HA_CREATE_INFO *create_info; MYSQL_LOCK *lock; Field **field; public: - select_create(const char *db_name, const char *table_name, - HA_CREATE_INFO *create_info_par, - List<create_field> &fields_par, - List<Key> &keys_par, - List<Item> &select_fields,enum_duplicates duplic) - :select_insert (NULL, &select_fields, duplic), db(db_name), - name(table_name), extra_fields(&fields_par),keys(&keys_par), - create_info(create_info_par), lock(0) + select_create (TABLE_LIST *table, + HA_CREATE_INFO *create_info_par, + List<create_field> &fields_par, + List<Key> &keys_par, + List<Item> &select_fields,enum_duplicates duplic) + :select_insert (NULL, NULL, &select_fields, duplic), create_table(table), + extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), + lock(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &values); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 9889d4a2b4a..5a42c79efe9 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -746,7 +746,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, table_list->alias= table_list->real_name; // If lower_case_table_names=2 /* Link into list */ (*tot_list_next)= table_list; - tot_list_next= &table_list->next; + tot_list_next= &table_list->next_local; deleted++; } else diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 9e425f86579..910b673dc32 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -260,7 +260,7 @@ cleanup: SYNOPSIS mysql_prepare_delete() thd - thread handler - table_list - global table list + table_list - global/local table list conds - conditions RETURN VALUE @@ -270,25 +270,25 @@ cleanup: */ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) { - TABLE_LIST *delete_table_list= ((TABLE_LIST*) thd->lex-> - select_lex.table_list.first); SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); - if (setup_conds(thd, delete_table_list, conds) || + if (setup_tables(thd, table_list, conds) || + setup_conds(thd, table_list, conds) || setup_ftfuncs(select_lex)) DBUG_RETURN(-1); - if (find_real_table_in_list(table_list->next, - table_list->db, table_list->real_name)) + if (!table_list->updatable || check_key_in_view(thd, table_list)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(-1); } - if (thd->current_arena && select_lex->first_execution) + if (find_real_table_in_list(table_list->next_global, + table_list->db, table_list->real_name)) { - select_lex->prep_where= select_lex->where; - select_lex->first_execution= 0; + my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); + DBUG_RETURN(-1); } + select_lex->fix_prepare_information(thd, conds); DBUG_RETURN(0); } @@ -305,6 +305,73 @@ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) return file->cmp_ref((const byte*)a, (const byte*)b); } +/* + make delete specific preparation and checks after opening tables + + SYNOPSIS + mysql_multi_delete_prepare() + thd thread handler + + RETURN + 0 OK + -1 Error +*/ + +int mysql_multi_delete_prepare(THD *thd) +{ + LEX *lex= thd->lex; + TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxilliary_table_list.first; + TABLE_LIST *target_tbl; + int res= 0; + DBUG_ENTER("mysql_multi_delete_prepare"); + + /* + setup_tables() need for VIEWs. JOIN::prepare() will not do it second + time. + + lex->query_tables also point on local list of DELETE SELECT_LEX + */ + if (setup_tables(thd, lex->query_tables, &lex->select_lex.where)) + DBUG_RETURN(-1); + + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (target_tbl= (TABLE_LIST*) aux_tables; + target_tbl; + target_tbl= target_tbl->next_local) + { + target_tbl->table= target_tbl->correspondent_table->table; + if (!target_tbl->correspondent_table->updatable || + check_key_in_view(thd, target_tbl->correspondent_table)) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name, + "DELETE"); + DBUG_RETURN(-1); + } + /* + Check are deleted table used somewhere inside subqueries. + + Multi-delete can't be constructed over-union => we always have + single SELECT on top and have to check underlaying SELECTs of it + */ + for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit(); + un; + un= un->next_unit()) + { + if (un->first_select()->linkage != DERIVED_TABLE_TYPE && + un->check_updateable(target_tbl->correspondent_table->db, + target_tbl->correspondent_table->real_name)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), + target_tbl->correspondent_table->real_name); + res= -1; + break; + } + } + } + DBUG_RETURN(res); +} + + multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables_arg) : delete_tables(dt), thd(thd_arg), deleted(0), found(0), @@ -337,7 +404,7 @@ multi_delete::initialize_tables(JOIN *join) DBUG_RETURN(1); table_map tables_to_delete_from=0; - for (walk= delete_tables ; walk ; walk=walk->next) + for (walk= delete_tables; walk; walk= walk->next_local) tables_to_delete_from|= walk->table->map; walk= delete_tables; @@ -349,7 +416,7 @@ multi_delete::initialize_tables(JOIN *join) { /* We are going to delete from this table */ TABLE *tbl=walk->table=tab->table; - walk=walk->next; + walk= walk->next_local; /* Don't use KEYREAD optimization on this table */ tbl->no_keyread=1; tbl->used_keys.clear_all(); @@ -363,7 +430,7 @@ multi_delete::initialize_tables(JOIN *join) } walk= delete_tables; tempfiles_ptr= tempfiles; - for (walk=walk->next ; walk ; walk=walk->next) + for (walk= walk->next_local ;walk ;walk= walk->next_local) { TABLE *table=walk->table; *tempfiles_ptr++= new Unique (refpos_order_cmp, @@ -378,9 +445,9 @@ multi_delete::initialize_tables(JOIN *join) multi_delete::~multi_delete() { - for (table_being_deleted=delete_tables ; - table_being_deleted ; - table_being_deleted=table_being_deleted->next) + for (table_being_deleted= delete_tables; + table_being_deleted; + table_being_deleted= table_being_deleted->next_local) { TABLE *t=table_being_deleted->table; free_io_cache(t); // Alloced by unique @@ -400,9 +467,9 @@ bool multi_delete::send_data(List<Item> &values) int secure_counter= -1; DBUG_ENTER("multi_delete::send_data"); - for (table_being_deleted=delete_tables ; - table_being_deleted ; - table_being_deleted=table_being_deleted->next, secure_counter++) + for (table_being_deleted= delete_tables; + table_being_deleted; + table_being_deleted= table_being_deleted->next_local, secure_counter++) { TABLE *table=table_being_deleted->table; @@ -419,7 +486,8 @@ bool multi_delete::send_data(List<Item> &values) table->status|= STATUS_DELETED; if (!(error=table->file->delete_row(table->record[0]))) deleted++; - else if (!table_being_deleted->next || table_being_deleted->table->file->has_transactions()) + else if (!table_being_deleted->next_local || + table_being_deleted->table->file->has_transactions()) { table->file->print_error(error,MYF(0)); DBUG_RETURN(1); @@ -489,9 +557,9 @@ int multi_delete::do_deletes(bool from_send_error) if (from_send_error) { /* Found out table number for 'table_being_deleted*/ - for (TABLE_LIST *aux=delete_tables; + for (TABLE_LIST *aux= delete_tables; aux != table_being_deleted; - aux=aux->next) + aux= aux->next_local) counter++; } else @@ -500,9 +568,9 @@ int multi_delete::do_deletes(bool from_send_error) do_delete= 0; if (!found) DBUG_RETURN(0); - for (table_being_deleted=table_being_deleted->next; - table_being_deleted ; - table_being_deleted=table_being_deleted->next, counter++) + for (table_being_deleted= table_being_deleted->next_local; + table_being_deleted; + table_being_deleted= table_being_deleted->next_local, counter++) { TABLE *table = table_being_deleted->table; if (tempfiles[counter]->get(table)) diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 48bc6f4c5a2..3137890f2ba 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -52,15 +52,17 @@ mysql_handle_derived(LEX *lex) { for (TABLE_LIST *cursor= sl->get_table_list(); cursor; - cursor= cursor->next) + cursor= cursor->next_local) { int res; - if (cursor->derived && (res=mysql_derived(lex->thd, lex, - cursor->derived, - cursor))) + if (cursor->derived && (res= mysql_derived(lex->thd, lex, + cursor->derived, + cursor))) { return res; } + else if (cursor->ancestor) + cursor->set_ancestor(); } if (lex->describe) { @@ -148,10 +150,11 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, derived_result->set_table(table); /* - if it is preparation PS only then we do not need real data and we - can skip execution (and parameters is not defined, too) + if it is preparation PS only or commands that need only VIEW structure + then we do not need real data and we can skip execution (and parameters + is not defined, too) */ - if (!thd->only_prepare()) + if (!thd->only_prepare() && !lex->only_view_structure()) { if (is_union) { @@ -197,11 +200,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, { org_table_list->real_name= table->real_name; org_table_list->table= table; - if (org_table_list->table_list) - { - org_table_list->table_list->real_name= table->real_name; - org_table_list->table_list->table= table; - } table->derived_select_number= first_select->select_number; table->tmp_table= TMP_TABLE; #ifndef NO_EMBEDDED_ACCESS_CHECKS diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 538ca3fd1f0..8d06d3f0017 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -166,7 +166,7 @@ int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed) if (tables) { - for (tl_item= tables ; tl_item; tl_item= tl_item->next) + for (tl_item= tables ; tl_item; tl_item= tl_item->next_local) { mysql_ha_close(thd, tl_item, /*dont_send_ok*/ 1, /*dont_lock*/ 1, /*no_alias*/ 1); @@ -249,7 +249,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, it++; // Skip first NULL field - insert_fields(thd,tables,tables->db,tables->alias,&it); + insert_fields(thd, tables, tables->db, tables->alias, &it, 0); select_limit+=offset_limit; protocol->send_fields(&list,1); diff --git a/sql/sql_help.cc b/sql/sql_help.cc index eabe66d33bf..08a8fc626cc 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -84,12 +84,11 @@ static bool init_fields(THD *thd, TABLE_LIST *tables, DBUG_ENTER("init_fields"); for (; count-- ; find_fields++) { - TABLE_LIST *not_used; /* We have to use 'new' here as field will be re_linked on free */ Item_field *field= new Item_field("mysql", find_fields->table_name, find_fields->field_name); if (!(find_fields->field= find_field_in_tables(thd, field, tables, - ¬_used, TRUE))) + 0, TRUE, 1))) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -622,16 +621,15 @@ int mysqld_help(THD *thd, const char *mask) bzero((gptr)tables,sizeof(tables)); tables[0].alias= tables[0].real_name= (char*) "help_topic"; tables[0].lock_type= TL_READ; - tables[0].next= &tables[1]; + tables[0].next_global= tables[0].next_local= &tables[1]; tables[1].alias= tables[1].real_name= (char*) "help_category"; tables[1].lock_type= TL_READ; - tables[1].next= &tables[2]; + tables[1].next_global= tables[1].next_local= &tables[2]; tables[2].alias= tables[2].real_name= (char*) "help_relation"; tables[2].lock_type= TL_READ; - tables[2].next= &tables[3]; + tables[2].next_global= tables[2].next_local= &tables[3]; tables[3].alias= tables[3].real_name= (char*) "help_keyword"; tables[3].lock_type= TL_READ; - tables[3].next= 0; tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql"; List<String> topics_list, categories_list, subcategories_list; @@ -645,8 +643,12 @@ int mysqld_help(THD *thd, const char *mask) res= -1; goto end; } - /* Init tables and fields to be usable from items */ - setup_tables(tables); + /* + Init tables and fields to be usable from items + + tables do not contain VIEWs => we can pass 0 as conds + */ + setup_tables(thd, tables, 0); memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); if (init_fields(thd, tables, used_fields, array_elements(used_fields))) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3acebf5cadc..6e30f2f9c76 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -46,10 +46,11 @@ static void unlink_blobs(register TABLE *table); to timestamp field, depending on if timestamp should be updated or not. */ -int -check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, - List<Item> &values, ulong counter) +static int +check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, + List<Item> &values, ulong counter, bool check_unique) { + TABLE *table= table_list->table; if (fields.elements == 0 && values.elements != 0) { if (values.elements != table->fields) @@ -60,14 +61,21 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, return -1; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (grant_option && - check_grant_all_columns(thd,INSERT_ACL,table)) - return -1; + { + Field_iterator_table fields; + fields.set_table(table); + if (grant_option && + check_grant_all_columns(thd, INSERT_ACL, &table->grant, + table->table_cache_key, table->real_name, + &fields)) + return -1; + } #endif table->timestamp_default_now= table->timestamp_on_update_now= 0; } else { // Part field list + TABLE_LIST *save_next= table_list->next_local; if (fields.elements != values.elements) { my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, @@ -75,19 +83,17 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, MYF(0),counter); return -1; } - TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); - table_list.db= table->table_cache_key; - table_list.real_name= table_list.alias= table->table_name; - table_list.table=table; - table_list.grant=table->grant; + table_list->next_local= 0; thd->dupp_field=0; - if (setup_tables(&table_list) || - setup_fields(thd, 0, &table_list,fields,1,0,0)) + if (setup_fields(thd, 0, table_list, fields, 1, 0, 0)) + { + table_list->next_local= save_next; return -1; + } + table_list->next_local= save_next; - if (thd->dupp_field) + if (check_unique && thd->dupp_field) { my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name); return -1; @@ -130,8 +136,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, char *query= thd->query; #endif thr_lock_type lock_type = table_list->lock_type; - TABLE_LIST *insert_table_list= (TABLE_LIST*) - thd->lex->select_lex.table_list.first; DBUG_ENTER("mysql_insert"); /* @@ -170,8 +174,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, if ((table= delayed_get_table(thd,table_list)) && !thd->is_fatal_error) { res= 0; - if (table_list->next) /* if sub select */ - res= open_and_lock_tables(thd, table_list->next); + if (table_list->next_global) /* if sub select */ + res= open_and_lock_tables(thd, table_list->next_global); + /* + First is not processed by open_and_lock_tables() => we need set + updateability flags "by hands". + */ + if (!table_list->derived && !table_list->view) + table_list->updatable= 1; // usual table } else { @@ -194,17 +204,17 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, if (duplic == DUP_UPDATE && !table->insert_values) { /* it should be allocated before Item::fix_fields() */ - table->insert_values= + table->insert_values= (byte *)alloc_root(&thd->mem_root, table->rec_buff_length); if (!table->insert_values) goto abort; } - if (mysql_prepare_insert(thd, table_list, insert_table_list, table, - fields, values, update_fields, - update_values, duplic)) + if (mysql_prepare_insert(thd, table_list, table, fields, values, + update_fields, update_values, duplic)) goto abort; + // is table which we are changing used somewhere in other parts of query value_count= values->elements; while ((values= its++)) { @@ -216,7 +226,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, MYF(0),counter); goto abort; } - if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0)) + if (setup_fields(thd, 0, table_list, *values, 0, 0, 0)) goto abort; } its.rewind (); @@ -428,33 +438,121 @@ abort: /* + Additional check for insertability for VIEW + + SYNOPSIS + check_view_insertability() + view - reference on VIEW + + RETURN + FALSE - OK + TRUE - can't be used for insert +*/ + +static bool check_view_insertability(TABLE_LIST *view, ulong query_id) +{ + DBUG_ENTER("check_key_in_view"); + + TABLE *table= view->table; + Item **trans= view->field_translation; + Field **field_ptr= table->field; + uint num= view->view->select_lex.item_list.elements; + ulong other_query_id= query_id - 1; + DBUG_ASSERT(view->table != 0 && view->field_translation != 0); + + view->contain_auto_increment= 0; + /* check simplicity and prepare unique test of view */ + for (uint i= 0; i < num; i++) + { + /* simple SELECT list entry (field without expression) */ + if (trans[i]->type() != Item::FIELD_ITEM) + DBUG_RETURN(TRUE); + if (((Item_field *)trans[i])->field->type() == Field::NEXT_NUMBER) + view->contain_auto_increment= 1; + /* prepare unique test */ + ((Item_field *)trans[i])->field->query_id= other_query_id; + } + /* unique test */ + for (uint i= 0; i < num; i++) + { + Item_field *field= (Item_field *)trans[i]; + if (field->field->query_id == query_id) + DBUG_RETURN(TRUE); + field->field->query_id= query_id; + } + + /* VIEW contain all fields without default value */ + for (; *field_ptr; ++field_ptr) + { + Field *field= *field_ptr; + /* field have not default value */ + if ((field->type() == FIELD_TYPE_BLOB) && + (table->timestamp_field != field || + field->unireg_check == Field::TIMESTAMP_UN_FIELD)) + { + uint i= 0; + for (; i < num; i++) + { + if (((Item_field *)trans[i])->field == *field_ptr) + break; + } + if (i >= num) + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); +} + + +/* Prepare items in INSERT statement SYNOPSIS mysql_prepare_insert() thd - thread handler - table_list - global table list - insert_table_list - local table list of INSERT SELECT_LEX + table_list - global/local table list RETURN VALUE 0 - OK -1 - error (message is not sent to user) */ -int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, - TABLE_LIST *insert_table_list, TABLE *table, +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, List<Item> &fields, List_item *values, List<Item> &update_fields, List<Item> &update_values, enum_duplicates duplic) { + bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_prepare_insert"); - if (check_insert_fields(thd, table, fields, *values, 1) || - setup_tables(insert_table_list) || - setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) || + + /* TODO: use this condition for 'WHITH CHECK OPTION' */ + Item *unused_conds= 0; + if (setup_tables(thd, table_list, &unused_conds)) + DBUG_RETURN(-1); + + if (insert_into_view && !fields.elements) + { + thd->lex->empty_field_list_on_rset= 1; + 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); + } + + if (check_insert_fields(thd, table_list, fields, *values, 1, + !insert_into_view) || + setup_fields(thd, 0, table_list, *values, 0, 0, 0) || (duplic == DUP_UPDATE && - (setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) || - setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0)))) + (setup_fields(thd, 0, table_list, update_fields, 0, 0, 0) || + setup_fields(thd, 0, table_list, update_values, 0, 0, 0)))) DBUG_RETURN(-1); - 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); @@ -1440,13 +1538,55 @@ bool delayed_insert::handle_inserts(void) Store records in INSERT ... SELECT * ***************************************************************************/ + +/* + make insert specific preparation and checks after opening tables + + SYNOPSIS + mysql_insert_select_prepare() + thd thread handler + + RETURN + 0 OK + -1 Error +*/ + +int mysql_insert_select_prepare(THD *thd) +{ + LEX *lex= thd->lex; + TABLE_LIST *table_list= lex->query_tables; + bool insert_into_view= (table_list->view != 0); + DBUG_ENTER("mysql_insert_select_prepare"); + + if (setup_tables(thd, table_list, &lex->select_lex.where)) + DBUG_RETURN(-1); + + if (insert_into_view && !lex->field_list.elements) + { + lex->empty_field_list_on_rset= 1; + insert_view_fields(&lex->field_list, 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) +} + + int select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { DBUG_ENTER("select_insert::prepare"); unit= u; - if (check_insert_fields(thd,table,*fields,values,1)) + if (check_insert_fields(thd, table_list, *fields, values, 1, + !insert_into_view)) DBUG_RETURN(1); restore_record(table,default_values); // Get empty record @@ -1603,7 +1743,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_ENTER("select_create::prepare"); unit= u; - table= create_table_from_items(thd, create_info, db, name, + table= create_table_from_items(thd, create_info, create_table, extra_fields, keys, &values, &lock); if (!table) DBUG_RETURN(-1); // abort() deletes table @@ -1694,7 +1834,7 @@ void select_create::abort() if (!table->tmp_table) hash_delete(&open_cache,(byte*) table); if (!create_info->table_existed) - quick_rm_table(table_type,db,name); + quick_rm_table(table_type, create_table->db, create_table->real_name); table=0; } VOID(pthread_mutex_unlock(&LOCK_open)); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 723399e275b..d96c6371ff1 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -995,7 +995,7 @@ void st_select_lex::init_query() embedding= 0; item_list.empty(); join= 0; - where= 0; + where= prep_where= 0; olap= UNSPECIFIED_OLAP_TYPE; having_fix_field= 0; resolve_mode= NOMATTER_MODE; @@ -1003,7 +1003,6 @@ void st_select_lex::init_query() conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_having_items= 0; - prep_where= 0; subquery_in_having= explicit_limit= 0; first_execution= 1; first_cond_optimization= 1; @@ -1269,122 +1268,6 @@ bool st_select_lex::test_limit() return(0); } -/* - Interface method of table list creation for query - - SYNOPSIS - st_select_lex_unit::create_total_list() - thd THD pointer - result pointer on result list of tables pointer - check_derived force derived table chacking (used for creating - table list for derived query) - DESCRIPTION - This is used for UNION & subselect to create a new table list of all used - tables. - The table_list->table entry in all used tables are set to point - to the entries in this list. - - RETURN - 0 - OK - !0 - error -*/ -bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex, - TABLE_LIST **result_arg) -{ - *result_arg= 0; - res= create_total_list_n_last_return(thd_arg, lex, &result_arg); - return res; -} - -/* - Table list creation for query - - SYNOPSIS - st_select_lex_unit::create_total_list() - thd THD pointer - lex pointer on LEX stricture - result pointer on pointer on result list of tables pointer - - DESCRIPTION - This is used for UNION & subselect to create a new table list of all used - tables. - The table_list->table_list in all tables of global list are set to point - to the local SELECT_LEX entries. - - RETURN - 0 - OK - !0 - error -*/ -bool st_select_lex_unit:: -create_total_list_n_last_return(THD *thd_arg, - st_lex *lex, - TABLE_LIST ***result_arg) -{ - TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first; - TABLE_LIST **new_table_list= *result_arg, *aux; - SELECT_LEX *sl= (SELECT_LEX*)slave; - - /* - iterate all inner selects + fake_select (if exists), - fake_select->next_select() always is 0 - */ - for (; - sl; - sl= (sl->next_select() ? - sl->next_select() : - (sl == fake_select_lex ? - 0 : - fake_select_lex))) - { - // check usage of ORDER BY in union - if (sl->order_list.first && sl->next_select() && !sl->braces && - sl->linkage != GLOBAL_OPTIONS_TYPE) - { - net_printf(thd_arg,ER_WRONG_USAGE,"UNION","ORDER BY"); - return 1; - } - - for (SELECT_LEX_UNIT *inner= sl->first_inner_unit(); - inner; - inner= inner->next_unit()) - { - if (inner->create_total_list_n_last_return(thd, lex, - &slave_list_last)) - return 1; - } - - if ((aux= (TABLE_LIST*) sl->table_list.first)) - { - TABLE_LIST *next_table; - for (; aux; aux= next_table) - { - TABLE_LIST *cursor; - next_table= aux->next; - /* Add to the total table list */ - if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux, - sizeof(*aux)))) - { - send_error(thd,0); - return 1; - } - *new_table_list= cursor; - cursor->table_list= aux; - new_table_list= &cursor->next; - *new_table_list= 0; // end result list - aux->table_list= cursor; - } - } - } - - if (slave_list_first) - { - *new_table_list= slave_list_first; - new_table_list= slave_list_last; - } - *result_arg= new_table_list; - return 0; -} - st_select_lex_unit* st_select_lex_unit::master_unit() { @@ -1539,7 +1422,7 @@ bool st_select_lex_unit::check_updateable(char *db, char *table) */ bool st_select_lex::check_updateable(char *db, char *table) { - if (find_real_table_in_list(get_table_list(), db, table)) + if (find_real_table_in_local_list(get_table_list(), db, table)) return 1; for (SELECT_LEX_UNIT *un= first_inner_unit(); @@ -1599,6 +1482,16 @@ void st_select_lex::print_order(String *str, ORDER *order) void st_select_lex::print_limit(THD *thd, String *str) { + Item_subselect *item= master_unit()->item; + if (item && + (item->substype() == Item_subselect::EXISTS_SUBS || + item->substype() == Item_subselect::IN_SUBS || + item->substype() == Item_subselect::ALL_SUBS)) + { + DBUG_ASSERT(select_limit == 1L && offset_limit == 0L); + return; + } + if (!thd) thd= current_thd; @@ -1619,6 +1512,98 @@ void st_select_lex::print_limit(THD *thd, String *str) } } + +/* + Check is merging algorithm can be used on this VIEW + + SYNOPSIS + st_lex::can_be_merged() + + RETURN + FALSE - only temporary table algorithm can be used + TRUE - merge algorithm can be used +*/ + +bool st_lex::can_be_merged() +{ + // TODO: do not forget implement case when select_lex.table_list.elements==0 + + /* find non VIEW subqueries/unions */ + uint selects= 0; + for (SELECT_LEX *sl= all_selects_list; + sl && selects <= 1; + sl= sl->next_select_in_list()) + if (sl->parent_lex == this) + selects++; + + return (selects <= 1 && + select_lex.order_list.elements == 0 && + select_lex.group_list.elements == 0 && + select_lex.having == 0 && + select_lex.table_list.elements == 1 && + !(select_lex.options & SELECT_DISTINCT) && + select_lex.select_limit == HA_POS_ERROR); +} + +/* + check if command can use VIEW with MERGE algorithm + + SYNOPSIS + st_lex::can_use_merged() + + RETURN + FALSE - command can't use merged VIEWs + TRUE - VIEWs with MERGE algorithms can be used +*/ + +bool st_lex::can_use_merged() +{ + switch (sql_command) + { + case SQLCOM_SELECT: + case SQLCOM_CREATE_TABLE: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_INSERT: + case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: + return TRUE; + default: + return FALSE; + } +} + +/* + Detect that we need only table structure of derived table/view + + SYNOPSIS + only_view_structure() + + RETURN + TRUE yes, we need only structure + FALSE no, we need data +*/ +bool st_lex::only_view_structure() +{ + switch(sql_command) + { + case SQLCOM_SHOW_CREATE: + case SQLCOM_SHOW_TABLES: + case SQLCOM_SHOW_FIELDS: + case SQLCOM_REVOKE_ALL: + case SQLCOM_REVOKE: + case SQLCOM_GRANT: + case SQLCOM_CREATE_VIEW: + return TRUE; + default: + return FALSE; + } +} + + /* initialize limit counters @@ -1627,6 +1612,7 @@ void st_select_lex::print_limit(THD *thd, String *str) values - SELECT_LEX with initial values for counters sl - SELECT_LEX for options set */ + void st_select_lex_unit::set_limit(SELECT_LEX *values, SELECT_LEX *sl) { @@ -1645,35 +1631,83 @@ void st_select_lex_unit::set_limit(SELECT_LEX *values, SYNOPSIS unlink_first_table() - tables Global table list - global_first Save first global table here - local_first Save first local table here + link_to_local do we need link this table to local - NORES - global_first & local_first are used to save result for link_first_table_back + NOTES + We rely on fact that first table in both list are same or local list + is empty RETURN - global list without first table + unlinked table */ -TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables, - TABLE_LIST **global_first, - TABLE_LIST **local_first) +TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) { - *global_first= tables; - *local_first= (TABLE_LIST*)select_lex.table_list.first; - /* - Exclude from global table list - */ - tables= tables->next; - /* - and from local list if it is not the same - */ - select_lex.table_list.first= ((&select_lex != all_selects_list) ? - (byte*) (*local_first)->next : - (byte*) tables); - (*global_first)->next= 0; - return tables; + TABLE_LIST *first; + if ((first= query_tables)) + { + /* + Exclude from global table list + */ + if ((query_tables= query_tables->next_global)) + query_tables->prev_global= &query_tables; + first->next_global= 0; + + /* + and from local list if it is not empty + */ + if ((*link_to_local= test(select_lex.table_list.first))) + { + select_lex.table_list.first= (byte*) first->next_local; + select_lex.table_list.elements--; //safety + first->next_local= 0; + /* + reorder global list to keep first tables the same in both lists + (if it is need) + */ + first_lists_tables_same(); + } + } + return first; +} + + +/* + Bring first local table of first most outer select to first place in global + table list + + SYNOPSYS + st_lex::first_lists_tables_same() + + NOTES + In many cases first table of main SELECT_LEX have special meaning => + check that it is first table in global list and relink it first in + queries_tables list if it is necessary (we need such relinking only + for queries with subqueries in select list, in this case tables of + subqueries will go to global list first) +*/ + +void st_lex::first_lists_tables_same() +{ + TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first; + if (query_tables != first_table && first_table != 0) + { + if (query_tables_last == &first_table->next_global) + query_tables_last= first_table->prev_global; + TABLE_LIST *next= *first_table->prev_global= first_table->next_global; + first_table->next_global= 0; + if (next) + next->prev_global= first_table->prev_global; + /* include in new place */ + first_table->next_global= query_tables; + /* + we are sure that above is not 0, because first_table was not + first table in global list => we can do following without check + */ + query_tables->prev_global= &first_table->next_global; + first_table->prev_global= &query_tables; + query_tables= first_table; + } } @@ -1682,36 +1716,54 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables, SYNOPSIS link_first_table_back() - tables Global table list - global_first Saved first global table - local_first Saved first local table + link_to_local do we need link this table to local RETURN global list */ -TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables, - TABLE_LIST *global_first, - TABLE_LIST *local_first) + +void st_lex::link_first_table_back(TABLE_LIST *first, + bool link_to_local) { - global_first->next= tables; - if (&select_lex != all_selects_list) + if (first) { - /* - we do not touch local table 'next' field => we need just - put the table in the list - */ - select_lex.table_list.first= (byte*) local_first; + if ((first->next_global= query_tables)) + query_tables->prev_global= &first->next_global; + query_tables= first; + + if (link_to_local) + { + first->next_local= (TABLE_LIST*) select_lex.table_list.first; + select_lex.table_list.first= (byte*) first; + select_lex.table_list.elements++; //safety + } + } +} + + +/* + fix some structures at the end of preparation + + SYNOPSIS + st_select_lex::fix_prepare_information + thd thread handler + conds pointer on conditions which will be used for execution statement +*/ + +void st_select_lex::fix_prepare_information(THD *thd, Item **conds) +{ + if (thd->current_arena && first_execution) + { + prep_where= where; + first_execution= 0; } - else - select_lex.table_list.first= (byte*) global_first; - return global_first; } /* - There are st_select_lex::add_table_to_list & + There are st_select_lex::add_table_to_list & st_select_lex::set_lock_for_tables are in sql_parse.cc - st_select_lex::print is in sql_select.h + st_select_lex::print is in sql_select.cc st_select_lex_unit::prepare, st_select_lex_unit::exec, st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7030a3ab2d4..014a009f951 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -83,8 +83,8 @@ enum enum_sql_command { SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC, SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC, - SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, + SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, /* This should be the last !!! */ SQLCOM_END }; @@ -98,6 +98,23 @@ enum suid_behaviour IS_DEFAULT_SUID= 0, IS_NOT_SUID, IS_SUID }; +#define DERIVED_SUBQUERY 1 +#define DERIVED_VIEW 2 + +enum enum_view_create_mode +{ + VIEW_CREATE_NEW, // check that there are not such VIEW/table + VIEW_ALTER, // check that VIEW .frm with such name exists + VIEW_CREATE_OR_REPLACE // check only that there are not such table +}; + +enum enum_drop_mode +{ + DROP_DEFAULT, // mode is not specified + DROP_CASCADE, // CASCADE option + DROP_RESTRICT // RESTRICT option +}; + typedef List<Item> List_item; typedef struct st_lex_master_info @@ -293,6 +310,8 @@ public: friend class st_select_lex_unit; friend bool mysql_new_select(struct st_lex *lex, bool move_down); + friend my_bool mysql_make_view (File_parser *parser, + TABLE_LIST *table); private: void fast_exclude(); }; @@ -355,7 +374,6 @@ public: bool describe; /* union exec() called for EXPLAIN */ void init_query(); - bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result); st_select_lex_unit* master_unit(); st_select_lex* outer_select(); st_select_lex* first_select() @@ -390,9 +408,6 @@ public: friend void mysql_init_query(THD *thd, bool lexonly); friend int subselect_union_engine::exec(); -private: - bool create_total_list_n_last_return(THD *thd, st_lex *lex, - TABLE_LIST ***result); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -405,6 +420,8 @@ public: char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */ Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ + /* point on lex in which it was created, used in view subquery detection */ + st_lex *parent_lex; enum olap_type olap; SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ List<Item> item_list; /* list of fields & expressions */ @@ -555,6 +572,7 @@ public: void print(THD *thd, String *str); static void print_order(String *str, ORDER *order); void print_limit(THD *thd, String *str); + void fix_prepare_information(THD *thd, Item **conds); }; typedef class st_select_lex SELECT_LEX; @@ -618,6 +636,9 @@ typedef struct st_lex gptr yacc_yyss,yacc_yyvs; THD *thd; CHARSET_INFO *charset; + TABLE_LIST *query_tables; /* global list of all tables in this query */ + /* last element next_global of previous list */ + TABLE_LIST **query_tables_last; List<key_part_spec> col_list; List<key_part_spec> ref_list; @@ -630,6 +651,7 @@ typedef struct st_lex List<List_item> many_values; List<set_var_base> var_list; List<Item_param> param_list; + List<LEX_STRING> view_list; // view list (list of field names in view) SQL_LIST proc_list, auxilliary_table_list, save_list; TYPELIB *interval; create_field *last_field; @@ -649,15 +671,21 @@ typedef struct st_lex enum enum_ha_read_modes ha_read_mode; enum ha_rkey_function ha_rkey_mode; enum enum_var_type option_type; + enum enum_view_create_mode create_view_mode; + enum enum_drop_mode drop_mode; uint uint_geom_type; uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint slave_thd_opt; uint8 describe; + uint8 derived_tables; + uint8 create_view_algorithm; bool drop_if_exists, drop_temporary, local_file, one_shot_set; bool in_comment, ignore_space, verbose, no_write_to_binlog; - bool derived_tables; + /* special JOIN::prepare mode: changing of query is prohibited */ + bool view_prepare_mode; bool safe_to_cache_query; + bool variables_used; ALTER_INFO alter_info; /* Prepared statements SQL syntax:*/ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ @@ -676,6 +704,12 @@ typedef struct st_lex sp_pcontext *spcont; HASH spfuns; /* Called functions */ st_sp_chistics sp_chistics; + bool only_view; /* used for SHOW CREATE TABLE/VIEW */ + /* + field_list was created for view and should be removed before PS/SP + rexecuton + */ + bool empty_field_list_on_rset; st_lex() { @@ -707,12 +741,13 @@ typedef struct st_lex un->uncacheable|= cause; } } - TABLE_LIST *unlink_first_table(TABLE_LIST *tables, - TABLE_LIST **global_first, - TABLE_LIST **local_first); - TABLE_LIST *link_first_table_back(TABLE_LIST *tables, - TABLE_LIST *global_first, - TABLE_LIST *local_first); + TABLE_LIST *unlink_first_table(bool *link_to_local); + void link_first_table_back(TABLE_LIST *first, bool link_to_local); + void first_lists_tables_same(); + + bool can_be_merged(); + bool can_use_merged(); + bool only_view_structure(); } LEX; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index c7a64ea8699..47295d4c6a0 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -113,8 +113,16 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(-1); } - if (!(table = open_ltable(thd,table_list,lock_type))) + table_list->lock_type= lock_type; + if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(-1); + /* TODO: add key check when we will support VIEWs in LOAD */ + if (!table_list->updatable) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD"); + DBUG_RETURN(-1); + } + table= table_list->table; transactional_table= table->file->has_transactions(); log_delayed= (transactional_table || table->tmp_table); @@ -127,7 +135,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else { // Part field list thd->dupp_field=0; - if (setup_tables(table_list) || + /* TODO: use this conds for 'WITH CHECK OPTIONS' */ + Item *unused_conds= 0; + if (setup_tables(thd, table_list, &unused_conds) || setup_fields(thd, 0, table_list, fields, 1, 0, 0)) DBUG_RETURN(-1); if (thd->dupp_field) diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index 46f1e6c156e..20fd7fe2ee0 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -152,7 +152,8 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) List<Item> all_fields(select_lex->item_list); - if (setup_tables((TABLE_LIST *)select_lex->table_list.first) || + if (setup_tables(lex->thd, (TABLE_LIST *)select_lex->table_list.first + &select_lex->where) || setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first, select_lex->item_list, 1, &all_fields,1) || setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 34a668472e2..fe2d6f8faa1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -540,6 +540,8 @@ void init_update_queries(void) uc_update_queries[SQLCOM_DELETE_MULTI]=1; uc_update_queries[SQLCOM_DROP_INDEX]=1; uc_update_queries[SQLCOM_UPDATE_MULTI]=1; + uc_update_queries[SQLCOM_CREATE_VIEW]=1; + uc_update_queries[SQLCOM_DROP_VIEW]=1; } bool is_update_query(enum enum_sql_command command) @@ -1181,10 +1183,10 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) db = (db && db[0]) ? db : thd->db; if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory - table_list->db = db; - table_list->real_name = table_list->alias = tbl_name; - table_list->lock_type = TL_READ_NO_INSERT; - table_list->next = 0; + table_list->db= db; + table_list->real_name= table_list->alias= tbl_name; + table_list->lock_type= TL_READ_NO_INSERT; + table_list->prev_global= &table_list; // can be removed after merge with 4.1 if (!db || check_db_name(db)) { @@ -1877,11 +1879,35 @@ mysql_execute_command(THD *thd) { int res= 0; LEX *lex= thd->lex; - TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first; + /* first table of first SELECT_LEX */ + TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + /* list of all tables in query */ + TABLE_LIST *all_tables; + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ SELECT_LEX *select_lex= &lex->select_lex; + /* most outer SELECT_LEX_UNIT of query */ SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_execute_command"); + /* + In many cases first table of main SELECT_LEX have special meaning => + check that it is first table in global list and relink it first in + queries_tables list if it is necessary (we need such relinking only + for queries with subqueries in select list, in this case tables of + subqueries will go to global list first) + + all_tables will differ from first_table only if most upper SELECT_LEX + do not contain tables. + + Because of above in place where should be at least one table in most + outer SELECT_LEX we have following check: + DBUG_ASSERT(first_table == all_tables); + DBUG_ASSERT(first_table == all_tables && first_table != 0); + */ + lex->first_lists_tables_same(); + /* should be assigned after making firts tables same */ + all_tables= lex->query_tables; + if (lex->sql_command != SQLCOM_CREATE_PROCEDURE && lex->sql_command != SQLCOM_CREATE_SPFUNCTION) { @@ -1895,7 +1921,7 @@ mysql_execute_command(THD *thd) that is not a SHOW command or a select that only access local variables, but for now this is probably good enough. */ - if (tables || &lex->select_lex != lex->all_selects_list) + if (all_tables || &lex->select_lex != lex->all_selects_list) mysql_reset_errors(thd); #ifdef HAVE_REPLICATION @@ -1905,7 +1931,7 @@ mysql_execute_command(THD *thd) Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated */ - if (all_tables_not_ok(thd,tables)) + if (all_tables_not_ok(thd, all_tables)) { /* we warn the slave SQL thread */ my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); @@ -1925,9 +1951,6 @@ mysql_execute_command(THD *thd) #endif } #endif /* !HAVE_REPLICATION */ - if (&lex->select_lex != lex->all_selects_list && - lex->unit.create_total_list(thd, lex, &tables)) - DBUG_RETURN(0); /* When option readonly is set deny operations which change tables. @@ -1953,23 +1976,24 @@ mysql_execute_command(THD *thd) } select_result *result=lex->result; - if (tables) + if (all_tables) { - res=check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - tables,0); + res= check_table_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL, + all_tables, 0); } else - res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db,0,0,0); + res= check_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, + any_db, 0, 0, 0); if (res) { res=0; break; // Error message is given } - if (!(res=open_and_lock_tables(thd,tables))) + if (!(res= open_and_lock_tables(thd, all_tables))) { if (lex->describe) { @@ -2006,7 +2030,7 @@ mysql_execute_command(THD *thd) break; } } - query_cache_store_query(thd, tables); + query_cache_store_query(thd, all_tables); res=handle_select(thd, lex, result); } } @@ -2105,8 +2129,9 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_DO: - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || - (res= open_and_lock_tables(thd,tables)))) + if (all_tables && + ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) || + (res= open_and_lock_tables(thd, all_tables)))) break; res= mysql_do(thd, *lex->insert_list); @@ -2188,41 +2213,45 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables,0) || + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_backup_table(thd, tables); + res = mysql_backup_table(thd, first_table); break; } case SQLCOM_RESTORE_TABLE: { - if (check_db_used(thd,tables) || - check_table_access(thd, INSERT_ACL, tables,0) || + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, INSERT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_restore_table(thd, tables); + res = mysql_restore_table(thd, first_table); break; } case SQLCOM_ASSIGN_TO_KEYCACHE: { - if (check_db_used(thd, tables) || - check_access(thd, INDEX_ACL, tables->db, - &tables->grant.privilege, 0, 0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_access(thd, INDEX_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length); + res= mysql_assign_to_keycache(thd, first_table, &lex->name_and_length); break; } case SQLCOM_PRELOAD_KEYS: { - if (check_db_used(thd, tables) || - check_access(thd, INDEX_ACL, tables->db, - &tables->grant.privilege, 0, 0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_access(thd, INDEX_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - res = mysql_preload_keys(thd, tables); + res = mysql_preload_keys(thd, first_table); break; } #ifdef HAVE_REPLICATION @@ -2275,19 +2304,21 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION case SQLCOM_LOAD_MASTER_TABLE: { - if (!tables->db) - tables->db=thd->db; - if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (!first_table->db) + first_table->db= thd->db; + if (check_access(thd, CREATE_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; /* purecov: inspected */ if (grant_option) { /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0)) + if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) goto error; } - if (strlen(tables->real_name) > NAME_LEN) + if (strlen(first_table->real_name) > NAME_LEN) { - net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name); + net_printf(thd, ER_WRONG_TABLE_NAME, first_table->real_name); break; } pthread_mutex_lock(&LOCK_active_mi); @@ -2295,7 +2326,7 @@ mysql_execute_command(THD *thd) fetch_master_table will send the error to the client on failure. Give error if the table already exists. */ - if (!fetch_master_table(thd, tables->db, tables->real_name, + if (!fetch_master_table(thd, first_table->db, first_table->real_name, active_mi, 0, 0)) { send_ok(thd); @@ -2307,12 +2338,13 @@ mysql_execute_command(THD *thd) case SQLCOM_CREATE_TABLE: { - /* Skip first table, which is the table we are creating */ - TABLE_LIST *create_table, *create_table_local; - tables= lex->unlink_first_table(tables, &create_table, - &create_table_local); + DBUG_ASSERT(first_table == all_tables && first_table != 0); + bool link_to_local; + // Skip first table, which is the table we are creating + TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); + TABLE_LIST *select_tables= lex->query_tables; - if ((res= create_table_precheck(thd, tables, create_table))) + if ((res= create_table_precheck(thd, select_tables, create_table))) goto unsent_create_error; #ifndef HAVE_READLINK @@ -2321,7 +2353,7 @@ mysql_execute_command(THD *thd) /* Fix names if symlinked tables */ if (append_file_to_dir(thd, &lex->create_info.data_file_name, create_table->real_name) || - append_file_to_dir(thd,&lex->create_info.index_file_name, + append_file_to_dir(thd, &lex->create_info.index_file_name, create_table->real_name)) { res=-1; @@ -2344,28 +2376,32 @@ mysql_execute_command(THD *thd) if (select_lex->item_list.elements) // With select { select_result *result; - + /* + Is table which we are changing used somewhere in other parts + of query + */ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - find_real_table_in_list(tables, create_table->db, + find_real_table_in_list(select_tables, create_table->db, create_table->real_name)) { - net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name); + net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name); goto create_error; } - if (tables && check_table_access(thd, SELECT_ACL, tables,0)) + if (select_tables && + check_table_access(thd, SELECT_ACL, select_tables, 0)) goto create_error; // Error message is given select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex, select_lex); - if (!(res=open_and_lock_tables(thd,tables))) + if (!(res= open_and_lock_tables(thd, select_tables))) { res= -1; // If error - if ((result=new select_create(create_table->db, - create_table->real_name, - &lex->create_info, - lex->create_list, - lex->key_list, - select_lex->item_list,lex->duplicates))) + if ((result= new select_create(create_table, + &lex->create_info, + lex->create_list, + lex->key_list, + select_lex->item_list, + lex->duplicates))) { /* CREATE from SELECT give its SELECT_LEX for SELECT, @@ -2388,36 +2424,33 @@ mysql_execute_command(THD *thd) (Table_ident *)lex->name); else { - res= mysql_create_table(thd,create_table->db, - create_table->real_name, &lex->create_info, - lex->create_list, - lex->key_list,0,0,0); // do logging + res= mysql_create_table(thd, create_table->db, + create_table->real_name, &lex->create_info, + lex->create_list, + lex->key_list, 0, 0, 0); // do logging } if (!res) send_ok(thd); } - - /* put tables back for PS rexecuting */ - tables= lex->link_first_table_back(tables, create_table, - create_table_local); + lex->link_first_table_back(create_table, link_to_local); break; create_error: - res= 1; //error reported + res= 1; //error reported unsent_create_error: /* put tables back for PS rexecuting */ - tables= lex->link_first_table_back(tables, create_table, - create_table_local); + lex->link_first_table_back(create_table, link_to_local); break; } case SQLCOM_CREATE_INDEX: - if (check_one_table_access(thd, INDEX_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; if (end_active_trans(thd)) res= -1; else - res = mysql_create_index(thd, tables, lex->key_list); + res = mysql_create_index(thd, first_table, lex->key_list); break; #ifdef HAVE_REPLICATION @@ -2456,6 +2489,7 @@ unsent_create_error: #endif /* HAVE_REPLICATION */ case SQLCOM_ALTER_TABLE: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ goto error; @@ -2469,16 +2503,17 @@ unsent_create_error: break; } if (!select_lex->db) - select_lex->db=tables->db; - if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) || + select_lex->db= first_table->db; + if (check_access(thd, ALTER_ACL, first_table->db, + &first_table->grant.privilege, 0, 0) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)|| - check_merge_table_access(thd, tables->db, + check_merge_table_access(thd, first_table->db, (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ if (grant_option) { - if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0)) + if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0)) goto error; if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table @@ -2502,7 +2537,7 @@ unsent_create_error: thd->slow_command=TRUE; res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, - tables, lex->create_list, + first_table, lex->create_list, lex->key_list, select_lex->order_list.elements, (ORDER *) select_lex->order_list.first, @@ -2513,38 +2548,37 @@ unsent_create_error: #endif /*DONT_ALLOW_SHOW_COMMANDS*/ case SQLCOM_RENAME_TABLE: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *table; - if (check_db_used(thd,tables)) + if (check_db_used(thd, all_tables)) goto error; - for (table=tables ; table ; table=table->next->next) + for (table= first_table; table; table= table->next_local->next_local) { if (check_access(thd, ALTER_ACL | DROP_ACL, table->db, &table->grant.privilege,0,0) || - check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db, - &table->next->grant.privilege,0,0)) + check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db, + &table->next_local->grant.privilege, 0, 0)) goto error; if (grant_option) { - TABLE_LIST old_list,new_list; + TABLE_LIST old_list, new_list; /* we do not need initialize old_list and new_list because we will come table[0] and table->next[0] there */ - old_list=table[0]; - new_list=table->next[0]; - old_list.next=new_list.next=0; - if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) || - (!test_all_bits(table->next->grant.privilege, + old_list= table[0]; + new_list= table->next_local[0]; + if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) || + (!test_all_bits(table->next_local->grant.privilege, INSERT_ACL | CREATE_ACL) && - check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, - UINT_MAX, 0))) + check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) goto error; } } - query_cache_invalidate3(thd, tables, 0); + query_cache_invalidate3(thd, first_table, 0); if (end_active_trans(thd)) res= -1; - else if (mysql_rename_tables(thd,tables)) + else if (mysql_rename_tables(thd, first_table)) res= -1; break; } @@ -2563,34 +2597,37 @@ unsent_create_error: #endif #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ goto error; #else { - if (check_db_used(thd, tables) || - check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db, - &tables->grant.privilege,0,0)) + if (check_db_used(thd, all_tables) || + check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - res = mysqld_show_create(thd, tables); + res = mysqld_show_create(thd, first_table); break; } #endif case SQLCOM_CHECKSUM: { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0)) goto error; /* purecov: inspected */ - res = mysql_checksum_table(thd, tables, &lex->check_opt); + res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; } case SQLCOM_REPAIR: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_repair_table(thd, tables, &lex->check_opt); + res= mysql_repair_table(thd, first_table, &lex->check_opt); /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { @@ -2604,20 +2641,22 @@ unsent_create_error: } case SQLCOM_CHECK: { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_check_table(thd, tables, &lex->check_opt); + res = mysql_check_table(thd, first_table, &lex->check_opt); break; } case SQLCOM_ANALYZE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_analyze_table(thd, tables, &lex->check_opt); + res = mysql_analyze_table(thd, first_table, &lex->check_opt); /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { @@ -2632,13 +2671,14 @@ unsent_create_error: case SQLCOM_OPTIMIZE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? - mysql_recreate_table(thd, tables, 1) : - mysql_optimize_table(thd, tables, &lex->check_opt); + mysql_recreate_table(thd, first_table, 1) : + mysql_optimize_table(thd, first_table, &lex->check_opt); /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { @@ -2651,9 +2691,10 @@ unsent_create_error: break; } case SQLCOM_UPDATE: - if (update_precheck(thd, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (update_precheck(thd, all_tables)) break; - res= mysql_update(thd,tables, + res= mysql_update(thd, all_tables, select_lex->item_list, lex->value_list, select_lex->where, @@ -2666,9 +2707,10 @@ unsent_create_error: break; case SQLCOM_UPDATE_MULTI: { - if ((res= multi_update_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= multi_update_precheck(thd, all_tables))) break; - res= mysql_multi_update(thd,tables, + res= mysql_multi_update(thd, all_tables, &select_lex->item_list, &lex->value_list, select_lex->where, @@ -2679,26 +2721,29 @@ unsent_create_error: case SQLCOM_REPLACE: case SQLCOM_INSERT: { - my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); - if ((res= insert_precheck(thd, tables, update))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); + if ((res= insert_precheck(thd, all_tables, update))) break; - res = mysql_insert(thd,tables,lex->field_list,lex->many_values, - select_lex->item_list, lex->value_list, - (update ? DUP_UPDATE : lex->duplicates)); + res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, + select_lex->item_list, lex->value_list, + (update ? DUP_UPDATE : lex->duplicates)); if (thd->net.report_error) res= -1; + if (first_table->view && !first_table->contain_auto_increment) + thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it break; } case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { - TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first; - if ((res= insert_select_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= insert_select_precheck(thd, all_tables))) break; /* Fix lock for first table */ - if (tables->lock_type == TL_WRITE_DELAYED) - tables->lock_type= TL_WRITE; + if (first_table->lock_type == TL_WRITE_DELAYED) + first_table->lock_type= TL_WRITE; /* Don't unlock tables until command is written to binary log */ select_lex->options|= SELECT_NO_UNLOCK; @@ -2706,37 +2751,45 @@ unsent_create_error: select_result *result; unit->set_limit(select_lex, select_lex); - if (find_real_table_in_list(tables->next, tables->db, tables->real_name)) + // is table which we are changing used somewhere in other parts of query + if (find_real_table_in_list(all_tables->next_global, + first_table->db, first_table->real_name)) { /* Using same table for INSERT and SELECT */ select_lex->options |= OPTION_BUFFER_RESULT; } - - if (!(res=open_and_lock_tables(thd, tables))) + if (!(res= open_and_lock_tables(thd, all_tables))) { - if ((result=new select_insert(tables->table,&lex->field_list, - lex->duplicates))) + 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_local_table->next; + lex->select_lex.table_list.first= (byte*) first_table->next_local; /* insert/replace from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT */ 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_local_table; + lex->select_lex.table_list.first= (byte*) first_table; lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; if (thd->net.report_error) res= -1; } else res= -1; + + if (first_table->view && !first_table->contain_auto_increment) + thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it + break; } case SQLCOM_TRUNCATE: - if (check_one_table_access(thd, DELETE_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, DELETE_ACL, all_tables)) goto error; /* Don't allow this within a transaction because we want to use @@ -2747,13 +2800,15 @@ unsent_create_error: send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); goto error; } - res=mysql_truncate(thd,tables); + + res= mysql_truncate(thd, first_table); break; case SQLCOM_DELETE: { - if ((res= delete_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= delete_precheck(thd, all_tables))) break; - res = mysql_delete(thd,tables, select_lex->where, + res = mysql_delete(thd, all_tables, select_lex->where, &select_lex->order_list, select_lex->select_limit, select_lex->options); if (thd->net.report_error) @@ -2762,13 +2817,13 @@ unsent_create_error: } case SQLCOM_DELETE_MULTI: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *target_tbl; uint table_count; multi_delete *result; - if ((res= multi_delete_precheck(thd, tables, &table_count))) + if ((res= multi_delete_precheck(thd, all_tables, &table_count))) break; /* condition will be TRUE on SP re-excuting */ @@ -2781,33 +2836,11 @@ unsent_create_error: } thd->proc_info="init"; - if ((res=open_and_lock_tables(thd,tables))) + if ((res= open_and_lock_tables(thd, all_tables))) + break; + + if ((res= mysql_multi_delete_prepare(thd))) break; - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next) - { - target_tbl->table= target_tbl->table_list->table; - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlaying SELECTs of it - */ - for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit(); - un; - un= un->next_unit()) - { - if (un->first_select()->linkage != DERIVED_TABLE_TYPE && - un->check_updateable(target_tbl->table_list->db, - target_tbl->table_list->real_name)) - { - my_error(ER_UPDATE_TABLE_USED, MYF(0), - target_tbl->table_list->real_name); - res= -1; - break; - } - } - } if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, table_count))) @@ -2833,9 +2866,10 @@ unsent_create_error: } case SQLCOM_DROP_TABLE: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd,DROP_ACL,tables,0)) + if (check_table_access(thd, DROP_ACL, all_tables, 0)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) { @@ -2856,16 +2890,18 @@ unsent_create_error: if (thd->slave_thread) lex->drop_if_exists= 1; } - res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary); + res= mysql_rm_table(thd, first_table, lex->drop_if_exists, + lex->drop_temporary); } break; case SQLCOM_DROP_INDEX: - if (check_one_table_access(thd, INDEX_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) res= -1; else - res = mysql_drop_index(thd, tables, &lex->alter_info); + res = mysql_drop_index(thd, first_table, &lex->alter_info); break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -2964,40 +3000,42 @@ unsent_create_error: res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS)); break; case SQLCOM_SHOW_FIELDS: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ goto error; #else { - char *db=tables->db; + char *db= first_table->db; remove_escape(db); // Fix escaped '_' - remove_escape(tables->real_name); + remove_escape(first_table->real_name); if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, - &tables->grant.privilege, 0, 0)) + &first_table->grant.privilege, 0, 0)) goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0)) + if (grant_option && check_grant(thd, SELECT_ACL, first_table, 2, UINT_MAX, 0)) goto error; - res= mysqld_show_fields(thd,tables, + res= mysqld_show_fields(thd, first_table, (lex->wild ? lex->wild->ptr() : NullS), lex->verbose); break; } #endif case SQLCOM_SHOW_KEYS: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ goto error; #else { - char *db=tables->db; + char *db= first_table->db; remove_escape(db); // Fix escaped '_' - remove_escape(tables->real_name); + remove_escape(first_table->real_name); if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, - &tables->grant.privilege, 0, 0)) + &first_table->grant.privilege, 0, 0)) goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0)) + if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0)) goto error; - res= mysqld_show_keys(thd,tables); + res= mysqld_show_keys(thd, first_table); break; } #endif @@ -3007,12 +3045,13 @@ unsent_create_error: case SQLCOM_LOAD: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); uint privilege= (lex->duplicates == DUP_REPLACE ? INSERT_ACL | DELETE_ACL : INSERT_ACL); if (!lex->local_file) { - if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0)) + if (check_access(thd, privilege | FILE_ACL, first_table->db, 0, 0, 0)) goto error; } else @@ -3023,19 +3062,20 @@ unsent_create_error: send_error(thd,ER_NOT_ALLOWED_COMMAND); goto error; } - if (check_one_table_access(thd, privilege, tables)) + if (check_one_table_access(thd, privilege, all_tables)) goto error; } - res=mysql_load(thd, lex->exchange, tables, lex->field_list, - lex->duplicates, (bool) lex->local_file, lex->lock_option); + res= mysql_load(thd, lex->exchange, first_table, lex->field_list, + lex->duplicates, (bool) lex->local_file, lex->lock_option); break; } case SQLCOM_SET_OPTION: { List<set_var_base> *lex_var_list= &lex->var_list; - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || - (res= open_and_lock_tables(thd,tables)))) + if (all_tables && + ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) || + (res= open_and_lock_tables(thd, all_tables)))) break; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) { @@ -3071,17 +3111,20 @@ purposes internal to the MySQL server", MYF(0)); break; case SQLCOM_LOCK_TABLES: unlock_locked_tables(thd); - if (check_db_used(thd,tables) || end_active_trans(thd)) + if (check_db_used(thd, all_tables) || end_active_trans(thd)) goto error; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0)) + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; - if (!(res= open_and_lock_tables(thd, tables))) + + + + if (!(res= open_and_lock_tables(thd, all_tables))) { #ifdef HAVE_QUERY_CACHE if (thd->variables.query_cache_wlock_invalidate) - query_cache.invalidate_locked_for_write(tables); + query_cache.invalidate_locked_for_write(first_table); #endif /*HAVE_QUERY_CACHE*/ thd->locked_tables=thd->lock; thd->lock=0; @@ -3259,9 +3302,10 @@ purposes internal to the MySQL server", MYF(0)); case SQLCOM_GRANT: { if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, - tables && tables->db ? tables->db : select_lex->db, - tables ? &tables->grant.privilege : 0, - tables ? 0 : 1,0)) + ((first_table && first_table->db) ? + first_table->db : select_lex->db), + first_table ? &first_table->grant.privilege : 0, + first_table ? 0 : 1, 0)) goto error; /* @@ -3300,15 +3344,15 @@ purposes internal to the MySQL server", MYF(0)); user->host.str); } } - if (tables) + if (first_table) { if (grant_option && check_grant(thd, (lex->grant | lex->grant_tot_col | GRANT_ACL), - tables, 0, UINT_MAX, 0)) + all_tables, 0, UINT_MAX, 0)) goto error; - if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, - lex->grant, + if (!(res = mysql_table_grant(thd, all_tables, lex->users_list, + lex->columns, lex->grant, lex->sql_command == SQLCOM_REVOKE)) && mysql_bin_log.is_open()) { @@ -3355,14 +3399,14 @@ purposes internal to the MySQL server", MYF(0)); lex->no_write_to_binlog= 1; case SQLCOM_FLUSH: { - if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables)) + if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables)) goto error; /* reload_acl_and_cache() will tell us if we are allowed to write to the binlog or not. */ bool write_to_binlog; - if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog)) + if (reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog)) send_error(thd, 0); else { @@ -3397,27 +3441,30 @@ purposes internal to the MySQL server", MYF(0)); break; #endif case SQLCOM_HA_OPEN: - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL, all_tables, 0)) goto error; - res = mysql_ha_open(thd, tables); + res= mysql_ha_open(thd, first_table); break; case SQLCOM_HA_CLOSE: - if (check_db_used(thd,tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables)) goto error; - res = mysql_ha_close(thd, tables); + res= mysql_ha_close(thd, first_table); break; case SQLCOM_HA_READ: + DBUG_ASSERT(first_table == all_tables && first_table != 0); /* There is no need to check for table permissions here, because if a user has no permissions to read a table, he won't be able to open it (with SQLCOM_HA_OPEN) in the first place. */ - if (check_db_used(thd,tables)) + if (check_db_used(thd, all_tables)) goto error; - res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir, - lex->insert_list, lex->ha_rkey_mode, select_lex->where, - select_lex->select_limit, select_lex->offset_limit); + res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->backup_dir, + lex->insert_list, lex->ha_rkey_mode, select_lex->where, + select_lex->select_limit, select_lex->offset_limit); break; case SQLCOM_BEGIN: @@ -3573,12 +3620,12 @@ purposes internal to the MySQL server", MYF(0)); LINT_INIT(smrx); /* In case the arguments are subselects... */ - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables, 0)) || - (res= open_and_lock_tables(thd, tables)))) + if (all_tables && + ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) || + (res= open_and_lock_tables(thd, all_tables)))) { break; } - fix_tables_pointers(lex->all_selects_list); #ifndef EMBEDDED_LIBRARY /* @@ -3764,6 +3811,23 @@ purposes internal to the MySQL server", MYF(0)); lex->wild->ptr() : NullS)); break; } + case SQLCOM_CREATE_VIEW: + { + res= mysql_create_view(thd, thd->lex->create_view_mode); + break; + } + case SQLCOM_DROP_VIEW: + { + if (check_table_access(thd, DROP_ACL, all_tables, 0)) + goto error; + if (end_active_trans(thd)) + { + res= -1; + break; + } + res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); + break; + } default: /* Impossible */ send_ok(thd); break; @@ -3836,27 +3900,28 @@ error: check_one_table_access() thd Thread handler privilege requested privelage - tables table list of command + all_tables global table list of query RETURN 0 - OK 1 - access denied, error is sent to client */ -int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables) +int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { - if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) + if (check_access(thd, privilege, all_tables->db, + &all_tables->grant.privilege, 0, 0)) return 1; /* Show only 1 table for check_grant */ - if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0)) + if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0)) return 1; /* Check rights on tables of subselect (if exists) */ TABLE_LIST *subselects_tables; - if ((subselects_tables= tables->next)) + if ((subselects_tables= all_tables->next_global)) { - if ((check_table_access(thd, SELECT_ACL, subselects_tables,0))) + if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0))) return 1; } return 0; @@ -4008,7 +4073,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, uint found=0; ulong found_access=0; TABLE_LIST *org_tables=tables; - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_global) { if (tables->derived || (tables->table && (int)tables->table->tmp_table)) continue; @@ -4046,7 +4111,7 @@ bool check_merge_table_access(THD *thd, char *db, { /* Check that all tables use the current database */ TABLE_LIST *tmp; - for (tmp=table_list; tmp ; tmp=tmp->next) + for (tmp= table_list; tmp; tmp= tmp->next_local) { if (!tmp->db || !tmp->db[0]) tmp->db=db; @@ -4060,7 +4125,7 @@ bool check_merge_table_access(THD *thd, char *db, static bool check_db_used(THD *thd,TABLE_LIST *tables) { - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_global) { if (!tables->db) { @@ -4154,6 +4219,7 @@ mysql_init_query(THD *thd, bool lexonly) lex->select_lex.init_query(); lex->value_list.empty(); lex->param_list.empty(); + lex->view_list.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to=0; lex->unit.prev= lex->unit.link_prev= 0; @@ -4167,10 +4233,16 @@ mysql_init_query(THD *thd, bool lexonly) lex->select_lex.init_order(); lex->select_lex.group_list.empty(); lex->describe= 0; - lex->derived_tables= FALSE; + lex->derived_tables= 0; + lex->view_prepare_mode= FALSE; lex->lock_option= TL_READ; lex->found_colon= 0; lex->safe_to_cache_query= 1; + lex->query_tables= 0; + lex->query_tables_last= &lex->query_tables; + lex->variables_used= 0; + lex->select_lex.parent_lex= lex; + lex->empty_field_list_on_rset= 0; if (! lexonly) { thd->select_number= lex->select_lex.select_number= 1; @@ -4215,6 +4287,7 @@ mysql_new_select(LEX *lex, bool move_down) select_lex->select_number= ++lex->thd->select_number; select_lex->init_query(); select_lex->init_select(); + select_lex->parent_lex= lex; if (move_down) { /* first select_lex of subselect or derived table */ @@ -4233,6 +4306,11 @@ mysql_new_select(LEX *lex, bool move_down) } else { + if (lex->current_select->order_list.first && !lex->current_select->braces) + { + net_printf(lex->thd, ER_WRONG_USAGE, "UNION", "ORDER BY"); + return 1; + } select_lex->include_neighbour(lex->current_select); SELECT_LEX_UNIT *unit= select_lex->master_unit(); SELECT_LEX *fake= unit->fake_select_lex; @@ -4299,6 +4377,8 @@ void mysql_init_multi_delete(LEX *lex) lex->select_lex.select_limit= lex->unit.select_limit_cnt= HA_POS_ERROR; lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list); + lex->query_tables= 0; + lex->query_tables_last= &lex->query_tables; } @@ -4931,6 +5011,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; + ptr->select_lex= thd->lex->current_select; ptr->cacheable_table= 1; if (use_index_arg) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg, @@ -4944,7 +5025,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ; tables ; - tables=tables->next) + tables=tables->next_local) { if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && !strcmp(ptr->db, tables->db)) @@ -4954,7 +5035,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } } - table_list.link_in_list((byte*) ptr, (byte**) &ptr->next); + table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); + LEX *lex= thd->lex; + *(ptr->prev_global= lex->query_tables_last)= ptr; + lex->query_tables_last= &ptr->next_global; DBUG_RETURN(ptr); } @@ -5210,9 +5294,9 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type, for_update)); - for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ; - tables ; - tables=tables->next) + for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first; + tables; + tables= tables->next_local) { tables->lock_type= lock_type; tables->updating= for_update; @@ -5643,7 +5727,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) SYNOPSIS multi_update_precheck() thd Thread handler - tables Global table list + tables Global/local table list (have to be the same) RETURN VALUE 0 OK @@ -5653,12 +5737,11 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) int multi_update_precheck(THD *thd, TABLE_LIST *tables) { - DBUG_ENTER("multi_update_precheck"); const char *msg= 0; TABLE_LIST *table; LEX *lex= thd->lex; SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first; + DBUG_ENTER("multi_update_precheck"); if (select_lex->item_list.elements != lex->value_list.elements) { @@ -5669,7 +5752,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) Ensure that we have UPDATE or SELECT privilege for each table The exact privilege is checked in mysql_multi_update() */ - for (table= update_list; table; table= table->next) + for (table= tables; table; table= table->next_local) { if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege, 0, 1) || @@ -5679,31 +5762,16 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) DBUG_RETURN(1); - /* - We assign following flag only to copy of table, because it will - be checked only if query contains subqueries i.e. only if copy exists - */ - if (table->table_list) - table->table_list->table_in_update_from_clause= 1; + table->table_in_first_from_clause= 1; } /* Is there tables of subqueries? */ if (&lex->select_lex != lex->all_selects_list) { - for (table= tables; table; table= table->next) + for (table= tables; table; table= table->next_global) { - if (table->table_in_update_from_clause) - { - /* - If we check table by local TABLE_LIST copy then we should copy - grants to global table list, because it will be used for table - opening. - */ - if (table->table_list) - table->grant= table->table_list->grant; - } - else + if (!table->table_in_first_from_clause) { if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0) || @@ -5732,7 +5800,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) SYNOPSIS multi_delete_precheck() thd Thread handler - tables Global table list + tables Global/local table list table_count Pointer to table counter RETURN VALUE @@ -5742,12 +5810,11 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) */ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) { - DBUG_ENTER("multi_delete_precheck"); SELECT_LEX *select_lex= &thd->lex->select_lex; TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first; TABLE_LIST *target_tbl; + DBUG_ENTER("multi_delete_precheck"); *table_count= 0; @@ -5762,12 +5829,12 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0)); DBUG_RETURN(-1); } - for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next) + for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local) { (*table_count)++; /* All tables in aux_tables must be found in FROM PART */ TABLE_LIST *walk; - for (walk= delete_tables; walk; walk= walk->next) + for (walk= tables; walk; walk= walk->next_local) { if (!my_strcasecmp(table_alias_charset, target_tbl->alias, walk->alias) && @@ -5780,14 +5847,8 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) "MULTI DELETE"); DBUG_RETURN(-1); } - if (walk->derived) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name, - "DELETE"); - DBUG_RETURN(-1); - } walk->lock_type= target_tbl->lock_type; - target_tbl->table_list= walk; // Remember corresponding table + target_tbl->correspondent_table= walk; // Remember corresponding table } DBUG_RETURN(0); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 23c5a302190..5214ae95b86 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -863,7 +863,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, SYNOPSIS mysql_test_insert() stmt prepared statemen handler - tables list of tables queries + tables global/local table list RETURN VALUE 0 ok @@ -883,8 +883,6 @@ static int mysql_test_insert(Prepared_statement *stmt, List_iterator_fast<List_item> its(values_list); List_item *values; int res= -1; - TABLE_LIST *insert_table_list= - (TABLE_LIST*) lex->select_lex.table_list.first; my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); DBUG_ENTER("mysql_test_insert"); @@ -907,9 +905,9 @@ static int mysql_test_insert(Prepared_statement *stmt, uint value_count; ulong counter= 0; - if ((res= mysql_prepare_insert(thd, table_list, insert_table_list, - table_list->table, fields, values, - update_fields, update_values, duplic))) + if ((res= mysql_prepare_insert(thd, table_list, table_list->table, + fields, values, update_fields, + update_values, duplic))) goto error; value_count= values->elements; @@ -925,7 +923,7 @@ static int mysql_test_insert(Prepared_statement *stmt, MYF(0), counter); goto error; } - if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0)) + if (setup_fields(thd, 0, table_list, *values, 0, 0, 0)) goto error; } } @@ -972,16 +970,14 @@ static int mysql_test_update(Prepared_statement *stmt, res= -1; else { - TABLE_LIST *update_table_list= (TABLE_LIST *)select->table_list.first; if (!(res= mysql_prepare_update(thd, table_list, - update_table_list, &select->where, select->order_list.elements, (ORDER *) select->order_list.first))) { - if (setup_fields(thd, 0, update_table_list, + if (setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0) || - setup_fields(thd, 0, update_table_list, + setup_fields(thd, 0, table_list, stmt->lex->value_list, 0, 0, 0)) res= -1; } @@ -1219,8 +1215,9 @@ error: SYNOPSIS select_like_statement_test() - stmt - prepared table handler - tables - global list of tables + stmt - prepared table handler + tables - global list of tables + specific_prepare - function of command specific prepare RETURN VALUE 0 success @@ -1228,7 +1225,8 @@ error: -1 error, not sent to client */ static int select_like_statement_test(Prepared_statement *stmt, - TABLE_LIST *tables) + TABLE_LIST *tables, + int (*specific_prepare)(THD *thd)) { DBUG_ENTER("select_like_statement_test"); THD *thd= stmt->thd; @@ -1242,6 +1240,9 @@ static int select_like_statement_test(Prepared_statement *stmt, if (tables && (res= open_and_lock_tables(thd, tables))) goto end; + if (specific_prepare && (res= (*specific_prepare)(thd))) + goto end; + thd->used_tables= 0; // Updated by setup_fields // JOIN::prepare calls @@ -1269,31 +1270,28 @@ end: 1 error, sent to client -1 error, not sent to client */ -static int mysql_test_create_table(Prepared_statement *stmt, - TABLE_LIST *tables) +static int mysql_test_create_table(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_create_table"); THD *thd= stmt->thd; LEX *lex= stmt->lex; SELECT_LEX *select_lex= &lex->select_lex; int res= 0; - /* Skip first table, which is the table we are creating */ - TABLE_LIST *create_table, *create_table_local; - tables= lex->unlink_first_table(tables, &create_table, - &create_table_local); + bool link_to_local; + TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); + TABLE_LIST *tables= lex->query_tables; if (!(res= create_table_precheck(thd, tables, create_table)) && select_lex->item_list.elements) { select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; - res= select_like_statement_test(stmt, tables); + res= select_like_statement_test(stmt, tables, 0); select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE; } /* put tables back for PS rexecuting */ - tables= lex->link_first_table_back(tables, create_table, - create_table_local); + lex->link_first_table_back(create_table, link_to_local); DBUG_RETURN(res); } @@ -1317,7 +1315,7 @@ static int mysql_test_multiupdate(Prepared_statement *stmt, int res; if ((res= multi_update_precheck(stmt->thd, tables))) return res; - return select_like_statement_test(stmt, tables); + return select_like_statement_test(stmt, tables, &mysql_multi_update_prepare); } @@ -1345,7 +1343,7 @@ static int mysql_test_multidelete(Prepared_statement *stmt, uint fake_counter; if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter))) return res; - return select_like_statement_test(stmt, tables); + return select_like_statement_test(stmt, tables, &mysql_multi_delete_prepare); } @@ -1371,14 +1369,15 @@ static int mysql_test_insert_select(Prepared_statement *stmt, return res; TABLE_LIST *first_local_table= (TABLE_LIST *)lex->select_lex.table_list.first; + DBUG_ASSERT(first_local_table != 0); /* Skip first table, which is the table we are inserting in */ - lex->select_lex.table_list.first= (byte*) first_local_table->next; + lex->select_lex.table_list.first= (byte*) first_local_table->next_local; /* insert/replace from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT */ lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; - res= select_like_statement_test(stmt, tables); + res= select_like_statement_test(stmt, tables, &mysql_insert_select_prepare); /* revert changes*/ lex->select_lex.table_list.first= (byte*) first_local_table; lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; @@ -1400,7 +1399,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) THD *thd= stmt->thd; LEX *lex= stmt->lex; SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first; + TABLE_LIST *tables; enum enum_sql_command sql_command= lex->sql_command; int res= 0; DBUG_ENTER("send_prepare_results"); @@ -1408,11 +1407,9 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) DBUG_PRINT("enter",("command: %d, param_count: %ld", sql_command, stmt->param_count)); - if (select_lex != lex->all_selects_list && - lex->unit.create_total_list(thd, lex, &tables)) - DBUG_RETURN(1); + lex->first_lists_tables_same(); + tables= lex->query_tables; - switch (sql_command) { case SQLCOM_REPLACE: case SQLCOM_INSERT: @@ -1438,7 +1435,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) DBUG_RETURN(0); case SQLCOM_CREATE_TABLE: - res= mysql_test_create_table(stmt, tables); + res= mysql_test_create_table(stmt); break; case SQLCOM_DO: @@ -1666,6 +1663,11 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) { SELECT_LEX *sl= lex->all_selects_list; + if (lex->empty_field_list_on_rset) + { + lex->field_list.empty(); + lex->empty_field_list_on_rset= 0; + } for (; sl; sl= sl->next_select_in_list()) { if (!sl->first_execution) @@ -1687,22 +1689,10 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) for (order= (ORDER *)sl->order_list.first; order; order= order->next) order->item= &order->item_ptr; } - - /* - TODO: When the new table structure is ready, then have a status bit - to indicate the table is altered, and re-do the setup_* - and open the tables back. - */ - for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first; - tables; - tables= tables->next) { - /* - Reset old pointers to TABLEs: they are not valid since the tables - were closed in the end of previous prepare or execute call. - */ - tables->table= 0; - tables->table_list= 0; + TABLE_LIST *tables= (TABLE_LIST *)sl->table_list.first; + if (tables) + tables->setup_is_done= 0; } { SELECT_LEX_UNIT *unit= sl->master_unit(); @@ -1712,6 +1702,22 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) unit->reinit_exec_mechanism(); } } + + /* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* + and open the tables back. + */ + for (TABLE_LIST *tables= lex->query_tables; + tables; + tables= tables->next_global) + { + /* + Reset old pointers to TABLEs: they are not valid since the tables + were closed in the end of previous prepare or execute call. + */ + tables->table= 0; + } lex->current_select= &lex->select_lex; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index a1676aed78d..97c4e39874c 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -64,10 +64,10 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) table_list= reverse_table_list(table_list); /* Find the last renamed table */ - for (table=table_list ; - table->next != ren_table ; - table=table->next->next) ; - table=table->next->next; // Skip error table + for (table= table_list; + table->next_local != ren_table ; + table= table->next_local->next_local) ; + table= table->next_local->next_local; // Skip error table /* Revert to old names */ rename_tables(thd, table, 1); @@ -89,7 +89,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) send_ok(thd); } - unlock_table_names(thd,table_list); + unlock_table_names(thd, table_list); err: pthread_mutex_unlock(&LOCK_open); @@ -114,8 +114,8 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list) while (table_list) { - TABLE_LIST *next= table_list->next; - table_list->next= prev; + TABLE_LIST *next= table_list->next_local; + table_list->next_local= prev; prev= table_list; table_list= next; } @@ -134,13 +134,13 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) TABLE_LIST *ren_table,*new_table; DBUG_ENTER("rename_tables"); - for (ren_table=table_list ; ren_table ; ren_table=new_table->next) + for (ren_table= table_list; ren_table; ren_table= new_table->next_local) { db_type table_type; char name[FN_REFLEN]; const char *new_alias, *old_alias; - new_table=ren_table->next; + new_table= ren_table->next_local; if (lower_case_table_names == 2) { old_alias= ren_table->alias; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a9ddfb0860b..db5c933545b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -225,7 +225,7 @@ int handle_select(THD *thd, LEX *lex, select_result *result) thd->net.report_error)); if (thd->net.report_error) res= 1; - if (res) + if (res > 0) { if (result) { @@ -236,45 +236,20 @@ int handle_select(THD *thd, LEX *lex, select_result *result) send_error(thd, 0, NullS); res= 1; // Error sent to client } + if (res < 0) + { + if (result) + { + result->abort(); + } + res= 1; + } if (result != lex->result) delete result; DBUG_RETURN(res); } -void relink_tables(SELECT_LEX *select_lex) -{ - for (TABLE_LIST *cursor= (TABLE_LIST *) select_lex->table_list.first; - cursor; - cursor=cursor->next) - if (cursor->table_list) - cursor->table= cursor->table_list->table; -} - - -void fix_tables_pointers(SELECT_LEX *select_lex) -{ - if (select_lex->next_select_in_list()) - { - /* Fix tables 'to-be-unioned-from' list to point at opened tables */ - for (SELECT_LEX *sl= select_lex; - sl; - sl= sl->next_select_in_list()) - relink_tables(sl); - } -} - -void fix_tables_pointers(SELECT_LEX_UNIT *unit) -{ - for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) - { - relink_tables(sl); - for (SELECT_LEX_UNIT *un= sl->first_inner_unit(); un; un= un->next_unit()) - fix_tables_pointers(un); - } -} - - /* Function to setup clauses without sum functions */ @@ -339,7 +314,7 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check that all tables, fields, conds and order are ok */ - if (setup_tables(tables_list) || + if (setup_tables(thd, tables_list, &conds) || setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1, @@ -366,20 +341,19 @@ JOIN::prepare(Item ***rref_pointer_array, having->split_sum_func(ref_pointer_array, all_fields); } - // Is it subselect + if (!thd->lex->view_prepare_mode) { Item_subselect *subselect; + /* Is it subselect? */ if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; - if ((res= subselect->select_transformer(this)) != + if ((res= ((!thd->lex->view_prepare_mode) ? + subselect->select_transformer(this) : + subselect->no_select_transform())) != Item_subselect::RES_OK) { - 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((res == Item_subselect::RES_ERROR)); } } @@ -415,7 +389,7 @@ JOIN::prepare(Item ***rref_pointer_array, } } TABLE_LIST *table_ptr; - for (table_ptr= tables_list ; table_ptr ; table_ptr= table_ptr->next) + for (table_ptr= tables_list; table_ptr; table_ptr= table_ptr->next_local) tables++; } { @@ -484,11 +458,7 @@ JOIN::prepare(Item ***rref_pointer_array, if (alloc_func_list()) goto err; - 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); // All OK err: @@ -1049,10 +1019,15 @@ JOIN::reinit() DBUG_ENTER("JOIN::reinit"); /* TODO move to unit reinit */ unit->set_limit(select_lex, select_lex); - - if (setup_tables(tables_list)) - DBUG_RETURN(1); - + + /* conds should not be used here, it is added just for safety */ + if (tables_list) + { + tables_list->setup_is_done= 0; + if (setup_tables(thd, tables_list, &conds)) + DBUG_RETURN(1); + } + /* Reset of sum functions */ first_record= 0; @@ -1747,7 +1722,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, found_const_table_map= all_table_map=0; const_count=0; - for (s=stat,i=0 ; tables ; s++,tables=tables->next,i++) + for (s= stat, i= 0; + tables; + s++, tables= tables->next_local, i++) { table_map dep_tables; TABLE_LIST *embedding= tables->embedding; @@ -5329,7 +5306,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (send_row) { - for (TABLE_LIST *table=tables; table ; table=table->next) + for (TABLE_LIST *table= tables; table; table= table->next_local) mark_as_null_row(table->table); // All fields are NULL if (having && having->val_int() == 0) send_row=0; @@ -10019,7 +9996,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT))) DBUG_RETURN(0); - for (; !(map & tables->table->map) ; tables=tables->next) ; + for (; !(map & tables->table->map); tables= tables->next_local); if (map != tables->table->map) DBUG_RETURN(0); // More than one table DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr)); @@ -11271,6 +11248,17 @@ void st_table_list::print(THD *thd, String *str) print_join(thd, str, &nested_join->join_list); str->append(')'); } + else if (view_name.str) + { + str->append(view_db.str, view_db.length); + str->append('.'); + str->append(view_name.str, view_name.length); + if (my_strcasecmp(table_alias_charset, view_name.str, alias)) + { + str->append(' '); + str->append(alias); + } + } else if (derived) { str->append('('); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 62bee1209e1..00d083dc150 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -39,6 +39,8 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), static int store_create_info(THD *thd, TABLE *table, String *packet); +static int +view_store_create_info(THD *thd, TABLE_LIST *table, String *packet); /* @@ -141,6 +143,9 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) List<char> files; char *file_name; Protocol *protocol= thd->protocol; + uint len; + bool show_type = !test(thd->variables.sql_mode & + (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323)); DBUG_ENTER("mysqld_show_tables"); field->name=(char*) thd->alloc(20+(uint) strlen(db)+ @@ -149,9 +154,12 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) if (wild && wild[0]) strxmov(end," (",wild,")",NullS); field->max_length=NAME_LEN; - (void) sprintf(path,"%s/%s",mysql_data_home,db); - (void) unpack_dirname(path,path); + (void) my_snprintf(path, FN_LEN, "%s/%s", mysql_data_home, db); + end= path + (len= unpack_dirname(path,path)); + len= FN_LEN - len; field_list.push_back(field); + if (show_type) + field_list.push_back(new Item_empty_string("Type", 5)); if (protocol->send_fields(&field_list,1)) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) @@ -161,6 +169,22 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) { protocol->prepare_for_resend(); protocol->store(file_name, system_charset_info); + if (show_type) + { + my_snprintf(end, len, "/%s%s", file_name, reg_ext); + switch (mysql_frm_type(path)) + { + case FRMTYPE_ERROR: + protocol->store("error", system_charset_info); + break; + case FRMTYPE_TABLE: + protocol->store("table", system_charset_info); + break; + case FRMTYPE_VIEW: + protocol->store("view", system_charset_info); + break; + } + } if (protocol->write()) DBUG_RETURN(-1); } @@ -516,9 +540,10 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) protocol->store(file_name, system_charset_info); table_list.db=(char*) db; table_list.real_name= table_list.alias= file_name; + table_list.select_lex= &thd->lex->select_lex; if (lower_case_table_names) my_casedn_str(files_charset_info, file_name); - if (!(table = open_ltable(thd, &table_list, TL_READ))) + if (open_and_lock_tables(thd, &table_list)) { for (uint i=2 ; i < field_list.elements ; i++) protocol->store_null(); @@ -526,10 +551,16 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) protocol->store(thd->net.last_error, system_charset_info); thd->clear_error(); } + else if (table_list.view) + { + for (uint i= 2; i < field_list.elements; i++) + protocol->store_null(); + protocol->store("view", system_charset_info); + } else { const char *str; - handler *file=table->file; + handler *file= (table= table_list.table)->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); protocol->store(file->table_type(), system_charset_info); protocol->store((ulonglong) table->frm_version); @@ -630,8 +661,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) if (comment != table->comment) my_free(comment,MYF(0)); } - close_thread_tables(thd,0); } + close_thread_tables(thd, 0); if (protocol->write()) DBUG_RETURN(-1); } @@ -657,11 +688,13 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); - if (!(table = open_ltable(thd, table_list, TL_UNLOCK))) + table_list->lock_type= TL_UNLOCK; + if (open_and_lock_tables(thd, table_list)) { send_error(thd); DBUG_RETURN(1); } + table= table_list->table; file=table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -755,7 +788,10 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, /* Add grant options & comments */ end=tmp; #ifndef NO_EMBEDDED_ACCESS_CHECKS - col_access= get_column_grant(thd,table_list,field) & COL_ACLS; + col_access= get_column_grant(thd, &table_list->grant, + table_list->db, + table_list->real_name, + field->field_name) & COL_ACLS; for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) { if (col_access & 1) @@ -793,14 +829,24 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); - /* Only one table for now */ - if (!(table = open_ltable(thd, table_list, TL_UNLOCK))) + /* Only one table for now, but VIEW can involve several tables */ + if (open_and_lock_tables(thd, table_list)) { send_error(thd); DBUG_RETURN(1); } + /* TODO: add environment variables show when it become possible */ + if (thd->lex->only_view && !table_list->view) + { + my_error(ER_WRONG_OBJECT, MYF(0), table_list->db, + table_list->real_name, "VIEW"); + DBUG_RETURN(-1); + } + table= table_list->table; - if (store_create_info(thd, table, &buffer)) + if ((table_list->view ? + view_store_create_info(thd, table_list, &buffer) : + store_create_info(thd, table, &buffer))) DBUG_RETURN(-1); List<Item> field_list; @@ -812,11 +858,21 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (protocol->send_fields(&field_list, 1)) DBUG_RETURN(1); protocol->prepare_for_resend(); - protocol->store(table->table_name, system_charset_info); buffer.length(0); - if (store_create_info(thd, table, &buffer)) - DBUG_RETURN(-1); + if (table_list->view) + { + protocol->store(table_list->view_name.str, system_charset_info); + if (view_store_create_info(thd, table_list, &buffer)) + DBUG_RETURN(-1); + } + else + { + protocol->store(table->table_name, system_charset_info); + if (store_create_info(thd, table, &buffer)) + DBUG_RETURN(-1); + } protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); + if (protocol->write()) DBUG_RETURN(1); send_eof(thd); @@ -1483,6 +1539,35 @@ store_create_info(THD *thd, TABLE *table, String *packet) } +static int +view_store_create_info(THD *thd, TABLE_LIST *table, String *buff) +{ + my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | + MODE_ORACLE | + MODE_MSSQL | + MODE_DB2 | + MODE_MAXDB | + MODE_ANSI)) != 0; + buff->append("CREATE ", 7); + if(!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE || + table->algorithm == VIEW_ALGORITHM_TMEPTABLE)) + { + buff->append("ALGORITHM=", 10); + if (table->algorithm == VIEW_ALGORITHM_TMEPTABLE) + buff->append("TMPTABLE ", 9); + else + buff->append("MERGE ", 6); + } + buff->append("VIEW ", 5); + buff->append(table->view_db.str, table->view_db.length); + buff->append('.'); + buff->append(table->view_name.str, table->view_name.length); + buff->append(" AS ", 4); + buff->append(table->query.str, table->query.length); + return 0; +} + + /**************************************************************************** Return info about all processes returns for each thread: thread id, user, host, db, command, info diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 21d41b84e4b..ea57536d7c1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -89,7 +89,7 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, } } - error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary, 0); + error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); err: pthread_mutex_unlock(&LOCK_open); @@ -134,8 +134,8 @@ int mysql_rm_table_part2_with_lock(THD *thd, thd->mysys_var->current_cond= &COND_refresh; VOID(pthread_mutex_lock(&LOCK_open)); - error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary, - dont_log_query); + error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1, + dont_log_query); pthread_mutex_unlock(&LOCK_open); @@ -157,6 +157,7 @@ int mysql_rm_table_part2_with_lock(THD *thd, if_exists If set, don't give an error if table doesn't exists. In this case we give an warning of level 'NOTE' drop_temporary Only drop temporary tables + drop_view Allow to delete VIEW .frm dont_log_query Don't log the query TODO: @@ -176,7 +177,8 @@ int mysql_rm_table_part2_with_lock(THD *thd, */ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool dont_log_query) + bool drop_temporary, bool drop_view, + bool dont_log_query) { TABLE_LIST *table; char path[FN_REFLEN], *alias; @@ -188,7 +190,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (lock_table_names(thd, tables)) DBUG_RETURN(1); - for (table=tables ; table ; table=table->next) + for (table= tables; table; table= table->next_local) { char *db=table->db; mysql_ha_close(thd, table, /*dont_send_ok*/ 1, /*dont_lock*/ 1); @@ -216,7 +218,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS); (void) unpack_filename(path,path); } - if (drop_temporary || access(path,F_OK)) + if (drop_temporary || access(path,F_OK) || + (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE)) { if (if_exists) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -1333,7 +1336,7 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) ****************************************************************************/ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - const char *db, const char *name, + TABLE_LIST *create_table, List<create_field> *extra_fields, List<Key> *keys, List<Item> *items, @@ -1373,21 +1376,24 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } /* create and lock table */ /* QQ: This should be done atomic ! */ - if (mysql_create_table(thd,db,name,create_info,*extra_fields, - *keys,0,1,select_field_count)) // no logging + if (mysql_create_table(thd, create_table->db, create_table->real_name, + create_info, *extra_fields, *keys, 0, 1, + select_field_count)) // no logging DBUG_RETURN(0); - if (!(table=open_table(thd,db,name,name,(bool*) 0))) + if (!(table= open_table(thd, create_table, 0, (bool*) 0))) { - quick_rm_table(create_info->db_type,db,table_case_name(create_info,name)); + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->real_name)); DBUG_RETURN(0); } table->reginfo.lock_type=TL_WRITE; - if (!((*lock)=mysql_lock_tables(thd,&table,1))) + if (!((*lock)= mysql_lock_tables(thd, &table,1))) { VOID(pthread_mutex_lock(&LOCK_open)); hash_delete(&open_cache,(byte*) table); VOID(pthread_mutex_unlock(&LOCK_open)); - quick_rm_table(create_info->db_type,db,table_case_name(create_info, name)); + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->real_name)); DBUG_RETURN(0); } table->file->extra(HA_EXTRA_WRITE_CACHE); @@ -1736,7 +1742,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, DBUG_RETURN(-1); mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1); - for (table = tables; table; table = table->next) + for (table= tables; table; table= table->next_local) { char table_name[NAME_LEN*2+2]; char* db = (table->db) ? table->db : thd->db; @@ -1875,8 +1881,9 @@ send_result_message: reopen the table and do ha_innobase::analyze() on it. */ close_thread_tables(thd); - TABLE_LIST *save_next= table->next; - table->next= 0; + TABLE_LIST *save_next_local= table->next_local, + *save_next_global= table->next_global; + table->next_local= table->next_global= 0; result_code= mysql_recreate_table(thd, table, 0); close_thread_tables(thd); if (!result_code) // recreation went ok @@ -1886,7 +1893,8 @@ send_result_message: result_code= 0; // analyze went ok } result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK; - table->next= save_next; + table->next_local= save_next_local; + table->next_global= save_next_global; goto send_result_message; } @@ -2100,9 +2108,9 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, DBUG_RETURN(-1); } + bzero((gptr)&src_tables_list, sizeof(src_tables_list)); src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db; src_tables_list.real_name= table_ident->table.str; - src_tables_list.next= 0; if (lock_and_wait_for_table_name(thd, &src_tables_list)) goto err; @@ -3004,7 +3012,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, DBUG_RETURN(error); if (table->tmp_table) - new_table=open_table(thd,new_db,tmp_name,tmp_name,0); + { + TABLE_LIST tbl; + bzero((void*) &tbl, sizeof(tbl)); + tbl.db= new_db; + tbl.real_name= tbl.alias= tmp_name; + new_table= open_table(thd, &tbl, 0, 0); + } else { char path[FN_REFLEN]; @@ -3438,7 +3452,7 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) if (protocol->send_fields(&field_list, 1)) DBUG_RETURN(-1); - for (table= tables; table; table= table->next) + for (table= tables; table; table= table->next_local) { char table_name[NAME_LEN*2+2]; TABLE *t; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 2b7c0d8f0c0..35f8a390308 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -28,11 +28,11 @@ int mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit) { DBUG_ENTER("mysql_union"); - int res= 0; + int res, res_cln; if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK))) res= unit->exec(); - res|= unit->cleanup(); - DBUG_RETURN(res); + res_cln= unit->cleanup(); + DBUG_RETURN(res?res:res_cln); } @@ -136,7 +136,7 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) fake_select_lex->ftfunc_list= &fake_select_lex->ftfunc_list_alloc; fake_select_lex->table_list.link_in_list((byte *)&result_table_list, (byte **) - &result_table_list.next); + &result_table_list.next_local); return options_tmp; } @@ -522,6 +522,7 @@ int st_select_lex_unit::exec() int st_select_lex_unit::cleanup() { int error= 0; + JOIN *join; DBUG_ENTER("st_select_lex_unit::cleanup"); if (cleaned) @@ -538,9 +539,8 @@ int st_select_lex_unit::cleanup() free_tmp_table(thd, table); table= 0; // Safety } - JOIN *join; - SELECT_LEX *sl= first_select_in_union(); - for (; sl; sl= sl->next_select()) + + for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select()) { if ((join= sl->join)) { @@ -565,6 +565,7 @@ int st_select_lex_unit::cleanup() error|= join->cleanup(); delete join; } + DBUG_RETURN(error); } 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> ¬_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> ¬_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> ¬_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> ¬_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; diff --git a/sql/sql_view.cc b/sql/sql_view.cc new file mode 100644 index 00000000000..7739cdf95bf --- /dev/null +++ b/sql/sql_view.cc @@ -0,0 +1,902 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "mysql_priv.h" +#include "sql_acl.h" +#include "sql_select.h" +#include "parse_file.h" + +static int mysql_register_view(THD *thd, TABLE_LIST *view, + enum_view_create_mode mode); + +const char *sql_updatable_view_key_names[]= { "NO", "YES", "LIMIT1", NullS }; +TYPELIB sql_updatable_view_key_typelib= +{ + array_elements(sql_updatable_view_key_names)-1, "", + sql_updatable_view_key_names +}; + + +/* + Creating/altering VIEW procedure + + SYNOPSIS + mysql_create_view() + thd - thread handler + mode - VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE + + RETURN VALUE + 0 OK + -1 Error + 1 Error and error message given +*/ +int mysql_create_view(THD *thd, + enum_view_create_mode mode) +{ + LEX *lex= thd->lex; + bool link_to_local; + /* first table in list is target VIEW name => cut off it */ + TABLE_LIST *view= lex->unlink_first_table(&link_to_local); + TABLE_LIST *tables= lex->query_tables; + SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX_UNIT *unit= &lex->unit; + int res= 0; + DBUG_ENTER("mysql_create_view"); + + if (lex->derived_tables || lex->proc_list.first || + lex->variables_used || lex->param_list.elements) + { + my_error((lex->derived_tables ? ER_VIEW_SELECT_DERIVED : + (lex->proc_list.first ? ER_VIEW_SELECT_PROCEDURE : + ER_VIEW_SELECT_VARIABLE)), MYF(0)); + res= -1; + goto err; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege, + 0, 0) || + grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) + DBUG_RETURN(1); + for (TABLE_LIST *tbl= tables; tbl; tbl= tbl->next_local) + { + /* + Ensure that we have some privilage on this table, more strict check + will be done on column level after preparation, + + SELECT_ACL will be checked for sure for all fields because it is + listed first (if we have not rights to SELECT from whole table this + right will be written as tbl->grant.want_privilege and will be checked + later (except fields which need any privilege and can be updated). + */ + if ((check_access(thd, SELECT_ACL, tbl->db, + &tbl->grant.privilege, 0, 1) || + grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 1)) && + (check_access(thd, INSERT_ACL, tbl->db, + &tbl->grant.privilege, 0, 1) || + grant_option && check_grant(thd, INSERT_ACL, tbl, 0, 1, 1)) && + (check_access(thd, DELETE_ACL, tbl->db, + &tbl->grant.privilege, 0, 1) || + grant_option && check_grant(thd, DELETE_ACL, tbl, 0, 1, 1)) && + (check_access(thd, UPDATE_ACL, tbl->db, + &tbl->grant.privilege, 0, 1) || + grant_option && check_grant(thd, UPDATE_ACL, tbl, 0, 1, 1)) + ) + { + my_printf_error(ER_TABLEACCESS_DENIED_ERROR, + ER(ER_TABLEACCESS_DENIED_ERROR), + MYF(0), + "ANY", + thd->priv_user, + thd->host_or_ip, + tbl->real_name); + DBUG_RETURN(-1); + } + /* mark this table as table which will be checked after preparation */ + tbl->table_in_first_from_clause= 1; + + /* + We need to check only SELECT_ACL for all normal fields, fields + where we need any privilege will be pmarked later + */ + tbl->grant.want_privilege= SELECT_ACL; + /* + Make sure that all rights are loaded to table 'grant' field. + + tbl->real_name will be correct name of table because VIEWs are + not opened yet. + */ + fill_effective_table_privileges(thd, &tbl->grant, tbl->db, + tbl->real_name); + } + + if (&lex->select_lex != lex->all_selects_list) + { + /* check tables of subqueries */ + for (TABLE_LIST *tbl= tables; tbl; tbl= tbl->next_global) + { + if (!tbl->table_in_first_from_clause) + { + if (check_access(thd, SELECT_ACL, tbl->db, + &tbl->grant.privilege, 0, 0) || + grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0)) + { + res= 1; + goto err; + } + } + } + } + /* + Mark fields for special privilege check (any privilege) + + 'if' should be changed if we made updateable UNION. + */ + if (lex->select_lex.next_select() == 0) + { + List_iterator_fast<Item> it(lex->select_lex.item_list); + Item *item; + while ((item= it++)) + { + if (item->type() == Item::FIELD_ITEM) + ((Item_field *)item)->any_privileges= 1; + } + } +#endif + + if ((res= open_and_lock_tables(thd, tables))) + DBUG_RETURN(res); + + /* check that tables are not temporary */ + for (TABLE_LIST *tbl= tables; tbl; tbl= tbl->next_global) + { + if (tbl->table->tmp_table != NO_TMP_TABLE && !test(tbl->view)) + { + my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias); + res= -1; + goto err; + } + + /* + Copy privileges of underlaying VIEWs which was filled by + fill_effective_table_privileges + (they was not copied in derived tables processing) + */ + tbl->table->grant.privilege= tbl->grant.privilege; + } + + // prepare select to resolve all fields + lex->view_prepare_mode= 1; + if ((res= unit->prepare(thd, 0, 0))) + goto err; + + /* view list (list of view fields names) */ + if (lex->view_list.elements) + { + if (lex->view_list.elements != select_lex->item_list.elements) + { + my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0)); + goto err; + } + List_iterator_fast<Item> it(select_lex->item_list); + List_iterator_fast<LEX_STRING> nm(lex->view_list); + Item *item; + LEX_STRING *name; + while((item= it++, name= nm++)) + { + item->set_name(name->str, name->length, system_charset_info); + } + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* + Compare/check grants on view with grants of underlaying tables + */ + { + char *db= view->db ? view->db : thd->db; + List_iterator_fast<Item> it(select_lex->item_list); + Item *item; + fill_effective_table_privileges(thd, &view->grant, db, + view->real_name); + while((item= it++)) + { + uint priv= (get_column_grant(thd, &view->grant, db, + view->real_name, item->name) & + VIEW_ANY_ACL); + if (item->type() == Item::FIELD_ITEM) + { + Item_field *fld= (Item_field *)item; + /* + There are no any privileges on VIWE column or there are + some other privileges then we have for underlaying table + */ + if (priv == 0 || test(~fld->have_privileges & priv)) + { + /* VIEW column has more privileges */ + my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, + ER(ER_COLUMNACCESS_DENIED_ERROR), + MYF(0), + "create view", + thd->priv_user, + thd->host_or_ip, + item->name, + view->real_name); + DBUG_RETURN(-1); + } + } + else + { + if (!test(priv & SELECT_ACL)) + { + /* user have not privilege to SELECT expression */ + my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, + ER(ER_COLUMNACCESS_DENIED_ERROR), + MYF(0), + "select", + thd->priv_user, + thd->host_or_ip, + item->name, + view->real_name); + DBUG_RETURN(-1); + } + } + } + } +#endif + + if (wait_if_global_read_lock(thd, 0)) + { + VOID(pthread_mutex_unlock(&LOCK_open)); + goto err; + } + VOID(pthread_mutex_lock(&LOCK_open)); + if ((res= mysql_register_view(thd, view, mode))) + { + VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); + goto err; + } + VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); + + send_ok(thd); + lex->link_first_table_back(view, link_to_local); + return 0; + +err: + thd->proc_info= "end"; + lex->link_first_table_back(view, link_to_local); + unit->cleanup(); + if (thd->net.report_error) + res= -1; + DBUG_RETURN(res); +} + + +// index of revision number in following table +static const int revision_number_position= 4; + +static char *view_field_names[]= +{ + (char*)"query", + (char*)"md5", + (char*)"updatable", + (char*)"algorithm", + (char*)"revision", + (char*)"timestamp", + (char*)"create-version", + (char*)"source" +}; + +// table of VIEW .frm field descriprors +static File_option view_parameters[]= +{{{view_field_names[0], 5}, offsetof(TABLE_LIST, query), + FILE_OPTIONS_STRING}, + {{view_field_names[1], 3}, offsetof(TABLE_LIST, md5), + FILE_OPTIONS_STRING}, + {{view_field_names[2], 9}, offsetof(TABLE_LIST, updatable), + FILE_OPTIONS_ULONGLONG}, + {{view_field_names[3], 9}, offsetof(TABLE_LIST, algorithm), + FILE_OPTIONS_ULONGLONG}, + {{view_field_names[4], 8}, offsetof(TABLE_LIST, revision), + FILE_OPTIONS_REV}, + {{view_field_names[5], 9}, offsetof(TABLE_LIST, timestamp), + FILE_OPTIONS_TIMESTAMP}, + {{view_field_names[6], 14}, offsetof(TABLE_LIST, file_version), + FILE_OPTIONS_ULONGLONG}, + {{view_field_names[7], 6}, offsetof(TABLE_LIST, source), + FILE_OPTIONS_ESTRING}, + {{NULL, 0}, 0, + FILE_OPTIONS_STRING} +}; + +static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}}; + + +/* + Register VIEW (write .frm & process .frm's history backups) + + SYNOPSIS + mysql_register_view() + thd - thread handler + view - view description + mode - VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE + + RETURN + 0 OK + -1 Error + 1 Error and error message given +*/ +static int mysql_register_view(THD *thd, TABLE_LIST *view, + enum_view_create_mode mode) +{ + char buff[4096]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + char md5[33]; + bool can_be_merged; + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + LEX_STRING dir, file; + DBUG_ENTER("mysql_register_view"); + + // print query + str.length(0); + thd->lex->unit.print(&str); + str.append('\0'); + DBUG_PRINT("VIEW", ("View: %s", str.ptr())); + + // print file name + (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/", + mysql_data_home, view->db); + unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + dir.length= strlen(dir_buff); + + file.str= file_buff; + file.length= my_snprintf(file_buff, FN_REFLEN, "%s%s", + view->real_name, reg_ext); + /* init timestamp */ + if (!test(view->timestamp.str)) + view->timestamp.str= view->timestamp_buffer; + + // check old .frm + { + char path_buff[FN_REFLEN]; + LEX_STRING path; + path.str= path_buff; + fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME); + path.length= strlen(path_buff); + + if (!access(path.str, F_OK)) + { + if (mode == VIEW_CREATE_NEW) + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias); + DBUG_RETURN(1); + } + + File_parser *parser= sql_parse_prepare(&path, &thd->mem_root, 0); + if (parser) + { + if(parser->ok() && + !strncmp("VIEW", parser->type()->str, parser->type()->length)) + { + /* + read revision number + + TODO: read dependense list, too, to process cascade/restrict + TODO: special cascade/restrict procedure for alter? + */ + if (parser->parse((gptr)view, &thd->mem_root, + view_parameters + revision_number_position, 1)) + { + DBUG_RETURN(1); + } + } + else + { + my_error(ER_WRONG_OBJECT, MYF(0), (view->db?view->db:thd->db), + view->real_name, "VIEW"); + DBUG_RETURN(1); + } + } + else + { + DBUG_RETURN(1); + } + } + else + { + if (mode == VIEW_ALTER) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias); + DBUG_RETURN(1); + } + } + } + // fill structure + view->query.str= (char*)str.ptr(); + view->query.length= str.length()-1; // we do not need last \0 + view->source.str= thd->query; + view->source.length= thd->query_length; + view->file_version= 1; + view->calc_md5(md5); + view->md5.str= md5; + view->md5.length= 32; + can_be_merged= thd->lex->can_be_merged(); + if (thd->lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && + !thd->lex->can_be_merged()) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, + ER(ER_WARN_VIEW_MERGE)); + thd->lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + } + view->algorithm= thd->lex->create_view_algorithm; + if ((view->updatable= (can_be_merged && + view->algorithm != VIEW_ALGORITHM_TMEPTABLE))) + { + // TODO: change here when we will support UNIONs + for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first; + tbl; + tbl= tbl->next_local) + { + if (tbl->view != 0 && !tbl->updatable) + { + view->updatable= 0; + break; + } + } + } + if (sql_create_definition_file(&dir, &file, view_file_type, + (gptr)view, view_parameters, 3)) + { + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +/* + read VIEW .frm and create structures + + SYNOPSIS + mysql_make_view() + parser - parser object; + table - TABLE_LIST structure for filling +*/ +my_bool +mysql_make_view(File_parser *parser, TABLE_LIST *table) +{ + DBUG_ENTER("mysql_make_view"); + + if (table->view) + { + DBUG_PRINT("info", + ("VIEW %s.%s is already processed on previos PS/SP execution", + table->view_db.str, table->view_name.str)); + DBUG_RETURN(0); + } + + TABLE_LIST *old_next, *tbl_end, *tbl_next; + SELECT_LEX *end, *next; + THD *thd= current_thd; + LEX *old_lex= thd->lex, *lex; + int res= 0; + + /* + For now we assume that tables will not be changed during PS life (it + will be TRUE as far as we make new table cache). + */ + Item_arena *arena= thd->current_arena, backup; + if (arena) + thd->set_n_backup_item_arena(arena, &backup); + + /* init timestamp */ + if (!test(table->timestamp.str)) + table->timestamp.str= table->timestamp_buffer; + /* + TODO: when VIEWs will be stored in cache, table mem_root should + be used here + */ + if (parser->parse((gptr)table, &thd->mem_root, view_parameters, 6)) + goto err; + + /* + Save VIEW parameters, which will be wiped out by derived table + processing + */ + table->view_db.str= table->db; + table->view_db.length= table->db_length; + table->view_name.str= table->real_name; + table->view_name.length= table->real_name_length; + + //TODO: md5 test here and warning if it is differ + + table->view= lex= thd->lex= new st_lex; + lex_start(thd, (uchar*)table->query.str, table->query.length); + mysql_init_query(thd, true); + lex->select_lex.select_number= ++thd->select_number; + old_lex->derived_tables|= DERIVED_VIEW; + { + ulong options= thd->options; + /* switch off modes which can prevent normal parsing of VIEW + - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing + + MODE_PIPES_AS_CONCAT affect expression parsing + + MODE_ANSI_QUOTES affect expression parsing + + MODE_IGNORE_SPACE affect expression parsing + - MODE_NOT_USED not used :) + * MODE_ONLY_FULL_GROUP_BY affect execution + * MODE_NO_UNSIGNED_SUBTRACTION affect execution + - MODE_NO_DIR_IN_CREATE affect table creation only + - MODE_POSTGRESQL compounded from other modes + - MODE_ORACLE compounded from other modes + - MODE_MSSQL compounded from other modes + - MODE_DB2 compounded from other modes + - MODE_MAXDB affect only CREATE TABLE parsing + - MODE_NO_KEY_OPTIONS affect only SHOW + - MODE_NO_TABLE_OPTIONS affect only SHOW + - MODE_NO_FIELD_OPTIONS affect only SHOW + - MODE_MYSQL323 affect only SHOW + - MODE_MYSQL40 affect only SHOW + - MODE_ANSI compounded from other modes + (+ transaction mode) + ? MODE_NO_AUTO_VALUE_ON_ZERO affect UPDATEs + + MODE_NO_BACKSLASH_ESCAPES affect expression parsing + */ + thd->options&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | + MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); + res= yyparse((void *)thd); + thd->options= options; + } + if (!res && !thd->is_fatal_error) + { + old_next= table->next_global; + if ((table->next_global= lex->query_tables)) + table->next_global->prev_global= &table->next_global; + + /* mark to avoid temporary table using */ + for (TABLE_LIST *tbl= table->next_global; tbl; tbl= tbl->next_global) + tbl->skip_temporary= 1; + + /* + check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show + underlaying tables + */ + if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe) || + old_lex->sql_command == SQLCOM_SHOW_CREATE) + { + if (check_table_access(thd, SELECT_ACL, table->next_global, 1) && + check_table_access(thd, SHOW_VIEW_ACL, table->next_global, 1)) + { + my_error(ER_VIEW_NO_EXPLAIN, MYF(0)); + goto err; + } + } + + /* move SQL_NO_CACHE & Co to whole query */ + old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query && + lex->safe_to_cache_query); + /* move SQL_CACHE to whole query */ + if (lex->select_lex.options & OPTION_TO_QUERY_CACHE) + old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + + /* + check MERGE algorithm ability + - algorithm is not explicit TEMPORARY TABLE + - VIEW SELECT allow marging + - VIEW used in subquery or command support MERGE algorithm + */ + if (table->algorithm != VIEW_ALGORITHM_TMEPTABLE && + lex->can_be_merged() && + (table->select_lex->master_unit() != &old_lex->unit || + old_lex->can_use_merged())) + { + /* + TODO: support multi tables substitutions + + table->next_global should be the same as + (TABLE_LIST *)lex->select_lex.table_list.first; + */ + TABLE_LIST *view_table= table->next_global; + /* lex should contain at least one table */ + DBUG_ASSERT(view_table != 0); + + table->effective_algorithm= VIEW_ALGORITHM_MERGE; + + if (old_next) + { + if ((view_table->next_global= old_next)) + old_next->prev_global= &view_table->next_global; + } + table->ancestor= view_table; + // next table should include SELECT_LEX under this table SELECT_LEX + table->ancestor->select_lex= table->select_lex; + /* + move lock type (TODO: should we issue error in case of TMPTABLE + algorithm and non-read locking)? + */ + view_table->lock_type= table->lock_type; + + /* Store WHERE clause for postprocessing in setup_ancestor */ + table->where= lex->select_lex.where; + + /* + This SELECT_LEX will be linked in global SELECT_LEX list + to make it processed by mysql_handle_derived(), + but it will not be included to SELECT_LEX tree, because it + will not be executed + */ + goto ok; + } + + table->effective_algorithm= VIEW_ALGORITHM_TMEPTABLE; + if (table->updatable) + { + //TOTO: warning: can't be updateable, .frm edited by hand. version + //downgrade? + table->updatable= 0; + } + + /* SELECT tree link */ + lex->unit.include_down(table->select_lex); + lex->unit.slave= &lex->select_lex; // fix include_down initialisation + + if (old_next) + { + if ((tbl_end= table->next_global)) + { + for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next); + if ((tbl_end->next_global= old_next)) + tbl_end->next_global->prev_global= &tbl_end->next_global; + } + } + + table->derived= &lex->unit; + } + else + goto err; + +ok: + if (arena) + thd->restore_backup_item_arena(arena, &backup); + /* global SELECT list linking */ + end= &lex->select_lex; // primary SELECT_LEX is always last + end->link_next= old_lex->all_selects_list; + old_lex->all_selects_list->link_prev= &end->link_next; + old_lex->all_selects_list= lex->all_selects_list; + lex->all_selects_list->link_prev= + (st_select_lex_node**)&old_lex->all_selects_list; + + thd->lex= old_lex; + DBUG_RETURN(0); + +err: + if (arena) + thd->restore_backup_item_arena(arena, &backup); + table->view= 0; // now it is not VIEW placeholder + thd->lex= old_lex; + DBUG_RETURN(1); +} + + +/* + drop view + + SYNOPSIS + mysql_drop_view() + thd - thread handler + views - views to delete + drop_mode - cascade/check + + RETURN VALUE + 0 OK + -1 Error + 1 Error and error message given +*/ +int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) +{ + DBUG_ENTER("mysql_drop_view"); + char path[FN_REFLEN]; + TABLE_LIST *view; + bool type= 0; + + for (view= views; view; view= view->next_local) + { + strxmov(path, mysql_data_home, "/", view->db, "/", view->real_name, + reg_ext, NullS); + (void) unpack_filename(path, path); + VOID(pthread_mutex_lock(&LOCK_open)); + if (access(path, F_OK) || (type= (mysql_frm_type(path) != FRMTYPE_VIEW))) + { + char name[FN_REFLEN]; + my_snprintf(name, sizeof(name), "%s.%s", view->db, view->real_name); + if (thd->lex->drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), + name); + VOID(pthread_mutex_unlock(&LOCK_open)); + continue; + } + if (type) + my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->real_name, "VIEW"); + else + my_error(ER_BAD_TABLE_ERROR, MYF(0), name); + goto err; + } + if (my_delete(path, MYF(MY_WME))) + goto err; + VOID(pthread_mutex_unlock(&LOCK_open)); + } + send_ok(thd); + DBUG_RETURN(0); + +err: + VOID(pthread_mutex_unlock(&LOCK_open)); + DBUG_RETURN(-1); + +} + + +/* + Check type of .frm if we are not going to parse it + + SYNOPSIS + mysql_frm_type() + path path to file + + RETURN + FRMTYPE_ERROR error + FRMTYPE_TABLE table + FRMTYPE_VIEW view +*/ + +frm_type_enum mysql_frm_type(char *path) +{ + File file; + char header[10]; //"TYPE=VIEW\n" it is 10 characters + DBUG_ENTER("mysql_frm_type"); + + if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0) + { + DBUG_RETURN(FRMTYPE_ERROR); + } + if (my_read(file, header, 10, MYF(MY_WME)) == MY_FILE_ERROR) + { + my_close(file, MYF(MY_WME)); + DBUG_RETURN(FRMTYPE_ERROR); + } + my_close(file, MYF(MY_WME)); + if (strncmp(header, "TYPE=VIEW\n", 10) != 0) + DBUG_RETURN(FRMTYPE_TABLE); + DBUG_RETURN(FRMTYPE_VIEW); +} + + +/* + check of key (primary or unique) presence in updatable view + + SYNOPSIS + check_key_in_view() + thd thread handler + view view for check with opened table + + RETURN + FALSE OK + TRUE view do not contain key or all fields +*/ + +bool check_key_in_view(THD *thd, TABLE_LIST *view) +{ + DBUG_ENTER("check_key_in_view"); + if (!view->view) + DBUG_RETURN(FALSE); /* it is normal table */ + + TABLE *table= view->table; + Item **trans= view->field_translation; + KEY *key_info= table->key_info; + uint primary_key= table->primary_key; + uint num= view->view->select_lex.item_list.elements; + DBUG_ASSERT(view->table != 0 && view->field_translation != 0); + + /* try to find key */ + for (uint i=0; i < table->keys; i++, key_info++) + { + if (i == primary_key && !strcmp(key_info->name, primary_key_name) || + key_info->flags & HA_NOSAME) + { + KEY_PART_INFO *key_part= key_info->key_part; + bool found= 1; + for (uint j=0; j < key_info->key_parts && found; j++, key_part++) + { + found= 0; + for (uint k= 0; k < num; k++) + { + if (trans[k]->type() == Item::FIELD_ITEM && + ((Item_field *)trans[k])->field == key_part->field && + (key_part->field->flags & NOT_NULL_FLAG)) + { + found= 1; + break; + } + } + } + if (found) + DBUG_RETURN(FALSE); + } + } + + /* check all fields presence */ + { + Field **field_ptr= table->field; + for (; *field_ptr; ++field_ptr) + { + uint i= 0; + for (; i < num; i++) + { + if (trans[i]->type() == Item::FIELD_ITEM && + ((Item_field *)trans[i])->field == *field_ptr) + break; + } + if (i >= num) + { + ulong mode= thd->variables.sql_updatable_view_key; + /* 1 == YES, 2 == LIMIT1 */ + if (mode == 1 || + (mode == 2 && + thd->lex->select_lex.select_limit == 1)) + { + DBUG_RETURN(TRUE); + } + else + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); + DBUG_RETURN(FALSE); + } + } + } + } + DBUG_RETURN(FALSE); +} + + +/* + insert fields from VIEW (MERGE algorithm) into given list + + SYNOPSIS + insert_view_fields() + list list for insertion + view view for processing +*/ + +void insert_view_fields(List<Item> *list, TABLE_LIST *view) +{ + uint num= view->view->select_lex.item_list.elements; + Item **trans= view->field_translation; + DBUG_ENTER("insert_view_fields"); + if (!trans) + DBUG_VOID_RETURN; + + for (uint i= 0; i < num; i++) + { + if (trans[i]->type() == Item::FIELD_ITEM) + { + list->push_back(trans[i]); + } + } + DBUG_VOID_RETURN; +} diff --git a/sql/sql_view.h b/sql/sql_view.h new file mode 100644 index 00000000000..36eae6cdcfc --- /dev/null +++ b/sql/sql_view.h @@ -0,0 +1,42 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +int mysql_create_view(THD *thd, + enum_view_create_mode mode); + +my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table); + +int mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode); + +bool check_key_in_view(THD *thd, TABLE_LIST * view); + +void insert_view_fields(List<Item> *list, TABLE_LIST *view); + +enum frm_type_enum +{ + FRMTYPE_ERROR, + FRMTYPE_TABLE, + FRMTYPE_VIEW +}; + +frm_type_enum mysql_frm_type(char *path); + +extern TYPELIB sql_updatable_view_key_typelib; + +#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL) + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 03d5d748837..af6694c6318 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -192,6 +192,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ACTION %token AGGREGATE_SYM +%token ALGORITHM_SYM %token ALL %token AND_SYM %token AS @@ -210,6 +211,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token BYTE_SYM %token CACHE_SYM %token CASCADE +%token CASCADED %token CAST_SYM %token CHARSET %token CHECKSUM_SYM @@ -302,6 +304,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LONG_NUM %token LONG_SYM %token LOW_PRIORITY +%token MERGE_SYM %token MASTER_HOST_SYM %token MASTER_USER_SYM %token MASTER_LOG_FILE_SYM @@ -402,6 +405,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TABLE_SYM %token TABLESPACE %token TEMPORARY +%token TEMPTABLE_SYM %token TERMINATED %token TEXT_STRING %token TO_SYM @@ -431,6 +435,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VALUE_SYM %token VALUES %token VARIABLES +%token VIEW_SYM %token WHERE %token WITH %token WRITE_SYM @@ -774,7 +779,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); subselect_end select_var_list select_var_list_init help opt_len opt_extended_describe prepare prepare_src execute deallocate - statement sp_suid + statement sp_suid opt_view_list view_list or_replace algorithm sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic sp_a_chistic END_OF_INPUT @@ -1182,7 +1187,19 @@ create: if (lex->sphead->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; lex->sphead->restore_thd_mem_root(YYTHD); - } + } + | CREATE or_replace algorithm VIEW_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_CREATE_VIEW; + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; + /* first table in list is target VIEW name */ + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, 0)) + YYABORT; + } + opt_view_list AS select_init check_option + {} ; sp_name: @@ -2177,6 +2194,10 @@ create_select: lex->sql_command= SQLCOM_INSERT_SELECT; else if (lex->sql_command == SQLCOM_REPLACE) lex->sql_command= SQLCOM_REPLACE_SELECT; + /* + following work only with local list, global list is + created correctly in this case + */ lex->current_select->table_list.save_and_clear(&lex->save_list); mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LEX_NODE::SELECT_LIST; @@ -2186,7 +2207,13 @@ create_select: Select->parsing_place= SELECT_LEX_NODE::NO_MATTER; } opt_select_from - { Lex->current_select->table_list.push_front(&Lex->save_list); } + { + /* + following work only with local list, global list is + created correctly in this case + */ + Lex->current_select->table_list.push_front(&Lex->save_list); + } ; opt_as: @@ -2260,10 +2287,12 @@ create_table_option: TABLE_LIST *table_list= lex->select_lex.get_table_list(); lex->create_info.merge_list= lex->select_lex.table_list; lex->create_info.merge_list.elements--; - lex->create_info.merge_list.first= (byte*) (table_list->next); + lex->create_info.merge_list.first= + (byte*) (table_list->next_local); lex->select_lex.table_list.elements=1; - lex->select_lex.table_list.next= (byte**) &(table_list->next); - table_list->next=0; + lex->select_lex.table_list.next= + (byte**) &(table_list->next_local); + table_list->next_local= 0; lex->create_info.used_fields|= HA_CREATE_USED_UNION; } | opt_default charset opt_equal charset_name_or_default @@ -2926,6 +2955,18 @@ alter: lex->sql_command= SQLCOM_ALTER_FUNCTION; lex->spname= $3; } + | ALTER VIEW_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_CREATE_VIEW; + lex->create_view_mode= VIEW_ALTER; + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; + /* first table in list is target VIEW name */ + lex->select_lex.add_table_to_list(thd, $3, NULL, 0); + } + opt_view_list AS select_init + {} ; alter_list: @@ -3074,9 +3115,10 @@ opt_ignore: | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }; opt_restrict: - /* empty */ {} - | RESTRICT {} - | CASCADE {}; + /* empty */ { Lex->drop_mode= DROP_DEFAULT; } + | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } + | CASCADE { Lex->drop_mode= DROP_CASCADE; } + ; opt_place: /* empty */ {} @@ -3790,12 +3832,16 @@ simple_expr: | '@' ident_or_text SET_VAR expr { $$= new Item_func_set_user_var($2,$4); - Lex->uncacheable(UNCACHEABLE_RAND); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + lex->variables_used= 1; } | '@' ident_or_text { $$= new Item_func_get_user_var($2); - Lex->uncacheable(UNCACHEABLE_RAND); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + lex->variables_used= 1; } | '@' '@' opt_var_ident_type ident_or_text opt_component { @@ -3807,6 +3853,7 @@ simple_expr: } if (!($$= get_system_var(YYTHD, (enum_var_type) $3, $4, $5))) YYABORT; + Lex->variables_used= 1; } | sum_expr | '+' expr %prec NEG { $$= $2; } @@ -4646,7 +4693,7 @@ table_factor: select_derived: { LEX *lex= Lex; - lex->derived_tables= 1; + lex->derived_tables|= DERIVED_SUBQUERY; if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN && lex->sql_command <= (int)SQLCOM_HA_READ) || lex->sql_command == (int)SQLCOM_KILL) @@ -5156,6 +5203,13 @@ drop: } user_list {} + | DROP VIEW_SYM if_exists table_list opt_restrict + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_DROP_VIEW; + lex->drop_if_exists= $3; + } ; table_list: @@ -5634,9 +5688,19 @@ show_param: } | CREATE TABLE_SYM table_ident { - Lex->sql_command = SQLCOM_SHOW_CREATE; - if (!Select->add_table_to_list(YYTHD, $3, NULL,0)) + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0)) + YYABORT; + lex->only_view= 0; + } + | CREATE VIEW_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0)) YYABORT; + lex->only_view= 1; } | MASTER_SYM STATUS_SYM { @@ -6310,6 +6374,7 @@ keyword: | AFTER_SYM {} | AGAINST {} | AGGREGATE_SYM {} + | ALGORITHM_SYM {} | ANY_SYM {} | ASCII_SYM {} | AUTO_INC {} @@ -6325,6 +6390,7 @@ keyword: | BYTE_SYM {} | BTREE_SYM {} | CACHE_SYM {} + | CASCADED {} | CHANGED {} | CHARSET {} | CHECKSUM_SYM {} @@ -6417,6 +6483,7 @@ keyword: | MAX_QUERIES_PER_HOUR {} | MAX_UPDATES_PER_HOUR {} | MEDIUM_SYM {} + | MERGE_SYM {} | MICROSECOND_SYM {} | MINUTE_SYM {} | MIN_ROWS {} @@ -6499,6 +6566,7 @@ keyword: | SUPER_SYM {} | TABLESPACE {} | TEMPORARY {} + | TEMPTABLE_SYM {} | TEXT_SYM {} | TRANSACTION_SYM {} | TRUNCATE_SYM {} @@ -6515,6 +6583,7 @@ keyword: | USER {} | USE_FRM {} | VARIABLES {} + | VIEW_SYM {} | VALUE_SYM {} | WARNINGS {} | WEEK_SYM {} @@ -6926,8 +6995,10 @@ grant_privilege: | SUPER_SYM { Lex->grant |= SUPER_ACL;} | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } - | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL;} - | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL;} + | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; } + | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } + | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } + | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } ; @@ -7316,3 +7387,41 @@ subselect_end: lex->current_select = lex->current_select->return_after_parsing(); }; +opt_view_list: + /* empty */ {} + | '(' view_list ')' + ; + +view_list: + ident + { + Lex->view_list.push_back((LEX_STRING*) + sql_memdup(&$1, sizeof(LEX_STRING))); + } + | view_list ',' ident + { + Lex->view_list.push_back((LEX_STRING*) + sql_memdup(&$3, sizeof(LEX_STRING))); + } + ; + +or_replace: + /* empty */ { Lex->create_view_mode= VIEW_CREATE_NEW; } + | OR_SYM REPLACE { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; } + ; + +algorithm: + /* empty */ + { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } + | ALGORITHM_SYM EQ MERGE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } + | ALGORITHM_SYM EQ TEMPTABLE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_TMEPTABLE; } + ; +check_option: + /* empty */ {} + | WITH CHECK_SYM OPTION {} + | WITH CASCADED CHECK_SYM OPTION {} + | WITH LOCAL_SYM CHECK_SYM OPTION {} + ; + diff --git a/sql/table.cc b/sql/table.cc index 7d1c733b116..43b39ffb37e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -20,7 +20,8 @@ #include "mysql_priv.h" #include <errno.h> #include <m_ctype.h> - +#include "md5.h" +#include "sql_acl.h" /* Functions defined in this file */ @@ -57,6 +58,7 @@ static byte* get_field_name(Field **buff,uint *length, 2 Error (see frm_error) 3 Wrong data in .frm file 4 Error (see frm_error) + 5 It is new format of .frm file */ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, @@ -81,17 +83,46 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, uchar *null_pos; uint null_bit, new_frm_ver, field_pack_length; SQL_CRYPT *crypted=0; + MEM_ROOT *old_root; DBUG_ENTER("openfrm"); DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam)); + error=1; + + if ((file=my_open(fn_format(index_file, name, "", reg_ext, + MY_UNPACK_FILENAME), + O_RDONLY | O_SHARE, + MYF(0))) + < 0) + { + goto err_w_init; + } + + if (my_read(file,(byte*) head,64,MYF(MY_NABP))) + { + goto err_w_init; + } + + if (memcmp(head, "TYPE=", 5) == 0) + { + // new .frm + my_close(file,MYF(MY_WME)); + + if (db_stat & NO_ERR_ON_NEW_FRM) + DBUG_RETURN(5); + + // caller can't process new .frm + error= 4; + goto err_w_init; + } + bzero((char*) outparam,sizeof(*outparam)); outparam->blob_ptr_size=sizeof(char*); disk_buff=NULL; record= NULL; keynames=NullS; outparam->db_stat = db_stat; - error=1; init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); + old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root); outparam->real_name=strdup_root(&outparam->mem_root, @@ -101,19 +132,11 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (!outparam->real_name || !outparam->table_name) goto err_end; - if ((file=my_open(fn_format(index_file,name,"",reg_ext,MY_UNPACK_FILENAME), - O_RDONLY | O_SHARE, - MYF(0))) - < 0) - { - goto err_end; /* purecov: inspected */ - } error=4; if (!(outparam->path= strdup_root(&outparam->mem_root,name))) goto err_not_open; *fn_ext(outparam->path)='\0'; // Remove extension - if (my_read(file,(byte*) head,64,MYF(MY_NABP))) goto err_not_open; if (head[0] != (uchar) 254 || head[1] != 1 || (head[2] != FRM_VER && head[2] != FRM_VER+1 && head[2] != FRM_VER+3)) goto err_not_open; /* purecov: inspected */ @@ -718,6 +741,13 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, #endif DBUG_RETURN (0); + err_w_init: + //awoid problem with uninitialized data + bzero((char*) outparam,sizeof(*outparam)); + outparam->real_name= (char*)name+dirname_length(name); + old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); + disk_buff= 0; + err_not_open: x_free((gptr) disk_buff); if (file > 0) @@ -1410,6 +1440,190 @@ db_type get_table_type(const char *name) } +/* + calculate md5 of query + + SYNOPSIS + st_table_list::calc_md5() + buffer buffer for md5 writing +*/ +void st_table_list::calc_md5(char *buffer) +{ + my_MD5_CTX context; + unsigned char digest[16]; + my_MD5Init (&context); + my_MD5Update (&context,(unsigned char *) query.str, query.length); + my_MD5Final (digest, &context); + sprintf((char *) buffer, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[0], digest[1], digest[2], digest[3], + digest[4], digest[5], digest[6], digest[7], + digest[8], digest[9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]); +} + + +/* + set ancestor TABLE for table place holder of VIEW + + SYNOPSIS + st_table_list::set_ancestor() +*/ +void st_table_list::set_ancestor() +{ + if (ancestor->ancestor) + ancestor->set_ancestor(); + table= ancestor->table; + ancestor->table->grant= grant; +} + + +/* + setup fields of placeholder of merged VIEW + + SYNOPSIS + st_table_list::setup_ancestor() + thd - thread handler + conds - condition of this JOIN + + RETURN + 0 - OK + 1 - error + + TODO: for several substituted table last set up table (or maybe subtree, + it depends on future join implementation) will contain all fields of VIEW + (to be able call fix_fields() for them. All other will looks like empty + (without fields) for name resolving, but substituted expressions will + return correct used tables mask. +*/ +bool st_table_list::setup_ancestor(THD *thd, Item **conds) +{ + Item **transl; + SELECT_LEX *select= &view->select_lex; + Item *item; + List_iterator_fast<Item> it(select->item_list); + uint i= 0; + bool save_set_query_id= thd->set_query_id; + DBUG_ENTER("st_table_list::setup_ancestor"); + + if (ancestor->ancestor && + ancestor->setup_ancestor(thd, conds)) + DBUG_RETURN(1); + + if (field_translation) + { + thd->set_query_id= 1; + /* this view was prepared already on previous PS/SP execution */ + Item **end= field_translation + select->item_list.elements; + for (Item **i= field_translation; i < end; i++) + { + //TODO: fix for several tables in VIEW + uint want_privilege= ancestor->table->grant.want_privilege; + /* real rights will be checked in VIEW field */ + ancestor->table->grant.want_privilege= 0; + if (!(*i)->fixed && (*i)->fix_fields(thd, ancestor, i)) + goto err; + ancestor->table->grant.want_privilege= want_privilege; + } + goto ok; + } + + /* view fields translation table */ + if (!(transl= + (Item**)(thd->current_arena ? + thd->current_arena : + thd)->alloc(select->item_list.elements * sizeof(Item*)))) + { + DBUG_RETURN(1); + } + /* + Resolve all view items against ancestor table. + + TODO: do it only for real used fields "on demand" to mark really + used fields correctly. + */ + thd->set_query_id= 1; + while ((item= it++)) + { + //TODO: fix for several tables in VIEW + uint want_privilege= ancestor->table->grant.want_privilege; + /* real rights will be checked in VIEW field */ + ancestor->table->grant.want_privilege= 0; + if (!item->fixed && item->fix_fields(thd, ancestor, &item)) + { + goto err; + } + ancestor->table->grant.want_privilege= want_privilege; + transl[i++]= item; + } + field_translation= transl; + //TODO: sort this list? Use hash for big number of fields + + if (where) + { + Item_arena *arena= thd->current_arena, backup; + if (!where->fixed && where->fix_fields(thd, ancestor, &where)) + goto err; + + if (arena) + thd->set_n_backup_item_arena(arena, &backup); + if (outer_join) + { + /* + Store WHERE condition to ON expression for outer join, because we + can't use WHERE to correctly execute jeft joins on VIEWs and this + expression will not be moved to WHERE condition (i.e. will be clean + correctly for PS/SP) + */ + on_expr= and_conds(on_expr, where); + } + else + { + /* + It is conds of JOIN, but it will be stored in st_select_lex::prep_where + for next reexecution + */ + *conds= and_conds(*conds, where); + } + if (arena) + thd->restore_backup_item_arena(arena, &backup); + } + +ok: + thd->set_query_id= save_set_query_id; + DBUG_RETURN(0); + +err: + thd->set_query_id= save_set_query_id; + DBUG_RETURN(1); +} + + +void Field_iterator_view::set(TABLE_LIST *table) +{ + ptr= table->field_translation; + array_end= ptr + table->view->select_lex.item_list.elements; +} + + +const char *Field_iterator_table::name() +{ + return (*ptr)->field_name; +} + + +Item *Field_iterator_table::item(THD *thd) +{ + return new Item_field(thd, *ptr); +} + + +const char *Field_iterator_view::name() +{ + return (*ptr)->name; +} + + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index 0b56c1ad280..5e00820a6e5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -20,6 +20,7 @@ class Item; /* Needed by ORDER */ class GRANT_TABLE; class st_select_lex_unit; +class st_select_lex; /* Order clause list element */ @@ -70,7 +71,7 @@ struct st_table { /* hash of field names (contains pointers to elements of field array) */ HASH name_hash; byte *record[2]; /* Pointer to records */ - byte *default_values; /* Default values for INSERT */ + byte *default_values; /* Default values for INSERT */ byte *insert_values; /* used by INSERT ... UPDATE */ uint fields; /* field count */ uint reclength; /* Recordlength */ @@ -170,19 +171,53 @@ struct st_table { #define JOIN_TYPE_LEFT 1 #define JOIN_TYPE_RIGHT 2 +#define VIEW_ALGORITHM_UNDEFINED 0 +#define VIEW_ALGORITHM_TMEPTABLE 1 +#define VIEW_ALGORITHM_MERGE 2 + +struct st_lex; + typedef struct st_table_list { - struct st_table_list *next; + /* link in a local table list (used by SQL_LIST) */ + struct st_table_list *next_local; + /* link in a global list of all queries tables */ + struct st_table_list *next_global, **prev_global; char *db, *alias, *real_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ struct st_table_list *natural_join; /* natural join on this table*/ /* ... join ... USE INDEX ... IGNORE INDEX */ List<String> *use_index, *ignore_index; - TABLE *table; /* opened table */ - st_table_list *table_list; /* pointer to node of list of all tables */ - class st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ - GRANT_INFO grant; + TABLE *table; /* opened table */ + /* + Reference from aux_tables to local list entry of main select of + multi-delete statement: + delete t1 from t2,t1 where t1.a<'B' and t2.b=t1.b; + here it will be reference of first occurrence of t1 to second (as you + can see this lists can't be merged) + */ + st_table_list *correspondent_table; + st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ + /* link to select_lex where this table was used */ + st_select_lex *select_lex; + st_lex *view; /* link on VIEW lex for merging */ + Item **field_translation; /* array of VIEW fields */ + /* ancestor of this table (VIEW merge algorithm) */ + st_table_list *ancestor; + Item *where; /* VIEW WHERE clause condition */ + LEX_STRING query; /* text of (CRETE/SELECT) statement */ + LEX_STRING md5; /* md5 of query tesxt */ + LEX_STRING source; /* source of CREATE VIEW */ + LEX_STRING view_db; /* save view database */ + LEX_STRING view_name; /* save view name */ + LEX_STRING timestamp; /* GMT time stamp of last operation */ + ulonglong file_version; /* version of file's field set */ + ulonglong revision; /* revision control number */ + ulonglong updatable; /* Is VIEW updateable */ + ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */ + uint effective_algorithm; /* which algorithm was really used */ + GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ uint shared; /* Used in multi-upd */ @@ -197,12 +232,64 @@ typedef struct st_table_list st_table_list *embedding; /* nested join containing the table */ List<struct st_table_list> *join_list;/* join list the table belongs to */ bool cacheable_table; /* stop PS caching */ - /* used in multi-upd privelege check */ - bool table_in_update_from_clause; + /* used in multi-upd/views privelege check */ + bool table_in_first_from_clause; + bool skip_temporary; /* this table shouldn't be temporary */ + bool setup_is_done; /* setup_tables() is done */ + /* do view contain auto_increment field */ + bool contain_auto_increment; + char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ + void calc_md5(char *buffer); + void set_ancestor(); + bool setup_ancestor(THD *thd, Item **conds); + bool placeholder() {return derived || view; } void print(THD *thd, String *str); } TABLE_LIST; +class Item; + +class Field_iterator: public Sql_alloc +{ +public: + virtual ~Field_iterator() {} + virtual void set(TABLE_LIST *)= 0; + virtual void next()= 0; + virtual bool end()= 0; + virtual const char *name()= 0; + virtual Item *item(THD *)= 0; + virtual Field *field()= 0; +}; + + +class Field_iterator_table: public Field_iterator +{ + Field **ptr; +public: + Field_iterator_table() :ptr(0) {} + void set(TABLE_LIST *table) { ptr= table->table->field; } + void set_table(TABLE *table) { ptr= table->field; } + void next() { ptr++; } + bool end() { return test(*ptr); } + const char *name(); + Item *item(THD *thd); + Field *field() { return *ptr; } +}; + + +class Field_iterator_view: public Field_iterator +{ + Item **ptr, **array_end; +public: + Field_iterator_view() :ptr(0), array_end(0) {} + void set(TABLE_LIST *table); + void next() { ptr++; } + bool end() { return ptr < array_end; } + const char *name(); + Item *item(THD *thd) { return *ptr; } + Field *field() { return 0; } +}; + typedef struct st_nested_join { List<TABLE_LIST> join_list; /* list of elements in the nested join */ diff --git a/sql/tztime.cc b/sql/tztime.cc index aab0d36b61e..f2d20634ec5 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1701,9 +1701,9 @@ tz_load_from_db(THD *thd, const String *tz_name) tables[1].alias= tables[1].real_name= (char*)"time_zone"; tables[2].alias= tables[2].real_name= (char*)"time_zone_transition"; tables[3].alias= tables[3].real_name= (char*)"time_zone_transition_type"; - tables[0].next= tables+1; - tables[1].next= tables+2; - tables[2].next= tables+3; + tables[0].next_local= tables[0].next_global= tables+1; + tables[1].next_local= tables[1].next_global= tables+2; + tables[2].next_local= tables[2].next_global= tables+3; tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= tables[3].lock_type= TL_READ; tables[0].db= tables[1].db= tables[2].db= tables[3].db= thd->db; diff --git a/sql/unireg.h b/sql/unireg.h index 4ab2ba26b15..2879e30d861 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -139,6 +139,7 @@ #define DONT_GIVE_ERROR 256 /* Don't do frm_error on openfrm */ #define READ_SCREENS 1024 /* Read screens, info and helpfile */ #define DELAYED_OPEN 4096 /* Open table later */ +#define NO_ERR_ON_NEW_FRM 8192 /* stop error sending on new format */ #define SC_INFO_LENGTH 4 /* Form format constant */ #define TE_INFO_LENGTH 3 diff --git a/tests/client_test.c b/tests/client_test.c index 4692ea45c48..13f5a3ac852 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -6823,8 +6823,8 @@ static void test_explain_bug() verify_prepare_field(result, 5, "key", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_LEN, 0); - verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_LONGLONG, - "", "", "", 3, 0); + verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING, + "", "", "", NAME_LEN*64, 0); verify_prepare_field(result, 7, "ref", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_LEN*16, 0); @@ -10037,6 +10037,358 @@ static void test_bug4030() mysql_stmt_close(stmt); } +static void test_view() +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND bind[1]; + char str_data[50]; + long length = 0L; + long is_null = 0L; + const char *query= + "SELECT COUNT(*) FROM v1 WHERE `SERVERNAME`=?"; + + myheader("test_view"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2,t3,v1"); + myquery(rc); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1,t2,t3"); + myquery(rc); + rc= mysql_query(mysql,"CREATE TABLE `t1` ( `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `DBINSTANCE` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERGRP`)) ENGINE=InnoDB DEFAULT CHARSET=latin1"); + myquery(rc); + rc= mysql_query(mysql,"CREATE TABLE `t2` ( `SERVERNAME` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERNAME`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); + myquery(rc); + rc= mysql_query(mysql,"CREATE TABLE `t3` ( `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `TABNAME` varchar(30) character set latin1 collate latin1_bin NOT NULL default '', `MAPSTATE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `ACTSTATE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `LOCAL_NAME` varchar(30) character set latin1 collate latin1_bin NOT NULL default '', `CHG_DATE` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `CHG_TIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `MXUSER` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERGRP`,`TABNAME`,`MAPSTATE`,`ACTSTATE`,`LOCAL_NAME`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;"); + myquery(rc); + rc= mysql_query(mysql,"CREATE VIEW v1 AS select sql_no_cache T0001.SERVERNAME AS `SERVERNAME`,T0003.TABNAME AS `TABNAME`,T0003.LOCAL_NAME AS `LOCAL_NAME`,T0002.DBINSTANCE AS `DBINSTANCE` from t2 T0001 join t1 T0002 join t3 T0003 where ((T0002.SERVERGRP = T0001.SERVERGRP) and (T0002.SERVERGRP = T0003.SERVERGRP) and (T0003.MAPSTATE = _latin1'A') and (T0003.ACTSTATE = _latin1' '))"); + myquery(rc); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + strcpy(str_data, "TEST"); + bind[0].buffer_type= FIELD_TYPE_STRING; + bind[0].buffer= (char *)&str_data; + bind[0].buffer_length= 50; + bind[0].length= &length; + length= 4; + bind[0].is_null= (char*)&is_null; + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt,rc); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(1 == my_process_stmt_result(stmt)); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1,t2,t3"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); +} + + +static void test_view_where() +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "select v1.c,v2.c from v1, v2"; + + myheader("test_view_where"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1,v2"); + myquery(rc); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,v2,t1"); + myquery(rc); + rc= mysql_query(mysql,"CREATE TABLE t1 (a int, b int)"); + myquery(rc); + rc= mysql_query(mysql,"insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)"); + myquery(rc); + rc= mysql_query(mysql,"create view v1 (c) as select b from t1 where a<3"); + myquery(rc); + rc= mysql_query(mysql,"create view v2 (c) as select b from t1 where a>=3"); + myquery(rc); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(4 == my_process_stmt_result(stmt)); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW v1, v2"); + myquery(rc); +} + + +static void test_view_2where() +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND bind[8]; + char parms[8][100]; + long length[8]; + const char *query= "SELECT `RELID` ,`REPORT` ,`HANDLE` ,`LOG_GROUP` ,`USERNAME` ,`VARIANT` ,`TYPE` ,`VERSION` ,`ERFDAT` ,`ERFTIME` ,`ERFNAME` ,`AEDAT` ,`AETIME` ,`AENAME` ,`DEPENDVARS` ,`INACTIVE` FROM `V_LTDX` WHERE `MANDT` = ? AND `RELID` = ? AND `REPORT` = ? AND `HANDLE` = ? AND `LOG_GROUP` = ? AND `USERNAME` IN ( ? , ? ) AND `TYPE` = ?"; + + myheader("test_view_2where"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS LTDX"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS V_LTDX"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE `LTDX` ( `MANDT` char(3) character set latin1 collate latin1_bin NOT NULL default '000', `RELID` char(2) character set latin1 collate latin1_bin NOT NULL default '', `REPORT` varchar(40) character set latin1 collate latin1_bin NOT NULL default '', `HANDLE` varchar(4) character set latin1 collate latin1_bin NOT NULL default '', `LOG_GROUP` varchar(4) character set latin1 collate latin1_bin NOT NULL default '', `USERNAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `VARIANT` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `TYPE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `SRTF2` int(11) NOT NULL default '0', `VERSION` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `ERFDAT` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `ERFTIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `ERFNAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `AEDAT` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `AETIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `AENAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `DEPENDVARS` varchar(10) character set latin1 collate latin1_bin NOT NULL default '', `INACTIVE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `CLUSTR` smallint(6) NOT NULL default '0', `CLUSTD` blob, PRIMARY KEY (`MANDT`,`RELID`,`REPORT`,`HANDLE`,`LOG_GROUP`,`USERNAME`,`VARIANT`,`TYPE`,`SRTF2`)) ENGINE=InnoDB DEFAULT CHARSET=latin1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE VIEW V_LTDX AS select T0001.MANDT AS `MANDT`,T0001.RELID AS `RELID`,T0001.REPORT AS `REPORT`,T0001.HANDLE AS `HANDLE`,T0001.LOG_GROUP AS `LOG_GROUP`,T0001.USERNAME AS `USERNAME`,T0001.VARIANT AS `VARIANT`,T0001.TYPE AS `TYPE`,T0001.VERSION AS `VERSION`,T0001.ERFDAT AS `ERFDAT`,T0001.ERFTIME AS `ERFTIME`,T0001.ERFNAME AS `ERFNAME`,T0001.AEDAT AS `AEDAT`,T0001.AETIME AS `AETIME`,T0001.AENAME AS `AENAME`,T0001.DEPENDVARS AS `DEPENDVARS`,T0001.INACTIVE AS `INACTIVE` from LTDX T0001 where (T0001.SRTF2 = 0)"); + myquery(rc); + for (i=0; i < 8; i++) { + strcpy(parms[i], "1"); + bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; + bind[i].buffer = (char *)&parms[i]; + bind[i].buffer_length = 100; + bind[i].is_null = 0; + bind[i].length = &length[i]; + length[i] = 1; + } + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt,rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(0 == my_process_stmt_result(stmt)); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW V_LTDX"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE LTDX"); + myquery(rc); +} + + +static void test_view_star() +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND bind[8]; + char parms[8][100]; + long length[8]; + const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)"; + + myheader("test_view_star"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, vt1"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, vt1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + myquery(rc); + rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1"); + myquery(rc); + for (i= 0; i < 2; i++) { + sprintf((char *)&parms[i], "%d", i); + bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; + bind[i].buffer = (char *)&parms[i]; + bind[i].buffer_length = 100; + bind[i].is_null = 0; + bind[i].length = &length[i]; + length[i] = 1; + } + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt,rc); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(0 == my_process_stmt_result(stmt)); + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW vt1"); + myquery(rc); +} + + +static void test_view_insert() +{ + MYSQL_STMT *insert_stmt, *select_stmt; + int rc, i; + MYSQL_BIND bind[1]; + long my_val = 0L; + ulong my_length = 0L; + long my_null = 0L; + const char *query= + "insert into v1 values (?)"; + + myheader("test_view_insert"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1"); + myquery(rc); + rc = mysql_query(mysql, "DROP VIEW IF EXISTS t1,v1"); + myquery(rc); + + rc= mysql_query(mysql,"create table t1 (a int, primary key (a))"); + myquery(rc); + + rc= mysql_query(mysql, "create view v1 as select a from t1 where a>=1"); + myquery(rc); + + insert_stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(insert_stmt, query, strlen(query)); + check_execute(insert_stmt, rc); + query= "select * from t1"; + select_stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(select_stmt, query, strlen(query)); + check_execute(select_stmt, rc); + + bind[0].buffer_type = FIELD_TYPE_LONG; + bind[0].buffer = (char *)&my_val; + bind[0].length = &my_length; + bind[0].is_null = (char*)&my_null; + rc= mysql_stmt_bind_param(insert_stmt, bind); + check_execute(insert_stmt, rc); + + for (i= 0; i < 3; i++) + { + my_val= i; + + rc= mysql_stmt_execute(insert_stmt); + check_execute(insert_stmt, rc); + + rc= mysql_stmt_execute(select_stmt); + check_execute(select_stmt, rc); + assert(i + 1 == (int) my_process_stmt_result(select_stmt)); + } + mysql_stmt_close(insert_stmt); + mysql_stmt_close(select_stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); +} + + +static void test_left_join_view() +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);"; + + myheader("test_left_join_view"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1"); + myquery(rc); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1"); + myquery(rc); + rc= mysql_query(mysql,"CREATE TABLE t1 (a int)"); + myquery(rc); + rc= mysql_query(mysql,"insert into t1 values (1), (2), (3)"); + myquery(rc); + rc= mysql_query(mysql,"create view v1 (x) as select a from t1 where a > 1"); + myquery(rc); + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(3 == my_process_stmt_result(stmt)); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); +} + + +static void test_view_insert_fields() +{ + MYSQL_STMT *stmt; + char parm[11][1000]; + long l[11]; + int rc, i; + MYSQL_BIND bind[11]; + const char *query= "INSERT INTO `v1` ( `K1C4` ,`K2C4` ,`K3C4` ,`K4N4` ,`F1C4` ,`F2I4` ,`F3N5` ,`F7F8` ,`F6N4` ,`F5C8` ,`F9D8` ) VALUES( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )"; + + myheader("test_view_insert_fields"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, v1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 ( K1C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K3C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', F1C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', F2I4 int(11) NOT NULL default '0', F3N5 varchar(5) character set latin1 collate latin1_bin NOT NULL default '00000', F4I4 int(11) NOT NULL default '0', F5C8 varchar(8) character set latin1 collate latin1_bin NOT NULL default '', F6N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', F7F8 double NOT NULL default '0', F8F8 double NOT NULL default '0', F9D8 decimal(8,2) NOT NULL default '0.00', PRIMARY KEY (K1C4,K2C4,K3C4,K4N4)) ENGINE=InnoDB DEFAULT CHARSET=latin1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE VIEW v1 AS select sql_no_cache K1C4 AS `K1C4`,K2C4 AS `K2C4`,K3C4 AS `K3C4`,K4N4 AS `K4N4`,F1C4 AS `F1C4`,F2I4 AS `F2I4`,F3N5 AS `F3N5`,F7F8 AS `F7F8`,F6N4 AS `F6N4`,F5C8 AS `F5C8`,F9D8 AS `F9D8` from t1 T0001"); + + for (i= 0; i < 11; i++) + { + l[i]= 20; + bind[i].buffer_type= MYSQL_TYPE_STRING; + bind[i].is_null= 0; + bind[i].buffer= (char *)&parm[i]; + + strcpy(parm[i], "1"); + bind[i].buffer_length= 2; + bind[i].length= &l[i]; + } + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + mysql_stmt_close(stmt); + + query= "select * from t1"; + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + assert(1 == my_process_stmt_result(stmt)); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + +} /* Read and parse arguments and MySQL options from my.cnf @@ -10335,6 +10687,13 @@ int main(int argc, char **argv) test_bug4236(); /* init -> execute */ test_bug4030(); /* test conversion string -> time types in libmysql */ + test_view(); /* Test of VIEWS with prepared statements */ + test_view_where(); /* VIEW with WHERE clause & merge algorithm */ + test_view_2where(); /* VIEW with WHERE * SELECt with WHERE */ + test_view_star(); /* using query with * from VIEW */ + test_view_insert(); /* inserting in VIEW without field list */ + test_left_join_view(); /* left join on VIEW with WHERE condition */ + test_view_insert_fields(); /* insert into VIOEW with fields list */ /* XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. |