summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <bell@sanja.is.com.ua>2004-07-16 01:15:55 +0300
committerunknown <bell@sanja.is.com.ua>2004-07-16 01:15:55 +0300
commit8790b1e65c3e1b2ae2ad9aedfe064c20d48bd096 (patch)
tree7a8e815ca26d9138de3bc77a619aa8103e037911
parentb0df20349d5e6ed2fc1527c3bd1db66a7d4ff311 (diff)
downloadmariadb-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
-rw-r--r--include/mysqld_error.h13
-rw-r--r--libmysqld/Makefile.am2
-rw-r--r--mysql-test/r/connect.result108
-rw-r--r--mysql-test/r/ctype_recoding.result20
-rw-r--r--mysql-test/r/drop.result4
-rw-r--r--mysql-test/r/grant.result6
-rw-r--r--mysql-test/r/lowercase_table.result8
-rw-r--r--mysql-test/r/ps_1general.result4
-rw-r--r--mysql-test/r/rename.result20
-rw-r--r--mysql-test/r/rpl000009.result18
-rw-r--r--mysql-test/r/rpl_error_ignored_table.result2
-rw-r--r--mysql-test/r/select.result14
-rw-r--r--mysql-test/r/system_mysql_db.result40
-rw-r--r--mysql-test/r/view.result943
-rw-r--r--mysql-test/t/system_mysql_db_fix.test11
-rw-r--r--mysql-test/t/view.test853
-rw-r--r--scripts/mysql_fix_privilege_tables.sql13
-rw-r--r--sql/Makefile.am3
-rw-r--r--sql/ha_myisammrg.cc4
-rw-r--r--sql/item.cc124
-rw-r--r--sql/item.h13
-rw-r--r--sql/item_subselect.cc19
-rw-r--r--sql/item_subselect.h3
-rw-r--r--sql/lex.h5
-rw-r--r--sql/lock.cc8
-rw-r--r--sql/mysql_priv.h37
-rw-r--r--sql/mysqld.cc8
-rw-r--r--sql/opt_sum.cc4
-rw-r--r--sql/set_var.cc7
-rw-r--r--sql/share/czech/errmsg.txt11
-rw-r--r--sql/share/danish/errmsg.txt11
-rw-r--r--sql/share/dutch/errmsg.txt11
-rw-r--r--sql/share/english/errmsg.txt11
-rw-r--r--sql/share/estonian/errmsg.txt11
-rw-r--r--sql/share/french/errmsg.txt11
-rw-r--r--sql/share/german/errmsg.txt11
-rw-r--r--sql/share/greek/errmsg.txt11
-rw-r--r--sql/share/hungarian/errmsg.txt11
-rw-r--r--sql/share/italian/errmsg.txt11
-rw-r--r--sql/share/japanese/errmsg.txt11
-rw-r--r--sql/share/korean/errmsg.txt11
-rw-r--r--sql/share/norwegian-ny/errmsg.txt11
-rw-r--r--sql/share/norwegian/errmsg.txt11
-rw-r--r--sql/share/polish/errmsg.txt11
-rw-r--r--sql/share/portuguese/errmsg.txt11
-rw-r--r--sql/share/romanian/errmsg.txt11
-rw-r--r--sql/share/russian/errmsg.txt11
-rw-r--r--sql/share/serbian/errmsg.txt11
-rw-r--r--sql/share/slovak/errmsg.txt11
-rw-r--r--sql/share/spanish/errmsg.txt11
-rw-r--r--sql/share/swedish/errmsg.txt11
-rw-r--r--sql/share/ukrainian/errmsg.txt11
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/sp.cc11
-rw-r--r--sql/sql_acl.cc228
-rw-r--r--sql/sql_acl.h40
-rw-r--r--sql/sql_base.cc610
-rw-r--r--sql/sql_cache.cc14
-rw-r--r--sql/sql_class.h33
-rw-r--r--sql/sql_db.cc2
-rw-r--r--sql/sql_delete.cc118
-rw-r--r--sql/sql_derived.cc22
-rw-r--r--sql/sql_handler.cc4
-rw-r--r--sql/sql_help.cc18
-rw-r--r--sql/sql_insert.cc214
-rw-r--r--sql/sql_lex.cc372
-rw-r--r--sql/sql_lex.h59
-rw-r--r--sql/sql_load.cc14
-rw-r--r--sql/sql_olap.cc3
-rw-r--r--sql/sql_parse.cc577
-rw-r--r--sql/sql_prepare.cc102
-rw-r--r--sql/sql_rename.cc18
-rw-r--r--sql/sql_select.cc98
-rw-r--r--sql/sql_show.cc111
-rw-r--r--sql/sql_table.cc54
-rw-r--r--sql/sql_union.cc15
-rw-r--r--sql/sql_update.cc210
-rw-r--r--sql/sql_view.cc902
-rw-r--r--sql/sql_view.h42
-rw-r--r--sql/sql_yacc.yy141
-rw-r--r--sql/table.cc236
-rw-r--r--sql/table.h103
-rw-r--r--sql/tztime.cc6
-rw-r--r--sql/unireg.h1
-rw-r--r--tests/client_test.c363
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,
- &not_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), &not_used_item, 0, 0, 0,
&not_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,
&not_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,
- &not_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> &not_used_values,
*/
update.empty();
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (tables_to_update & table->map)
@@ -642,7 +696,7 @@ int multi_update::prepare(List<Item> &not_used_values,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
- update.link_in_list((byte*) tl, (byte**) &tl->next);
+ update.link_in_list((byte*) tl, (byte**) &tl->next_local);
tl->shared= table_count++;
table->no_keyread=1;
table->used_keys.clear_all();
@@ -702,7 +756,7 @@ int multi_update::prepare(List<Item> &not_used_values,
which will cause an error when reading a row.
(This issue is mostly relevent for MyISAM tables)
*/
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (!(tables_to_update & table->map) &&
@@ -737,7 +791,7 @@ multi_update::initialize_tables(JOIN *join)
table_to_update= 0;
/* Create a temporary table for keys to all tables, except main table */
- for (table_ref= update_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
uint cnt= table_ref->shared;
@@ -849,7 +903,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
multi_update::~multi_update()
{
TABLE_LIST *table;
- for (table= update_tables ; table; table= table->next)
+ for (table= update_tables ; table; table= table->next_local)
table->table->no_keyread= table->table->no_cache= 0;
if (tmp_tables)
@@ -876,7 +930,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
TABLE *table= cur_table->table;
/*
@@ -990,7 +1044,7 @@ int multi_update::do_updates(bool from_send_error)
do_update= 0; // Don't retry this function
if (!found)
DBUG_RETURN(0);
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
byte *ref_pos;
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.