diff options
36 files changed, 13311 insertions, 136 deletions
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index cf4f90d99c9..463cd187c2b 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -72,7 +72,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ event_queue.cc event_db_repository.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ sql_tablespace.cc \ - rpl_injector.cc my_user.c partition_info.cc + rpl_injector.cc my_user.c partition_info.cc \ + sql_servers.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index fcef17eab9b..c50b25ccdae 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -114,6 +114,20 @@ REPLACE INTO user VALUES ('127.0.0.1' ,'root','','Y','Y','Y','Y','Y','Y','Y','Y INSERT INTO user (host,user) VALUES ('localhost',''); INSERT INTO user (host,user) VALUES ('@HOSTNAME@%',''); +CREATE TABLE servers ( + Server_name char(64) NOT NULL DEFAULT '', + Host char(64) NOT NULL DEFAULT '', + Db char(64) NOT NULL DEFAULT '', + Username char(64) NOT NULL DEFAULT '', + Password char(64) NOT NULL DEFAULT '', + Port INT(4) NOT NULL DEFAULT '0', + Socket char(64) NOT NULL DEFAULT '', + Wrapper char(64) NOT NULL DEFAULT '', + Owner char(64) NOT NULL DEFAULT '', + PRIMARY KEY (Server_name)) + comment='MySQL Foreign Servers table'; + +INSERT INTO servers VALUES ('test','localhost','test','root','', 0,'','mysql','root'); CREATE TABLE func ( name char(64) binary DEFAULT '' NOT NULL, diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result index 274a23cc48c..60190629dd0 100644 --- a/mysql-test/r/1st.result +++ b/mysql-test/r/1st.result @@ -19,6 +19,7 @@ ndb_binlog_index plugin proc procs_priv +servers slow_log tables_priv time_zone diff --git a/mysql-test/r/backup.result b/mysql-test/r/backup.result index b299940e332..be1a3efc5c6 100644 --- a/mysql-test/r/backup.result +++ b/mysql-test/r/backup.result @@ -6,26 +6,26 @@ Table Op Msg_type Msg_text test.t4 backup error Failed copying .frm file (errno: X) test.t4 backup status Operation failed Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead Error 1 Can't create/write to file 'MYSQLTEST_VARDIR/bogus/t4.frm' (Errcode: X) backup table t4 to '../tmp'; Table Op Msg_type Msg_text test.t4 backup status OK Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead backup table t4 to '../tmp'; Table Op Msg_type Msg_text test.t4 backup error Failed copying .frm file (errno: X) test.t4 backup status Operation failed Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead Error 1 Can't create/write to file 'MYSQLTEST_VARDIR/tmp/t4.frm' (Errcode: X) drop table t4; restore table t4 from '../tmp'; Table Op Msg_type Msg_text test.t4 restore status OK Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead select count(*) from t4; count(*) 0 @@ -35,19 +35,19 @@ backup table t1 to '../tmp'; Table Op Msg_type Msg_text test.t1 backup status OK Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead drop table t1; restore table t1 from '../bogus'; Table Op Msg_type Msg_text t1 restore error Failed copying .frm file Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead Error 29 File 'MYSQLTEST_VARDIR/bogus/t1.frm' not found (Errcode: X) restore table t1 from '../tmp'; Table Op Msg_type Msg_text test.t1 restore status OK Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead select n from t1; n 23 @@ -62,7 +62,7 @@ Table Op Msg_type Msg_text test.t2 backup status OK test.t3 backup status OK Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead drop table t1,t2,t3; restore table t1,t2,t3 from '../tmp'; Table Op Msg_type Msg_text @@ -70,7 +70,7 @@ test.t1 restore status OK test.t2 restore status OK test.t3 restore status OK Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead select n from t1; n 23 @@ -91,7 +91,7 @@ restore table t1 from '../tmp'; Table Op Msg_type Msg_text test.t1 restore status OK Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead rename table t1 to t5; lock tables t5 write; backup table t5 to '../tmp'; @@ -99,7 +99,7 @@ unlock tables; Table Op Msg_type Msg_text test.t5 backup status OK Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead drop table t5; DROP TABLE IF EXISTS `t+1`; CREATE TABLE `t+1` (c1 INT); @@ -108,13 +108,13 @@ BACKUP TABLE `t+1` TO '../tmp'; Table Op Msg_type Msg_text test.t+1 backup status OK Warnings: -Warning 1541 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'BACKUP TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead DROP TABLE `t+1`; RESTORE TABLE `t+1` FROM '../tmp'; Table Op Msg_type Msg_text test.t+1 restore status OK Warnings: -Warning 1541 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead +Warning 1543 The syntax 'RESTORE TABLE' is deprecated and will be removed in MySQL 5.2. Please use MySQL Administrator (mysqldump, mysql) instead SELECT * FROM `t+1`; c1 1 diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index a222d11cb50..25cf4f90e6d 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -15,6 +15,7 @@ ndb_binlog_index plugin proc procs_priv +servers slow_log tables_priv time_zone @@ -47,6 +48,7 @@ ndb_binlog_index plugin proc procs_priv +servers slow_log tables_priv time_zone @@ -87,6 +89,7 @@ ndb_binlog_index plugin proc procs_priv +servers slow_log tables_priv time_zone diff --git a/mysql-test/r/federated_server.result b/mysql-test/r/federated_server.result new file mode 100644 index 00000000000..a8dc9a5a949 --- /dev/null +++ b/mysql-test/r/federated_server.result @@ -0,0 +1,112 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +stop slave; +DROP DATABASE IF EXISTS federated; +CREATE DATABASE federated; +DROP DATABASE IF EXISTS federated; +CREATE DATABASE federated; +create database first_db; +create database second_db; +use first_db; +DROP TABLE IF EXISTS first_db.t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE first_db.t1 ( +`id` int(20) NOT NULL, +`name` varchar(64) NOT NULL default '' + ) +DEFAULT CHARSET=latin1; +use second_db; +DROP TABLE IF EXISTS second_db.t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE second_db.t1 ( +`id` int(20) NOT NULL, +`name` varchar(64) NOT NULL default '' + ) +DEFAULT CHARSET=latin1; +drop server if exists 'server_one'; +create server 'server_one' foreign data wrapper 'mysql' options +(HOST '127.0.0.1', +DATABASE 'first_db', +USER 'root', +PASSWORD '', +PORT 9308, +SOCKET '', +OWNER 'root'); +drop server if exists 'server_two'; +create server 'server_two' foreign data wrapper 'mysql' options +(HOST '127.0.0.1', +DATABASE 'second_db', +USER 'root', +PASSWORD '', +PORT 9308, +SOCKET '', +OWNER 'root'); +select * from mysql.servers; +Server_name Host Db Username Password Port Socket Wrapper Owner +test localhost test root 0 mysql root +server_one 127.0.0.1 first_db root 9308 mysql root +server_two 127.0.0.1 second_db root 9308 mysql root +DROP TABLE IF EXISTS federated.old; +Warnings: +Note 1051 Unknown table 'old' +CREATE TABLE federated.old ( +`id` int(20) NOT NULL, +`name` varchar(64) NOT NULL default '' + ) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +CONNECTION='mysql://root@127.0.0.1:9308/first_db/t1'; +INSERT INTO federated.old (id, name) values (1, 'federated.old url'); +SELECT * FROM federated.old; +id name +1 federated.old url +DROP TABLE IF EXISTS federated.old2; +Warnings: +Note 1051 Unknown table 'old2' +CREATE TABLE federated.old2 ( +`id` int(20) NOT NULL, +`name` varchar(64) NOT NULL default '' + ) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +CONNECTION='mysql://root@127.0.0.1:9308/second_db/t1'; +INSERT INTO federated.old2 (id, name) values (1, 'federated.old2 url'); +DROP TABLE IF EXISTS federated.t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE federated.t1 ( +`id` int(20) NOT NULL, +`name` varchar(64) NOT NULL default '' + ) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +CONNECTION='server_one'; +INSERT INTO federated.t1 (id, name) values (1, 'server_one, new scheme'); +SELECT * FROM federated.t1; +id name +1 federated.old url +1 server_one, new scheme +ALTER SERVER 'server_one' options(DATABASE 'second_db'); +flush tables; +INSERT INTO federated.t1 (id, name) values (1, 'server_two, new scheme'); +SELECT * FROM federated.t1; +id name +1 federated.old2 url +1 server_two, new scheme +drop table federated.t1; +drop server 'server_one'; +drop server 'server_two'; +select * from mysql.servers; +Server_name Host Db Username Password Port Socket Wrapper Owner +test localhost test root 0 mysql root +drop table first_db.t1; +drop table second_db.t1; +drop database first_db; +drop database second_db; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index ac51bfe2a36..e8599241cc0 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -76,6 +76,7 @@ host plugin proc procs_priv +servers slow_log tables_priv time_zone @@ -852,7 +853,7 @@ flush privileges; SELECT table_schema, count(*) FROM information_schema.TABLES where table_name<>'ndb_binlog_index' AND table_name<>'ndb_apply_status' GROUP BY TABLE_SCHEMA; table_schema count(*) information_schema 27 -mysql 21 +mysql 22 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row begin diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result index 3bba1944c52..a36bb5aec15 100644 --- a/mysql-test/r/mysql.result +++ b/mysql-test/r/mysql.result @@ -172,8 +172,8 @@ ERROR 1049 (42000) at line 1: Unknown database 'invalid' ERROR 1049 (42000) at line 1: Unknown database 'invalid' Test connect with dbname + hostname Test connect with dbname + _invalid_ hostname -ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'invalid_hostname' (errno) -ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'invalid_hostname' (errno) +ERROR 2003 (HY000) at line 1: Can't connect to MySQL server on 'invalid_hostname' (errno) +ERROR 2003 (HY000) at line 1: Can't connect to MySQL server on 'invalid_hostname' (errno) The commands reported in the bug report ERROR 2005 (HY000) at line 1: Unknown MySQL server host 'cyril has found a bug :)XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' (errno) Too long dbname diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index 37d0f6ebeeb..c664932f2bb 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -17,6 +17,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.servers OK mysql.slow_log note : The storage engine for the table doesn't support optimize mysql.tables_priv OK @@ -41,6 +42,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.servers OK mysql.slow_log note : The storage engine for the table doesn't support optimize mysql.tables_priv OK diff --git a/mysql-test/r/ndb_dd_basic.result b/mysql-test/r/ndb_dd_basic.result index 83cb12ce964..724b42b6db3 100644 --- a/mysql-test/r/ndb_dd_basic.result +++ b/mysql-test/r/ndb_dd_basic.result @@ -5,20 +5,20 @@ INITIAL_SIZE 16M UNDO_BUFFER_SIZE = 1M ENGINE=MYISAM; Warnings: -Error 1465 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' +Error 1466 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' ALTER LOGFILE GROUP lg1 ADD UNDOFILE 'undofile02.dat' INITIAL_SIZE = 4M ENGINE=XYZ; Warnings: Error 1286 Unknown table engine 'XYZ' -Error 1465 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' +Error 1466 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' CREATE TABLESPACE ts1 ADD DATAFILE 'datafile.dat' USE LOGFILE GROUP lg1 INITIAL_SIZE 12M; Warnings: -Error 1465 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' +Error 1466 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' set storage_engine=ndb; CREATE LOGFILE GROUP lg1 ADD UNDOFILE 'undofile.dat' diff --git a/mysql-test/r/ndb_dd_ddl.result b/mysql-test/r/ndb_dd_ddl.result index eea80090768..aab44e261b2 100644 --- a/mysql-test/r/ndb_dd_ddl.result +++ b/mysql-test/r/ndb_dd_ddl.result @@ -16,7 +16,7 @@ ERROR HY000: Failed to create LOGFILE GROUP SHOW WARNINGS; Level Code Message Error 1296 Got error 1514 'Currently there is a limit of one logfile group' from NDB -Error 1515 Failed to create LOGFILE GROUP +Error 1516 Failed to create LOGFILE GROUP CREATE LOGFILE GROUP lg1 ADD UNDOFILE 'undofile.dat' INITIAL_SIZE 1M diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index f24b109d75a..15937a4a6f5 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3475,7 +3475,7 @@ SELECT 0.9888889889 * 1.011111411911; 0.9998769417899202067879 prepare stmt from 'select 1 as " a "'; Warnings: -Warning 1546 Leading spaces are removed from name ' a ' +Warning 1548 Leading spaces are removed from name ' a ' execute stmt; a 1 diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 73450c9a773..516355839b5 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -712,7 +712,7 @@ drop database mysqltest; show full plugin; show warnings; Level Code Message -Warning 1541 The syntax 'SHOW PLUGIN' is deprecated and will be removed in MySQL 5.2. Please use 'SHOW PLUGINS' instead +Warning 1543 The syntax 'SHOW PLUGIN' is deprecated and will be removed in MySQL 5.2. Please use 'SHOW PLUGINS' instead show plugin; show plugins; create database `mysqlttest\1`; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 6859de2ed63..7c5782707a4 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -5633,7 +5633,7 @@ drop function if exists pi; create function pi() returns varchar(50) return "pie, my favorite desert."; Warnings: -Note 1579 This function 'pi' has the same name as a native function. +Note 1580 This function 'pi' has the same name as a native function. SET @save_sql_mode=@@sql_mode; SET SQL_MODE='IGNORE_SPACE'; select pi(), pi (); @@ -5682,15 +5682,15 @@ use test; create function `database`() returns varchar(50) return "Stored function database"; Warnings: -Note 1579 This function 'database' has the same name as a native function. +Note 1580 This function 'database' has the same name as a native function. create function `current_user`() returns varchar(50) return "Stored function current_user"; Warnings: -Note 1579 This function 'current_user' has the same name as a native function. +Note 1580 This function 'current_user' has the same name as a native function. create function md5(x varchar(50)) returns varchar(50) return "Stored function md5"; Warnings: -Note 1579 This function 'md5' has the same name as a native function. +Note 1580 This function 'md5' has the same name as a native function. SET SQL_MODE='IGNORE_SPACE'; select database(), database (); database() database () diff --git a/mysql-test/r/sp_gis.result b/mysql-test/r/sp_gis.result index 0eebd1b9342..9f1d9a58012 100644 --- a/mysql-test/r/sp_gis.result +++ b/mysql-test/r/sp_gis.result @@ -7,11 +7,11 @@ return 1; create function x() returns int return 2; Warnings: -Note 1579 This function 'x' has the same name as a native function. +Note 1580 This function 'x' has the same name as a native function. create function y() returns int return 3; Warnings: -Note 1579 This function 'y' has the same name as a native function. +Note 1580 This function 'y' has the same name as a native function. select a(); a() 1 diff --git a/mysql-test/r/sp_trans.result b/mysql-test/r/sp_trans.result index bf4c478677b..a0d687e565b 100644 --- a/mysql-test/r/sp_trans.result +++ b/mysql-test/r/sp_trans.result @@ -535,7 +535,7 @@ use db_bug7787| CREATE PROCEDURE p1() SHOW INNODB STATUS; | Warnings: -Warning 1541 The syntax 'SHOW INNODB STATUS' is deprecated and will be removed in MySQL 5.2. Please use 'SHOW ENGINE INNODB STATUS' instead +Warning 1543 The syntax 'SHOW INNODB STATUS' is deprecated and will be removed in MySQL 5.2. Please use 'SHOW ENGINE INNODB STATUS' instead GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost| DROP DATABASE db_bug7787| drop user user_bug7787@localhost| diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 6bbd0adfc49..424e9e9c12d 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -15,6 +15,7 @@ ndb_binlog_index plugin proc procs_priv +servers slow_log tables_priv time_zone diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result index 281a5f3fab6..c075a199dae 100644 --- a/mysql-test/r/type_timestamp.result +++ b/mysql-test/r/type_timestamp.result @@ -101,13 +101,13 @@ create table t1 (t2 timestamp(2), t4 timestamp(4), t6 timestamp(6), t8 timestamp(8), t10 timestamp(10), t12 timestamp(12), t14 timestamp(14)); Warnings: -Warning 1541 The syntax 'TIMESTAMP(2)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(4)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(6)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(8)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(10)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(12)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead -Warning 1541 The syntax 'TIMESTAMP(14)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(2)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(4)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(6)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(8)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(10)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(12)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead +Warning 1543 The syntax 'TIMESTAMP(14)' is deprecated and will be removed in MySQL 5.2. Please use 'TIMESTAMP' instead insert t1 values (0,0,0,0,0,0,0), ("1997-12-31 23:47:59", "1997-12-31 23:47:59", "1997-12-31 23:47:59", "1997-12-31 23:47:59", "1997-12-31 23:47:59", "1997-12-31 23:47:59", diff --git a/mysql-test/r/warnings.result b/mysql-test/r/warnings.result index d03c5ed2f54..21c553951f6 100644 --- a/mysql-test/r/warnings.result +++ b/mysql-test/r/warnings.result @@ -175,7 +175,7 @@ Warning 1266 Using storage engine MyISAM for table 't1' drop table t1; set table_type=MYISAM; Warnings: -Warning 1541 The syntax 'table_type' is deprecated and will be removed in MySQL 5.2. Please use 'storage_engine' instead +Warning 1543 The syntax 'table_type' is deprecated and will be removed in MySQL 5.2. Please use 'storage_engine' instead create table t1 (a int); insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); update t1 set a='abc'; diff --git a/mysql-test/r/xml.result b/mysql-test/r/xml.result index bb7f84d0287..08f8293ff52 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -640,32 +640,32 @@ select extractValue('<a>a','/a'); extractValue('<a>a','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 5: unexpected END-OF-INPUT' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 5: unexpected END-OF-INPUT' select extractValue('<a>a<','/a'); extractValue('<a>a<','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 6: END-OF-INPUT unexpected (ident or '/' wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 6: END-OF-INPUT unexpected (ident or '/' wanted)' select extractValue('<a>a</','/a'); extractValue('<a>a</','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 7: END-OF-INPUT unexpected (ident wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 7: END-OF-INPUT unexpected (ident wanted)' select extractValue('<a>a</a','/a'); extractValue('<a>a</a','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 8: END-OF-INPUT unexpected ('>' wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 8: END-OF-INPUT unexpected ('>' wanted)' select extractValue('<a>a</a></b>','/a'); extractValue('<a>a</a></b>','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 12: '</b>' unexpected (END-OF-INPUT wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 12: '</b>' unexpected (END-OF-INPUT wanted)' select extractValue('<a b=>a</a>','/a'); extractValue('<a b=>a</a>','/a') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 7: '>' unexpected (ident or string wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 7: '>' unexpected (ident or string wanted)' select extractValue('<e>1</e>','position()'); ERROR HY000: XPATH syntax error: '' select extractValue('<e>1</e>','last()'); @@ -716,17 +716,17 @@ select extractValue('<zot><tim0><01>10:39:15</01><02>140</02></tim0></zot>','//* extractValue('<zot><tim0><01>10:39:15</01><02>140</02></tim0></zot>','//*') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 13: unknown token unexpected (ident or '/' wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 13: unknown token unexpected (ident or '/' wanted)' select extractValue('<.>test</.>','//*'); extractValue('<.>test</.>','//*') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)' select extractValue('<->test</->','//*'); extractValue('<->test</->','//*') NULL Warnings: -Warning 1512 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)' +Warning 1513 Incorrect XML value: 'parse error at line 1 pos 2: unknown token unexpected (ident or '/' wanted)' select extractValue('<:>test</:>','//*'); extractValue('<:>test</:>','//*') test diff --git a/mysql-test/t/federated_server.test b/mysql-test/t/federated_server.test new file mode 100644 index 00000000000..bfe732c309d --- /dev/null +++ b/mysql-test/t/federated_server.test @@ -0,0 +1,103 @@ +# should work with embedded server after mysqltest is fixed +-- source include/not_embedded.inc +source include/federated.inc; + +connection slave; +create database first_db; +create database second_db; + +use first_db; + +DROP TABLE IF EXISTS first_db.t1; +CREATE TABLE first_db.t1 ( + `id` int(20) NOT NULL, + `name` varchar(64) NOT NULL default '' + ) + DEFAULT CHARSET=latin1; + +use second_db; +DROP TABLE IF EXISTS second_db.t1; +CREATE TABLE second_db.t1 ( + `id` int(20) NOT NULL, + `name` varchar(64) NOT NULL default '' + ) + DEFAULT CHARSET=latin1; + +connection master; + +drop server if exists 'server_one'; +eval create server 'server_one' foreign data wrapper 'mysql' options + (HOST '127.0.0.1', + DATABASE 'first_db', + USER 'root', + PASSWORD '', + PORT $SLAVE_MYPORT, + SOCKET '', + OWNER 'root'); + +drop server if exists 'server_two'; +eval create server 'server_two' foreign data wrapper 'mysql' options + (HOST '127.0.0.1', + DATABASE 'second_db', + USER 'root', + PASSWORD '', + PORT $SLAVE_MYPORT, + SOCKET '', + OWNER 'root'); + +select * from mysql.servers; + +DROP TABLE IF EXISTS federated.old; +eval CREATE TABLE federated.old ( + `id` int(20) NOT NULL, + `name` varchar(64) NOT NULL default '' + ) + ENGINE="FEDERATED" DEFAULT CHARSET=latin1 + CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/first_db/t1'; + +INSERT INTO federated.old (id, name) values (1, 'federated.old url'); + +SELECT * FROM federated.old; + +DROP TABLE IF EXISTS federated.old2; +eval CREATE TABLE federated.old2 ( + `id` int(20) NOT NULL, + `name` varchar(64) NOT NULL default '' + ) + ENGINE="FEDERATED" DEFAULT CHARSET=latin1 + CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/second_db/t1'; + +INSERT INTO federated.old2 (id, name) values (1, 'federated.old2 url'); + +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( + `id` int(20) NOT NULL, + `name` varchar(64) NOT NULL default '' + ) + ENGINE="FEDERATED" DEFAULT CHARSET=latin1 + CONNECTION='server_one'; + +INSERT INTO federated.t1 (id, name) values (1, 'server_one, new scheme'); + +SELECT * FROM federated.t1; + +ALTER SERVER 'server_one' options(DATABASE 'second_db'); + +flush tables; + +INSERT INTO federated.t1 (id, name) values (1, 'server_two, new scheme'); +SELECT * FROM federated.t1; + +drop table federated.t1; + +drop server 'server_one'; +drop server 'server_two'; +select * from mysql.servers; + +connection slave; +drop table first_db.t1; +drop table second_db.t1; +drop database first_db; +drop database second_db; + +source include/federated_cleanup.inc; diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 3b9d69e259b..1b4472f5baa 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -30,6 +30,7 @@ windows=$4 # Initialize variables c_d="" i_d="" +c_s="" i_s="" c_h="" i_h="" c_u="" i_u="" c_f="" i_f="" @@ -192,6 +193,33 @@ then fi fi +# Check for old tables +if test ! -f $mdata/servers.frm +then + if test "$1" = "verbose" ; then + echo "Preparing servers table" 1>&2; + fi + + c_s=" +CREATE TABLE servers (" + c_s="$c_s Server_name char(64) NOT NULL," + c_s="$c_s Host char(64) NOT NULL," + c_s="$c_s Db char(64) NOT NULL," + c_s="$c_s Username char(64) NOT NULL," + c_s="$c_s Password char(64) NOT NULL," + c_s="$c_s Port INT(4)," + c_s="$c_s Socket char(64)," + c_s="$c_s Wrapper char(64) NOT NULL," + c_s="$c_s Owner char(64) NOT NULL," + c_s="$c_s PRIMARY KEY (Server_name))" + c_s="$c_s comment='MySQL Foreign Servers table';" + + i_s="INSERT INTO servers VALUES + ('test','localhost','test','root','', 0, + '','mysql','root'); + " +fi + if test ! -f $mdata/func.frm then if test "$1" = "verbose" ; then @@ -849,6 +877,9 @@ $i_f $c_pl $i_pl +$c_s +$i_s + $c_t $c_c diff --git a/sql/Makefile.am b/sql/Makefile.am index 91fe875c73d..d6c703fec9b 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -68,7 +68,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ event_db_repository.h event_queue.h \ sql_plugin.h authors.h sql_partition.h event_data_objects.h \ partition_info.h partition_element.h event_scheduler.h \ - contributors.h + contributors.h sql_servers.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -106,7 +106,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ event_scheduler.cc event_data_objects.cc \ event_queue.cc event_db_repository.cc events.cc \ sql_plugin.cc sql_binlog.cc \ - sql_builtin.cc sql_tablespace.cc partition_info.cc + sql_builtin.cc sql_tablespace.cc partition_info.cc \ + sql_servers.cc gen_lex_hash_SOURCES = gen_lex_hash.cc diff --git a/sql/lex.h b/sql/lex.h index 254d7f10fb7..900e5481c76 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -229,6 +229,7 @@ static SYMBOL symbols[] = { { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY)}, + { "HOST", SYM(HOST_SYM)}, { "HOSTS", SYM(HOSTS_SYM)}, { "HOUR", SYM(HOUR_SYM)}, { "HOUR_MICROSECOND", SYM(HOUR_MICROSECOND_SYM)}, @@ -368,6 +369,7 @@ static SYMBOL symbols[] = { { "ONE_SHOT", SYM(ONE_SHOT_SYM)}, { "OPEN", SYM(OPEN_SYM)}, { "OPTIMIZE", SYM(OPTIMIZE)}, + { "OPTIONS", SYM(OPTIONS_SYM)}, { "OPTION", SYM(OPTION)}, { "OPTIONALLY", SYM(OPTIONALLY)}, { "OR", SYM(OR_SYM)}, @@ -375,6 +377,7 @@ static SYMBOL symbols[] = { { "OUT", SYM(OUT_SYM)}, { "OUTER", SYM(OUTER)}, { "OUTFILE", SYM(OUTFILE)}, + { "OWNER", SYM(OWNER_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, { "PARSER", SYM(PARSER_SYM)}, { "PARTIAL", SYM(PARTIAL)}, @@ -387,6 +390,7 @@ static SYMBOL symbols[] = { { "PLUGINS", SYM(PLUGINS_SYM)}, { "POINT", SYM(POINT_SYM)}, { "POLYGON", SYM(POLYGON)}, + { "PORT", SYM(PORT_SYM)}, { "PRECISION", SYM(PRECISION)}, { "PREPARE", SYM(PREPARE_SYM)}, { "PRESERVE", SYM(PRESERVE_SYM)}, @@ -456,6 +460,7 @@ static SYMBOL symbols[] = { { "SERIAL", SYM(SERIAL_SYM)}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM)}, { "SESSION", SYM(SESSION_SYM)}, + { "SERVER", SYM(SERVER_SYM)}, { "SET", SYM(SET)}, { "SHARE", SYM(SHARE_SYM)}, { "SHOW", SYM(SHOW)}, @@ -465,6 +470,7 @@ static SYMBOL symbols[] = { { "SLAVE", SYM(SLAVE)}, { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, + { "SOCKET", SYM(SOCKET_SYM)}, { "SOME", SYM(ANY_SYM)}, { "SONAME", SYM(SONAME_SYM)}, { "SOUNDS", SYM(SOUNDS_SYM)}, @@ -569,6 +575,7 @@ static SYMBOL symbols[] = { { "VIEW", SYM(VIEW_SYM)}, { "WITH", SYM(WITH)}, { "WORK", SYM(WORK_SYM)}, + { "WRAPPER", SYM(WRAPPER_SYM)}, { "WRITE", SYM(WRITE_SYM)}, { "X509", SYM(X509_SYM)}, { "XOR", SYM(XOR)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 812268fc92c..4c13cab4885 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -624,6 +624,7 @@ Item *negate_expression(THD *thd, Item *expr); #include "sql_acl.h" #include "tztime.h" #ifdef MYSQL_SERVER +#include "sql_servers.h" #include "opt_range.h" #ifdef HAVE_QUERY_CACHE diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 96dfb3d01ec..17beaad166e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1180,6 +1180,7 @@ void clean_up(bool print_message) my_tz_free(); my_database_names_free(); #ifndef NO_EMBEDDED_ACCESS_CHECKS + servers_free(1); acl_free(1); grant_free(); #endif @@ -3655,6 +3656,9 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (!opt_noacl) (void) grant_init(); + if (!opt_bootstrap) + servers_init(0); + if (!opt_noacl) { #ifdef HAVE_DLOPEN diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index c64f4da045e..1b9e1aa96ec 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5607,6 +5607,8 @@ ER_SP_RECURSION_LIMIT ER_SP_PROC_TABLE_CORRUPT eng "Failed to load routine %-.64s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" ger "Routine %-64s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)" +ER_FOREIGN_SERVER_EXISTS + eng "The foreign server, %s, you are trying to create already exists." ER_SP_WRONG_NAME 42000 eng "Incorrect routine name '%-.64s'" ger "Ungültiger Routinenname '%-.64s'" @@ -5847,6 +5849,9 @@ ER_BINLOG_ROW_WRONG_TABLE_DEF ER_BINLOG_ROW_RBR_TO_SBR eng "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events" ger "Slave, die mit --log-slave-updates laufen, müssen zeilenbasiertes Loggen verwenden, um zeilenbasierte Binärlog-Ereignisse loggen zu können" +ER_FOREIGN_SERVER_DOESNT_EXIST + eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s" + ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s" ER_EVENT_ALREADY_EXISTS eng "Event '%-.64s' already exists" ger "Event '%-.64s' existiert bereits" diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d156973a790..3a98ebdd4d1 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -183,6 +183,21 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->nest_level=0 ; lex->allow_sum_func= 0; lex->in_sum_func= NULL; + /* + ok, there must be a better solution for this, long-term + I tried "bzero" in the sql_yacc.yy code, but that for + some reason made the values zero, even if they were set + */ + lex->server_options.server_name= 0; + lex->server_options.server_name_length= 0; + lex->server_options.host= 0; + lex->server_options.db= 0; + lex->server_options.username= 0; + lex->server_options.password= 0; + lex->server_options.scheme= 0; + lex->server_options.socket= 0; + lex->server_options.owner= 0; + lex->server_options.port= -1; DBUG_VOID_RETURN; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7fd60cbfa58..86dbacd5d77 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -111,6 +111,7 @@ enum enum_sql_command { SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, SQLCOM_SHOW_PLUGINS, SQLCOM_SHOW_CONTRIBUTORS, + SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER, SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, @@ -174,6 +175,14 @@ enum enum_drop_mode typedef List<Item> List_item; +/* SERVERS CACHE CHANGES */ +typedef struct st_lex_server_options +{ + long port; + uint server_name_length; + char *server_name, *host, *db, *username, *password, *scheme, *socket, *owner; +} LEX_SERVER_OPTIONS; + typedef struct st_lex_master_info { char *host, *user, *password, *log_file_name; @@ -977,6 +986,7 @@ typedef struct st_lex : public Query_tables_list HA_CREATE_INFO create_info; KEY_CREATE_INFO key_create_info; LEX_MASTER_INFO mi; // used by CHANGE MASTER + LEX_SERVER_OPTIONS server_options; USER_RESOURCES mqh; ulong type; /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 82e2417fac0..caafdcc9893 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5189,6 +5189,58 @@ create_sp_error: #endif /* EMBEDDED_LIBRARY */ break; } + case SQLCOM_CREATE_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER")); + if ((error= create_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem creating server", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_ALTER_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER")); + if ((error= alter_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem altering server", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_DROP_SERVER: + { + int err_code; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER")); + if ((err_code= drop_server(thd, &lex->server_options))) + { + if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_EXISTS) + { + DBUG_PRINT("info", ("problem dropping server %s", + lex->server_options.server_name)); + my_error(err_code, MYF(0), lex->server_options.server_name); + } + else + { + send_ok(thd, 0); + } + break; + } + send_ok(thd, 1); + break; + } default: #ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc new file mode 100644 index 00000000000..beffdc3f945 --- /dev/null +++ b/sql/sql_servers.cc @@ -0,0 +1,1238 @@ +/* Copyright (C) 2000-2003 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 */ + + +/* + The servers are saved in the system table "servers" +*/ + +#include "mysql_priv.h" +#include "hash_filo.h" +#include <m_ctype.h> +#include <stdarg.h> +#include "sp_head.h" +#include "sp.h" + +HASH servers_cache; +pthread_mutex_t servers_cache_mutex; // To init the hash +uint servers_cache_initialised=FALSE; +/* Version of server table. incremented by servers_load */ +static uint servers_version=0; +static MEM_ROOT mem; +static rw_lock_t THR_LOCK_servers; +static bool initialized=0; + +static byte *servers_cache_get_key(FOREIGN_SERVER *server, uint *length, + my_bool not_used __attribute__((unused))) +{ + DBUG_ENTER("servers_cache_get_key"); + DBUG_PRINT("info", ("server_name_length %d server_name %s", + server->server_name_length, + server->server_name)); + + *length= (uint) server->server_name_length; + DBUG_RETURN((byte*) server->server_name); +} + +/* + Initialize structures responsible for servers used in federated + server scheme information for them from the server + table in the 'mysql' database. + + SYNOPSIS + servers_init() + dont_read_server_table TRUE if we want to skip loading data from + server table and disable privilege checking. + + NOTES + This function is mostly responsible for preparatory steps, main work + on initialization and grants loading is done in servers_reload(). + + RETURN VALUES + 0 ok + 1 Could not initialize servers +*/ + +my_bool servers_init(bool dont_read_servers_table) +{ + THD *thd; + my_bool return_val= 0; + DBUG_ENTER("servers_init"); + + /* init the mutex */ + if (pthread_mutex_init(&servers_cache_mutex, MY_MUTEX_INIT_FAST)) + DBUG_RETURN(1); + + /* initialise our servers cache */ + if (hash_init(&servers_cache, system_charset_info, 32, 0, 0, + (hash_get_key) servers_cache_get_key, 0, 0)) + { + return_val= 1; /* we failed, out of memory? */ + goto end; + } + + /* Initialize the mem root for data */ + init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0); + + /* + at this point, the cache is initialised, let it be known + */ + servers_cache_initialised= TRUE; + + if (dont_read_servers_table) + goto end; + + /* + To be able to run this from boot, we allocate a temporary THD + */ + if (!(thd=new THD)) + DBUG_RETURN(1); + thd->thread_stack= (char*) &thd; + thd->store_globals(); + /* + It is safe to call servers_reload() since servers_* arrays and hashes which + will be freed there are global static objects and thus are initialized + by zeros at startup. + */ + return_val= servers_reload(thd); + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + +end: + DBUG_RETURN(return_val); +} + +/* + Initialize server structures + + SYNOPSIS + servers_load() + thd Current thread + tables List containing open "mysql.servers" + + RETURN VALUES + FALSE Success + TRUE Error +*/ + +static my_bool servers_load(THD *thd, TABLE_LIST *tables) +{ + TABLE *table; + READ_RECORD read_record_info; + my_bool return_val= TRUE; + DBUG_ENTER("servers_load"); + + if (!servers_cache_initialised) + DBUG_RETURN(0); + + /* need to figure out how to utilise this variable */ + servers_version++; /* servers updated */ + + /* first, send all cached rows to sleep with the fishes, oblivion! + I expect this crappy comment replaced */ + free_root(&mem, MYF(MY_MARK_BLOCKS_FREE)); + my_hash_reset(&servers_cache); + + init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info))) + { + /* return_val is already TRUE, so no need to set */ + if ((get_server_from_table_to_cache(table))) + goto end; + } + + return_val=0; + +end: + end_read_record(&read_record_info); + DBUG_RETURN(return_val); +} + + +/* + Forget current servers cache and read new servers + from the conneciton table. + + SYNOPSIS + servers_reload() + thd Current thread + + NOTE + All tables of calling thread which were open and locked by LOCK TABLES + statement will be unlocked and closed. + This function is also used for initialization of structures responsible + for user/db-level privilege checking. + + RETURN VALUE + FALSE Success + TRUE Failure +*/ + +my_bool servers_reload(THD *thd) +{ + TABLE_LIST tables[1]; + my_bool return_val= 1; + DBUG_ENTER("servers_reload"); + + if (thd->locked_tables) + { // Can't have locked tables here + thd->lock=thd->locked_tables; + thd->locked_tables=0; + close_thread_tables(thd); + } + + /* + To avoid deadlocks we should obtain table locks before + obtaining servers_cache->lock mutex. + */ + bzero((char*) tables, sizeof(tables)); + tables[0].alias= tables[0].table_name= (char*) "servers"; + tables[0].db= (char*) "mysql"; + tables[0].lock_type= TL_READ; + + if (simple_open_n_lock_tables(thd, tables)) + { + sql_print_error("Fatal error: Can't open and lock privilege tables: %s", + thd->net.last_error); + goto end; + } + + DBUG_PRINT("info", ("locking servers_cache")); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + //old_servers_cache= servers_cache; + //old_mem=mem; + + if ((return_val= servers_load(thd, tables))) + { // Error. Revert to old list + /* blast, for now, we have no servers, discuss later way to preserve */ + + DBUG_PRINT("error",("Reverting to old privileges")); + servers_free(); + } + + DBUG_PRINT("info", ("unlocking servers_cache")); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + +end: + close_thread_tables(thd); + DBUG_RETURN(return_val); +} + +/* + Initialize structures responsible for servers used in federated + server scheme information for them from the server + table in the 'mysql' database. + + SYNOPSIS + get_server_from_table_to_cache() + TABLE *table open table pointer + + + NOTES + This function takes a TABLE pointer (pointing to an opened + table). With this open table, a FOREIGN_SERVER struct pointer + is allocated into root memory, then each member of the FOREIGN_SERVER + struct is populated. A char pointer takes the return value of get_field + for each column we're interested in obtaining, and if that pointer + isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise, + is set to the value of an empty string, since get_field would set it to + 0x0 if the column's value is empty, even if the default value for that + column is NOT NULL. + + RETURN VALUES + 0 ok + 1 could not insert server struct into global servers cache +*/ + +my_bool get_server_from_table_to_cache(TABLE *table) +{ + /* alloc a server struct */ + char *ptr; + char *blank= (char*)""; + FOREIGN_SERVER *server= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + DBUG_ENTER("get_server_from_table_to_cache"); + table->use_all_columns(); + + /* get each field into the server struct ptr */ + server->server_name= get_field(&mem, table->field[0]); + server->server_name_length= strlen(server->server_name); + ptr= get_field(&mem, table->field[1]); + server->host= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[2]); + server->db= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[3]); + server->username= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[4]); + server->password= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[5]); + server->sport= ptr ? ptr : blank; + + server->port= server->sport ? atoi(server->sport) : 0; + + ptr= get_field(&mem, table->field[6]); + server->socket= ptr && strlen(ptr) ? ptr : NULL; + ptr= get_field(&mem, table->field[7]); + server->scheme= ptr ? ptr : blank; + ptr= get_field(&mem, table->field[8]); + server->owner= ptr ? ptr : blank; + DBUG_PRINT("info", ("server->server_name %s", server->server_name)); + DBUG_PRINT("info", ("server->host %s", server->host)); + DBUG_PRINT("info", ("server->db %s", server->db)); + DBUG_PRINT("info", ("server->username %s", server->username)); + DBUG_PRINT("info", ("server->password %s", server->password)); + DBUG_PRINT("info", ("server->socket %s", server->socket)); + if (my_hash_insert(&servers_cache, (byte*) server)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + server->server_name, server)); + // error handling needed here + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + +/* + SYNOPSIS + server_exists_in_table() + THD *thd - thread pointer + LEX_SERVER_OPTIONS *server_options - pointer to Lex->server_options + + NOTES + This function takes a LEX_SERVER_OPTIONS struct, which is very much the + same type of structure as a FOREIGN_SERVER, it contains the values parsed + in any one of the [CREATE|DELETE|DROP] SERVER statements. Using the + member "server_name", index_read_idx either founds the record and returns + 1, or doesn't find the record, and returns 0 + + RETURN VALUES + 0 record not found + 1 record found +*/ + +my_bool server_exists_in_table(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + byte server_key[MAX_KEY_LENGTH]; + int result= 1; + int error; + TABLE_LIST tables; + TABLE *table; + + DBUG_ENTER("server_exists"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + table->use_all_columns(); + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + rw_wrlock(&THR_LOCK_servers); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server_options->server_name, + server_options->server_name_length, + system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + result= -1; + } + result= 0; + DBUG_PRINT("info",("record for server '%s' not found!", + server_options->server_name)); + } + + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(result); +} + +/* + SYNOPSIS + insert_server() + THD *thd - thread pointer + FOREIGN_SERVER *server - pointer to prepared FOREIGN_SERVER struct + + NOTES + This function takes a server object that is has all members properly + prepared, ready to be inserted both into the mysql.servers table and + the servers cache. + + RETURN VALUES + 0 - no error + other - error code +*/ + +int insert_server(THD *thd, FOREIGN_SERVER *server) +{ + byte server_key[MAX_KEY_LENGTH]; + int error; + TABLE_LIST tables; + TABLE *table; + + DBUG_ENTER("insert_server"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + /* lock mutex to make sure no changes happen */ + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + /* lock table */ + rw_wrlock(&THR_LOCK_servers); + + /* insert the server into the table */ + if ((error= insert_server_record(table, server))) + goto end; + + /* insert the server into the cache */ + if ((error= insert_server_record_into_cache(server))) + goto end; + +end: + /* unlock the table */ + rw_unlock(&THR_LOCK_servers); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(error); +} + +/* + SYNOPSIS + int insert_server_record_into_cache() + FOREIGN_SERVER *server + + NOTES + This function takes a FOREIGN_SERVER pointer to an allocated (root mem) + and inserts it into the global servers cache + + RETURN VALUE + 0 - no error + >0 - error code + +*/ + +int insert_server_record_into_cache(FOREIGN_SERVER *server) +{ + int error=0; + DBUG_ENTER("insert_server_record_into_cache"); + /* + We succeded in insertion of the server to the table, now insert + the server to the cache + */ + DBUG_PRINT("info", ("inserting server %s at %lx, length %d", + server->server_name, server, + server->server_name_length)); + if (my_hash_insert(&servers_cache, (byte*) server)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + server->server_name, server)); + // error handling needed here + error= 1; + } + DBUG_RETURN(error); +} + +/* + SYNOPSIS + store_server_fields() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes an opened table object, and a pointer to an + allocated FOREIGN_SERVER struct, and then stores each member of + the FOREIGN_SERVER to the appropriate fields in the table, in + advance of insertion into the mysql.servers table + + RETURN VALUE + VOID + +*/ + +void store_server_fields(TABLE *table, FOREIGN_SERVER *server) +{ + + table->use_all_columns(); + /* + "server" has already been prepped by prepare_server_struct_for_<> + so, all we need to do is check if the value is set (> -1 for port) + + If this happens to be an update, only the server members that + have changed will be set. If an insert, then all will be set, + even if with empty strings + */ + if (server->host) + table->field[1]->store(server->host, + (uint) strlen(server->host), system_charset_info); + if (server->db) + table->field[2]->store(server->db, + (uint) strlen(server->db), system_charset_info); + if (server->username) + table->field[3]->store(server->username, + (uint) strlen(server->username), system_charset_info); + if (server->password) + table->field[4]->store(server->password, + (uint) strlen(server->password), system_charset_info); + if (server->port > -1) + table->field[5]->store(server->port); + + if (server->socket) + table->field[6]->store(server->socket, + (uint) strlen(server->socket), system_charset_info); + if (server->scheme) + table->field[7]->store(server->scheme, + (uint) strlen(server->scheme), system_charset_info); + if (server->owner) + table->field[8]->store(server->owner, + (uint) strlen(server->owner), system_charset_info); +} + +/* + SYNOPSIS + insert_server_record() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes the arguments of an open table object and a pointer + to an allocated FOREIGN_SERVER struct. It stores the server_name into + the first field of the table (the primary key, server_name column). With + this, index_read_idx is called, if the record is found, an error is set + to ER_FOREIGN_SERVER_EXISTS (the server with that server name exists in the + table), if not, then store_server_fields stores all fields of the + FOREIGN_SERVER to the table, then ha_write_row is inserted. If an error + is encountered in either index_read_idx or ha_write_row, then that error + is returned + + RETURN VALUE + 0 - no errors + >0 - error code + + */ + +int insert_server_record(TABLE *table, FOREIGN_SERVER *server) +{ + int error; + DBUG_ENTER("insert_server_record"); + table->use_all_columns(); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server->server_name, + server->server_name_length, + system_charset_info); + + /* read index until record is that specified in server_name */ + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + /* if not found, err */ + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + /* store each field to be inserted */ + store_server_fields(table, server); + + DBUG_PRINT("info",("record for server '%s' not found!", + server->server_name)); + /* write/insert the new server */ + if ((error=table->file->ha_write_row(table->record[0]))) + { + table->file->print_error(error, MYF(0)); + } + else + error= 0; + } + else + error= ER_FOREIGN_SERVER_EXISTS; + DBUG_RETURN(error); +} + +/* + SYNOPSIS + drop_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + This function takes as its arguments a THD object pointer and a pointer + to a LEX_SERVER_OPTIONS struct from the parser. The member 'server_name' + of this LEX_SERVER_OPTIONS struct contains the value of the server to be + deleted. The mysql.servers table is opened via open_ltable, a table object + returned, the servers cache mutex locked, then delete_server_record is + called with this table object and LEX_SERVER_OPTIONS server_name and + server_name_length passed, containing the name of the server to be + dropped/deleted, then delete_server_record_in_cache is called to delete + the server from the servers cache. + + RETURN VALUE + 0 - no error + > 0 - error code +*/ + +int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + byte server_key[MAX_KEY_LENGTH]; + int error; + TABLE_LIST tables; + TABLE *table; + + DBUG_ENTER("drop_server"); + DBUG_PRINT("info", ("server name server->server_name %s", + server_options->server_name)); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*) "mysql"; + tables.alias= tables.table_name= (char*) "servers"; + + /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(TRUE); + + rw_wrlock(&THR_LOCK_servers); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + + if ((error= delete_server_record(table, + server_options->server_name, + server_options->server_name_length))) + goto end; + + + if ((error= delete_server_record_in_cache(server_options))) + goto end; + +end: + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(error); +} +/* + + SYNOPSIS + delete_server_record_in_cache() + LEX_SERVER_OPTIONS *server_options + + NOTES + This function's argument is a LEX_SERVER_OPTIONS struct pointer. This + function uses the "server_name" and "server_name_length" members of the + lex->server_options to search for the server in the servers_cache. Upon + returned the server (pointer to a FOREIGN_SERVER struct), it then deletes + that server from the servers_cache hash. + + RETURN VALUE + 0 - no error + +*/ + +int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options) +{ + + int error= 0; + FOREIGN_SERVER *server; + DBUG_ENTER("delete_server_record_in_cache"); + + DBUG_PRINT("info",("trying to obtain server name %s length %d", + server_options->server_name, + server_options->server_name_length)); + + + if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_options->server_name, + server_options->server_name_length))) + { + DBUG_PRINT("info", ("server_name %s length %d not found!", + server_options->server_name, + server_options->server_name_length)); + // what should be done if not found in the cache? + } + /* + We succeded in deletion of the server to the table, now delete + the server from the cache + */ + DBUG_PRINT("info",("deleting server %s length %d", + server->server_name, + server->server_name_length)); + + if (server) + VOID(hash_delete(&servers_cache, (byte*) server)); + + servers_version++; /* servers updated */ + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + update_server() + THD *thd + FOREIGN_SERVER *existing + FOREIGN_SERVER *altered + + NOTES + This function takes as arguments a THD object pointer, and two pointers, + one pointing to the existing FOREIGN_SERVER struct "existing" (which is + the current record as it is) and another pointer pointing to the + FOREIGN_SERVER struct with the members containing the modified/altered + values that need to be updated in both the mysql.servers table and the + servers_cache. It opens a table, passes the table and the altered + FOREIGN_SERVER pointer, which will be used to update the mysql.servers + table for the particular server via the call to update_server_record, + and in the servers_cache via update_server_record_in_cache. + + RETURN VALUE + 0 - no error + >0 - error code + +*/ + +int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered) +{ + int error= 0; + TABLE *table; + TABLE_LIST tables; + DBUG_ENTER("update_server"); + + bzero((char*) &tables, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.alias= tables.table_name= (char*)"servers"; + + if (!(table= open_ltable(thd, &tables, TL_WRITE))) + DBUG_RETURN(1); + + rw_wrlock(&THR_LOCK_servers); + if ((error= update_server_record(table, altered))) + goto end; + + update_server_record_in_cache(existing, altered); + +end: + rw_unlock(&THR_LOCK_servers); + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + update_server_record_in_cache() + FOREIGN_SERVER *existing + FOREIGN_SERVER *altered + + NOTES + This function takes as an argument the FOREIGN_SERVER structi pointer + for the existing server and the FOREIGN_SERVER struct populated with only + the members which have been updated. It then "merges" the "altered" struct + members to the existing server, the existing server then represents an + updated server. Then, the existing record is deleted from the servers_cache + HASH, then the updated record inserted, in essence replacing the old + record. + + RETURN VALUE + 0 - no error + 1 - error + +*/ + +int update_server_record_in_cache(FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered) +{ + int error= 0; + DBUG_ENTER("update_server_record_in_cache"); + + /* + update the members that haven't been change in the altered server struct + with the values of the existing server struct + */ + merge_server_struct(existing, altered); + + /* + delete the existing server struct from the server cache + */ + VOID(hash_delete(&servers_cache, (byte*)existing)); + + /* + Insert the altered server struct into the server cache + */ + if (my_hash_insert(&servers_cache, (byte*)altered)) + { + DBUG_PRINT("info", ("had a problem inserting server %s at %lx", + altered->server_name, altered)); + error= 1; + } + + servers_version++; /* servers updated */ + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + merge_server_struct() + FOREIGN_SERVER *from + FOREIGN_SERVER *to + + NOTES + This function takes as its arguments two pointers each to an allocated + FOREIGN_SERVER struct. The first FOREIGN_SERVER struct represents the struct + that we will obtain values from (hence the name "from"), the second + FOREIGN_SERVER struct represents which FOREIGN_SERVER struct we will be + "copying" any members that have a value to (hence the name "to") + + RETURN VALUE + VOID + +*/ + +void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to) +{ + DBUG_ENTER("merge_server_struct"); + if (!to->host) + to->host= strdup_root(&mem, from->host); + if (!to->db) + to->db= strdup_root(&mem, from->db); + if (!to->username) + to->username= strdup_root(&mem, from->username); + if (!to->password) + to->password= strdup_root(&mem, from->password); + if (to->port == -1) + to->port= from->port; + if (!to->socket) + to->socket= strdup_root(&mem, from->socket); + if (!to->scheme) + to->scheme= strdup_root(&mem, from->scheme); + if (!to->owner) + to->owner= strdup_root(&mem, from->owner); + + DBUG_VOID_RETURN; +} + +/* + + SYNOPSIS + update_server_record() + TABLE *table + FOREIGN_SERVER *server + + NOTES + This function takes as its arguments an open TABLE pointer, and a pointer + to an allocated FOREIGN_SERVER structure representing an updated record + which needs to be inserted. The primary key, server_name is stored to field + 0, then index_read_idx is called to read the index to that record, the + record then being ready to be updated, if found. If not found an error is + set and error message printed. If the record is found, store_record is + called, then store_server_fields stores each field from the the members of + the updated FOREIGN_SERVER struct. + + RETURN VALUE + 0 - no error + +*/ + +int update_server_record(TABLE *table, FOREIGN_SERVER *server) +{ + int error=0; + DBUG_ENTER("update_server_record"); + table->use_all_columns(); + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server->server_name, + server->server_name_length, + system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + DBUG_PRINT("info",("server not found!")); + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + } + else + { + /* ok, so we can update since the record exists in the table */ + store_record(table,record[1]); + store_server_fields(table, server); + if ((error=table->file->ha_update_row(table->record[1],table->record[0]))) + { + DBUG_PRINT("info",("problems with ha_update_row %d", error)); + goto end; + } + } + +end: + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + delete_server_record() + TABLE *table + char *server_name + int server_name_length + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int delete_server_record(TABLE *table, + char *server_name, + int server_name_length) +{ + int error= 0; + DBUG_ENTER("delete_server_record"); + table->use_all_columns(); + + /* set the field that's the PK to the value we're looking for */ + table->field[0]->store(server_name, server_name_length, system_charset_info); + + if ((error= table->file->index_read_idx(table->record[0], 0, + (byte *)table->field[0]->ptr, + table->key_info[0].key_length, + HA_READ_KEY_EXACT))) + { + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + table->file->print_error(error, MYF(0)); + error= 1; + } + DBUG_PRINT("info",("server not found!")); + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + } + else + { + if ((error= table->file->ha_delete_row(table->record[0]))) + table->file->print_error(error, MYF(0)); + } + +end: + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + create_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int error; + FOREIGN_SERVER *server; + + DBUG_ENTER("create_server"); + DBUG_PRINT("info", ("server_options->server_name %s", + server_options->server_name)); + + server= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + + if ((error= prepare_server_struct_for_insert(server_options, server))) + goto end; + + if ((error= insert_server(thd, server))) + goto end; + + DBUG_PRINT("info", ("error returned %d", error)); + +end: + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + alter_server() + THD *thd + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options) +{ + int error= 0; + FOREIGN_SERVER *altered, *existing; + DBUG_ENTER("alter_server"); + DBUG_PRINT("info", ("server_options->server_name %s", + server_options->server_name)); + + altered= (FOREIGN_SERVER *)alloc_root(&mem, + sizeof(FOREIGN_SERVER)); + + VOID(pthread_mutex_lock(&servers_cache_mutex)); + + if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_options->server_name, + server_options->server_name_length))) + { + error= ER_FOREIGN_SERVER_DOESNT_EXIST; + goto end; + } + + if ((error= prepare_server_struct_for_update(server_options, existing, altered))) + goto end; + + if ((error= update_server(thd, existing, altered))) + goto end; + +end: + DBUG_PRINT("info", ("error returned %d", error)); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + prepare_server_struct_for_insert() + LEX_SERVER_OPTIONS *server_options + FOREIGN_SERVER *server + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *server) +{ + int error; + char *unset_ptr= (char*)""; + DBUG_ENTER("prepare_server_struct"); + + error= 0; + + /* these two MUST be set */ + server->server_name= strdup_root(&mem, server_options->server_name); + server->server_name_length= server_options->server_name_length; + + server->host= server_options->host ? + strdup_root(&mem, server_options->host) : unset_ptr; + + server->db= server_options->db ? + strdup_root(&mem, server_options->db) : unset_ptr; + + server->username= server_options->username ? + strdup_root(&mem, server_options->username) : unset_ptr; + + server->password= server_options->password ? + strdup_root(&mem, server_options->password) : unset_ptr; + + /* set to 0 if not specified */ + server->port= server_options->port > -1 ? + server_options->port : 0; + + server->socket= server_options->socket ? + strdup_root(&mem, server_options->socket) : unset_ptr; + + server->scheme= server_options->scheme ? + strdup_root(&mem, server_options->scheme) : unset_ptr; + + server->owner= server_options->owner ? + strdup_root(&mem, server_options->owner) : unset_ptr; + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + prepare_server_struct_for_update() + LEX_SERVER_OPTIONS *server_options + + NOTES + + RETURN VALUE + 0 - no error + +*/ + +int prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered) +{ + int error; + DBUG_ENTER("prepare_server_struct_for_update"); + error= 0; + + altered->server_name= strdup_root(&mem, server_options->server_name); + altered->server_name_length= server_options->server_name_length; + DBUG_PRINT("info", ("existing name %s altered name %s", + existing->server_name, altered->server_name)); + + /* + The logic here is this: is this value set AND is it different + than the existing value? + */ + altered->host= + (server_options->host && (strcmp(server_options->host, existing->host))) ? + strdup_root(&mem, server_options->host) : 0; + + altered->db= + (server_options->db && (strcmp(server_options->db, existing->db))) ? + strdup_root(&mem, server_options->db) : 0; + + altered->username= + (server_options->username && + (strcmp(server_options->username, existing->username))) ? + strdup_root(&mem, server_options->username) : 0; + + altered->password= + (server_options->password && + (strcmp(server_options->password, existing->password))) ? + strdup_root(&mem, server_options->password) : 0; + + /* + port is initialised to -1, so if unset, it will be -1 + */ + altered->port= (server_options->port > -1 && + server_options->port != existing->port) ? + server_options->port : -1; + + altered->socket= + (server_options->socket && + (strcmp(server_options->socket, existing->socket))) ? + strdup_root(&mem, server_options->socket) : 0; + + altered->scheme= + (server_options->scheme && + (strcmp(server_options->scheme, existing->scheme))) ? + strdup_root(&mem, server_options->scheme) : 0; + + altered->owner= + (server_options->owner && + (strcmp(server_options->owner, existing->owner))) ? + strdup_root(&mem, server_options->owner) : 0; + + DBUG_RETURN(error); +} + +/* + + SYNOPSIS + servers_free() + bool end + + NOTES + + RETURN VALUE + void + +*/ + +void servers_free(bool end) +{ + DBUG_ENTER("servers_free"); + if (!servers_cache_initialised) + DBUG_VOID_RETURN; + VOID(pthread_mutex_destroy(&servers_cache_mutex)); + servers_cache_initialised=0; + free_root(&mem,MYF(0)); + hash_free(&servers_cache); + DBUG_VOID_RETURN; +} + + + +/* + + SYNOPSIS + get_server_by_name() + const char *server_name + + NOTES + + RETURN VALUE + FOREIGN_SERVER * + +*/ + +FOREIGN_SERVER *get_server_by_name(const char *server_name) +{ + ulong error_num=0; + uint i, server_name_length; + FOREIGN_SERVER *server; + DBUG_ENTER("get_server_by_name"); + DBUG_PRINT("info", ("server_name %s", server_name)); + + server_name_length= strlen(server_name); + + if (! server_name || !strlen(server_name)) + { + DBUG_PRINT("info", ("server_name not defined!")); + error_num= 1; + DBUG_RETURN((FOREIGN_SERVER *)NULL); + } + + DBUG_PRINT("info", ("locking servers_cache")); + VOID(pthread_mutex_lock(&servers_cache_mutex)); + if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache, + (byte*) server_name, + server_name_length))) + { + DBUG_PRINT("info", ("server_name %s length %d not found!", + server_name, server_name_length)); + server= (FOREIGN_SERVER *) NULL; + } + DBUG_PRINT("info", ("unlocking servers_cache")); + VOID(pthread_mutex_unlock(&servers_cache_mutex)); + DBUG_RETURN(server); + +} diff --git a/sql/sql_servers.h b/sql/sql_servers.h new file mode 100644 index 00000000000..0079885163c --- /dev/null +++ b/sql/sql_servers.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 "slave.h" // for tables_ok(), rpl_filter + +/* structs */ +typedef struct st_federated_server +{ + char *server_name; + long port; + uint server_name_length; + char *db, *scheme, *username, *password, *socket, *owner, *host, *sport; +} FOREIGN_SERVER; + +/* cache handlers */ +my_bool servers_init(bool dont_read_server_table); +static my_bool servers_load(THD *thd, TABLE_LIST *tables); +my_bool servers_reload(THD *thd); +my_bool get_server_from_table_to_cache(TABLE *table); +void servers_free(bool end=0); + +/* insert functions */ +int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int insert_server(THD *thd, FOREIGN_SERVER *server_options); +int insert_server_record(TABLE *table, FOREIGN_SERVER *server); +int insert_server_record_into_cache(FOREIGN_SERVER *server); +void store_server_fields_for_insert(TABLE *table, FOREIGN_SERVER *server); +void store_server_fields_for_insert(TABLE *table, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +int prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *server); + +/* drop functions */ +int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int delete_server_record(TABLE *table, + char *server_name, + int server_name_length); +int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options); + +/* update functions */ +int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options); +int prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options, + FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered); +int update_server_record(TABLE *table, FOREIGN_SERVER *server); +int update_server_record_in_cache(FOREIGN_SERVER *existing, + FOREIGN_SERVER *altered); +/* utility functions */ +void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to); +FOREIGN_SERVER *get_server_by_name(const char *server_name); +my_bool server_exists_in_table(THD *thd, char *server_name); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d6768a474e5..dd856b33b7a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -364,6 +364,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token HELP_SYM %token HEX_NUM %token HIGH_PRIORITY +%token HOST_SYM %token HOSTS_SYM %token HOUR_MICROSECOND_SYM %token HOUR_MINUTE_SYM @@ -506,6 +507,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ONE_SYM %token OPEN_SYM /* SQL-2003-R */ %token OPTIMIZE +%token OPTIONS_SYM %token OPTION /* SQL-2003-N */ %token OPTIONALLY %token OR2_SYM @@ -515,6 +517,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token OUTER %token OUTFILE %token OUT_SYM /* SQL-2003-R */ +%token OWNER_SYM %token PACK_KEYS_SYM %token PARAM_MARKER %token PARSER_SYM @@ -528,6 +531,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PLUGIN_SYM %token POINT_SYM %token POLYGON +%token PORT_SYM %token POSITION_SYM /* SQL-2003-N */ %token PRECISION /* SQL-2003-R */ %token PREPARE_SYM /* SQL-2003-R */ @@ -596,6 +600,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ +%token SERVER_SYM +%token SERVER_OPTIONS %token SET /* SQL-2003-R */ %token SET_VAR %token SHARE_SYM @@ -608,6 +614,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SLAVE %token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM +%token SOCKET_SYM %token SONAME_SYM %token SOUNDS_SYM %token SPATIAL_SYM @@ -715,6 +722,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WHILE_SYM %token WITH /* SQL-2003-R */ %token WORK_SYM /* SQL-2003-N */ +%token WRAPPER_SYM %token WRITE_SYM /* SQL-2003-N */ %token X509_SYM %token XA_SYM @@ -910,6 +918,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event init_key_options key_options key_opts key_opt key_using_alg + server_def server_options_list server_option END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1303,8 +1312,61 @@ create: { Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; } + | CREATE server_def + { + Lex->sql_command= SQLCOM_CREATE_SERVER; + } ; +server_def: + SERVER_SYM ident_or_text FOREIGN DATA_SYM WRAPPER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + Lex->server_options.server_name= $2.str; + Lex->server_options.server_name_length= $2.length; + Lex->server_options.scheme= $6.str; + } + ; +server_options_list: + server_option + | server_options_list ',' server_option + ; + +server_option: + USER TEXT_STRING_sys + { + Lex->server_options.username= $2.str; + } + | + HOST_SYM TEXT_STRING_sys + { + Lex->server_options.host= $2.str; + } + | + DATABASE TEXT_STRING_sys + { + Lex->server_options.db= $2.str; + } + | + OWNER_SYM TEXT_STRING_sys + { + Lex->server_options.owner= $2.str; + } + | + PASSWORD TEXT_STRING_sys + { + Lex->server_options.password= $2.str; + } + | + SOCKET_SYM TEXT_STRING_sys + { + Lex->server_options.socket= $2.str; + } + | + PORT_SYM ulong_num + { + Lex->server_options.port= $2; + } + ; event_tail: EVENT_SYM opt_if_not_exists sp_name @@ -4829,6 +4891,13 @@ alter: LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; } + | ALTER SERVER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + LEX *lex= Lex; + Lex->sql_command= SQLCOM_ALTER_SERVER; + Lex->server_options.server_name= $3.str; + Lex->server_options.server_name_length= $3.length; + } ; ev_alter_on_schedule_completion: /* empty */ { $$= 0;} @@ -7744,6 +7813,13 @@ drop: LEX *lex= Lex; lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; } + | DROP SERVER_SYM if_exists ident_or_text + { + Lex->sql_command = SQLCOM_DROP_SERVER; + Lex->drop_if_exists= $3; + Lex->server_options.server_name= $4.str; + Lex->server_options.server_name_length= $4.length; + } ; table_list: @@ -9310,12 +9386,16 @@ keyword: | FLUSH_SYM {} | HANDLER_SYM {} | HELP_SYM {} + | HOST_SYM {} | INSTALL_SYM {} | LANGUAGE_SYM {} | NO_SYM {} | OPEN_SYM {} + | OPTIONS_SYM {} + | OWNER_SYM {} | PARSER_SYM {} | PARTITION_SYM {} + | PORT_SYM {} | PREPARE_SYM {} | REMOVE_SYM {} | REPAIR {} @@ -9324,7 +9404,9 @@ keyword: | ROLLBACK_SYM {} | SAVEPOINT_SYM {} | SECURITY_SYM {} + | SERVER_SYM {} | SIGNED_SYM {} + | SOCKET_SYM {} | SLAVE {} | SONAME_SYM {} | START_SYM {} @@ -9332,6 +9414,8 @@ keyword: | TRUNCATE_SYM {} | UNICODE_SYM {} | UNINSTALL_SYM {} + | USER {} + | WRAPPER_SYM {} | XA_SYM {} | UPGRADE_SYM {} ; diff --git a/sql/sql_yacc.yy.bak b/sql/sql_yacc.yy.bak new file mode 100644 index 00000000000..a0cda868e18 --- /dev/null +++ b/sql/sql_yacc.yy.bak @@ -0,0 +1,11279 @@ +/* Copyright (C) 2000-2003 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 */ + +/* sql_yacc.yy */ + +%{ +/* thd is passed as an arg to yyparse(), and subsequently to yylex(). +** The type will be void*, so it must be cast to (THD*) when used. +** Use the YYTHD macro for this. +*/ +#define YYPARSE_PARAM yythd +#define YYLEX_PARAM yythd +#define YYTHD ((THD *)yythd) + +#define MYSQL_YACC +#define YYINITDEPTH 100 +#define YYMAXDEPTH 3200 /* Because of 64K stack */ +#define Lex (YYTHD->lex) +#define Select Lex->current_select +#include "mysql_priv.h" +#include "slave.h" +#include "lex_symbol.h" +#include "item_create.h" +#include "sp_head.h" +#include "sp_pcontext.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "event_data_objects.h" +#include <myisam.h> +#include <myisammrg.h> + +int yylex(void *yylval, void *yythd); + +const LEX_STRING null_lex_str={0,0}; + +#define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} + +#define YYERROR_UNLESS(A) \ + if (!(A)) \ + { \ + yyerror(ER(ER_SYNTAX_ERROR)); \ + YYABORT; \ + } + +/* Helper for parsing "IS [NOT] truth_value" */ +inline Item *is_truth_value(THD *thd, Item *A, bool v1, bool v2) +{ + Item *v1_t= new (thd->mem_root) Item_int((char *) (v1 ? "TRUE" : "FALSE"), + v1, 1); + Item *v1_f= new (thd->mem_root) Item_int((char *) (v1 ? "FALSE" : "TRUE"), + !v1, 1); + Item *v2_t= new (thd->mem_root) Item_int((char *) (v2 ? "TRUE" : "FALSE"), + v2, 1); + Item *ifnull= new (thd->mem_root) Item_func_ifnull(A, v2_t); + + return new (thd->mem_root) Item_func_if(ifnull, v1_t, v1_f); +} + +#ifndef DBUG_OFF +#define YYDEBUG 1 +#else +#define YYDEBUG 0 +#endif + +#ifndef DBUG_OFF +void turn_parser_debug_on() +{ + /* + MYSQLdebug is in sql/sql_yacc.cc, in bison generated code. + Turning this option on is **VERY** verbose, and should be + used when investigating a syntax error problem only. + + The syntax to run with bison traces is as follows : + - Starting a server manually : + mysqld --debug="d,parser_debug" ... + - Running a test : + mysql-test-run.pl --mysqld="--debug=d,parser_debug" ... + + The result will be in the process stderr (var/log/master.err) + */ + + extern int yydebug; + yydebug= 1; +} +#endif + +static bool is_native_function(THD *thd, const LEX_STRING *name) +{ + if (find_native_function_builder(thd, *name)) + return true; + + if (is_lex_native_function(name)) + return true; + + return false; +} + +%} +%union { + int num; + ulong ulong_num; + ulonglong ulonglong_number; + longlong longlong_number; + LEX_STRING lex_str; + LEX_STRING *lex_str_ptr; + LEX_SYMBOL symbol; + Table_ident *table; + char *simple_string; + Item *item; + Item_num *item_num; + List<Item> *item_list; + List<String> *string_list; + String *string; + key_part_spec *key_part; + TABLE_LIST *table_list; + udf_func *udf; + LEX_USER *lex_user; + struct sys_var_with_base variable; + enum enum_var_type var_type; + Key::Keytype key_type; + enum ha_key_alg key_alg; + handlerton *db_type; + enum row_type row_type; + enum ha_rkey_function ha_rkey_mode; + enum enum_tx_isolation tx_isolation; + enum Cast_target cast_type; + enum Item_udftype udf_type; + CHARSET_INFO *charset; + thr_lock_type lock_type; + interval_type interval, interval_time_st; + timestamp_type date_time_type; + st_select_lex *select_lex; + chooser_compare_func_creator boolfunc2creator; + struct sp_cond_type *spcondtype; + struct { int vars, conds, hndlrs, curs; } spblock; + sp_name *spname; + struct st_lex *lex; + sp_head *sphead; + struct p_elem_val *p_elem_value; +} + +%{ +bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); +%} + +%pure_parser /* We have threads */ + +/* + Comments for TOKENS. + For each token, please include in the same line a comment that contains + the following tags: + SQL-2003-R : Reserved keyword as per SQL-2003 + SQL-2003-N : Non Reserved keyword as per SQL-2003 + SQL-1999-R : Reserved keyword as per SQL-1999 + SQL-1999-N : Non Reserved keyword as per SQL-1999 + MYSQL : MySQL extention (unspecified) + MYSQL-FUNC : MySQL extention, function + INTERNAL : Not a real token, lex optimization + OPERATOR : SQL operator + FUTURE-USE : Reserved for futur use + + This makes the code grep-able, and helps maintenance. +*/ + +%token ABORT_SYM /* INTERNAL (used in lex) */ +%token ACCESSIBLE_SYM +%token ACTION /* SQL-2003-N */ +%token ADD /* SQL-2003-R */ +%token ADDDATE_SYM /* MYSQL-FUNC */ +%token AFTER_SYM /* SQL-2003-N */ +%token AGAINST +%token AGGREGATE_SYM +%token ALGORITHM_SYM +%token ALL /* SQL-2003-R */ +%token ALTER /* SQL-2003-R */ +%token ANALYZE_SYM +%token AND_AND_SYM /* OPERATOR */ +%token AND_SYM /* SQL-2003-R */ +%token ANY_SYM /* SQL-2003-R */ +%token AS /* SQL-2003-R */ +%token ASC /* SQL-2003-N */ +%token ASCII_SYM /* MYSQL-FUNC */ +%token ASENSITIVE_SYM /* FUTURE-USE */ +%token AT_SYM /* SQL-2003-R */ +%token AUTHORS_SYM +%token AUTOEXTEND_SIZE_SYM +%token AUTO_INC +%token AVG_ROW_LENGTH +%token AVG_SYM /* SQL-2003-N */ +%token BACKUP_SYM +%token BEFORE_SYM /* SQL-2003-N */ +%token BEGIN_SYM /* SQL-2003-R */ +%token BETWEEN_SYM /* SQL-2003-R */ +%token BIGINT /* SQL-2003-R */ +%token BINARY /* SQL-2003-R */ +%token BINLOG_SYM +%token BIN_NUM +%token BIT_AND /* MYSQL-FUNC */ +%token BIT_OR /* MYSQL-FUNC */ +%token BIT_SYM /* MYSQL-FUNC */ +%token BIT_XOR /* MYSQL-FUNC */ +%token BLOB_SYM /* SQL-2003-R */ +%token BOOLEAN_SYM /* SQL-2003-R */ +%token BOOL_SYM +%token BOTH /* SQL-2003-R */ +%token BTREE_SYM +%token BY /* SQL-2003-R */ +%token BYTE_SYM +%token CACHE_SYM +%token CALL_SYM /* SQL-2003-R */ +%token CASCADE /* SQL-2003-N */ +%token CASCADED /* SQL-2003-R */ +%token CASE_SYM /* SQL-2003-R */ +%token CAST_SYM /* SQL-2003-R */ +%token CHAIN_SYM /* SQL-2003-N */ +%token CHANGE +%token CHANGED +%token CHARSET +%token CHAR_SYM /* SQL-2003-R */ +%token CHECKSUM_SYM +%token CHECK_SYM /* SQL-2003-R */ +%token CIPHER_SYM +%token CLIENT_SYM +%token CLOSE_SYM /* SQL-2003-R */ +%token COALESCE /* SQL-2003-N */ +%token CODE_SYM +%token COLLATE_SYM /* SQL-2003-R */ +%token COLLATION_SYM /* SQL-2003-N */ +%token COLUMNS +%token COLUMN_SYM /* SQL-2003-R */ +%token COMMENT_SYM +%token COMMITTED_SYM /* SQL-2003-N */ +%token COMMIT_SYM /* SQL-2003-R */ +%token COMPACT_SYM +%token COMPLETION_SYM +%token COMPRESSED_SYM +%token CONCURRENT +%token CONDITION_SYM /* SQL-2003-N */ +%token CONNECTION_SYM +%token CONSISTENT_SYM +%token CONSTRAINT /* SQL-2003-R */ +%token CONTAINS_SYM /* SQL-2003-N */ +%token CONTINUE_SYM /* SQL-2003-R */ +%token CONTRIBUTORS_SYM +%token CONVERT_SYM /* SQL-2003-N */ +%token COUNT_SYM /* SQL-2003-N */ +%token CREATE /* SQL-2003-R */ +%token CROSS /* SQL-2003-R */ +%token CUBE_SYM /* SQL-2003-R */ +%token CURDATE /* MYSQL-FUNC */ +%token CURRENT_USER /* SQL-2003-R */ +%token CURSOR_SYM /* SQL-2003-R */ +%token CURTIME /* MYSQL-FUNC */ +%token DATABASE +%token DATABASES +%token DATAFILE_SYM +%token DATA_SYM /* SQL-2003-N */ +%token DATETIME +%token DATE_ADD_INTERVAL /* MYSQL-FUNC */ +%token DATE_SUB_INTERVAL /* MYSQL-FUNC */ +%token DATE_SYM /* SQL-2003-R */ +%token DAY_HOUR_SYM +%token DAY_MICROSECOND_SYM +%token DAY_MINUTE_SYM +%token DAY_SECOND_SYM +%token DAY_SYM /* SQL-2003-R */ +%token DEALLOCATE_SYM /* SQL-2003-R */ +%token DECIMAL_NUM +%token DECIMAL_SYM /* SQL-2003-R */ +%token DECLARE_SYM /* SQL-2003-R */ +%token DEFAULT /* SQL-2003-R */ +%token DEFINER_SYM +%token DELAYED_SYM +%token DELAY_KEY_WRITE_SYM +%token DELETE_SYM /* SQL-2003-R */ +%token DESC /* SQL-2003-N */ +%token DESCRIBE /* SQL-2003-R */ +%token DES_KEY_FILE +%token DETERMINISTIC_SYM /* SQL-2003-R */ +%token DIRECTORY_SYM +%token DISABLE_SYM +%token DISCARD +%token DISK_SYM +%token DISTINCT /* SQL-2003-R */ +%token DIV_SYM +%token DOUBLE_SYM /* SQL-2003-R */ +%token DO_SYM +%token DROP /* SQL-2003-R */ +%token DUAL_SYM +%token DUMPFILE +%token DUPLICATE_SYM +%token DYNAMIC_SYM /* SQL-2003-R */ +%token EACH_SYM /* SQL-2003-R */ +%token ELSE /* SQL-2003-R */ +%token ELSEIF_SYM +%token ENABLE_SYM +%token ENCLOSED +%token END /* SQL-2003-R */ +%token ENDS_SYM +%token END_OF_INPUT /* INTERNAL */ +%token ENGINES_SYM +%token ENGINE_SYM +%token ENUM +%token EQ /* OPERATOR */ +%token EQUAL_SYM /* OPERATOR */ +%token ERRORS +%token ESCAPED +%token ESCAPE_SYM /* SQL-2003-R */ +%token EVENTS_SYM +%token EVENT_SYM +%token EVERY_SYM /* SQL-2003-N */ +%token EXECUTE_SYM /* SQL-2003-R */ +%token EXISTS /* SQL-2003-R */ +%token EXIT_SYM +%token EXPANSION_SYM +%token EXTENDED_SYM +%token EXTENT_SIZE_SYM +%token EXTRACT_SYM /* SQL-2003-N */ +%token FALSE_SYM /* SQL-2003-R */ +%token FAST_SYM +%token FETCH_SYM /* SQL-2003-R */ +%token FILE_SYM +%token FIRST_SYM /* SQL-2003-N */ +%token FIXED_SYM +%token FLOAT_NUM +%token FLOAT_SYM /* SQL-2003-R */ +%token FLUSH_SYM +%token FORCE_SYM +%token FOREIGN /* SQL-2003-R */ +%token FOR_SYM /* SQL-2003-R */ +%token FOUND_SYM /* SQL-2003-R */ +%token FRAC_SECOND_SYM +%token FROM +%token FULL /* SQL-2003-R */ +%token FULLTEXT_SYM +%token FUNCTION_SYM /* SQL-2003-R */ +%token GE +%token GEOMETRYCOLLECTION +%token GEOMETRY_SYM +%token GET_FORMAT /* MYSQL-FUNC */ +%token GLOBAL_SYM /* SQL-2003-R */ +%token GRANT /* SQL-2003-R */ +%token GRANTS +%token GROUP /* SQL-2003-R */ +%token GROUP_CONCAT_SYM +%token GROUP_UNIQUE_USERS +%token GT_SYM /* OPERATOR */ +%token HANDLER_SYM +%token HASH_SYM +%token HAVING /* SQL-2003-R */ +%token HELP_SYM +%token HEX_NUM +%token HIGH_PRIORITY +%token HOST_SYM +%token HOSTS_SYM +%token HOUR_MICROSECOND_SYM +%token HOUR_MINUTE_SYM +%token HOUR_SECOND_SYM +%token HOUR_SYM /* SQL-2003-R */ +%token IDENT +%token IDENTIFIED_SYM +%token IDENT_QUOTED +%token IF +%token IGNORE_SYM +%token IMPORT +%token INDEXES +%token INDEX_SYM +%token INFILE +%token INITIAL_SIZE_SYM +%token INNER_SYM /* SQL-2003-R */ +%token INNOBASE_SYM +%token INOUT_SYM /* SQL-2003-R */ +%token INSENSITIVE_SYM /* SQL-2003-R */ +%token INSERT /* SQL-2003-R */ +%token INSERT_METHOD +%token INSTALL_SYM +%token INTERVAL_SYM /* SQL-2003-R */ +%token INTO /* SQL-2003-R */ +%token INT_SYM /* SQL-2003-R */ +%token INVOKER_SYM +%token IN_SYM /* SQL-2003-R */ +%token IS /* SQL-2003-R */ +%token ISOLATION /* SQL-2003-R */ +%token ISSUER_SYM +%token ITERATE_SYM +%token JOIN_SYM /* SQL-2003-R */ +%token KEYS +%token KEY_BLOCK_SIZE +%token KEY_SYM /* SQL-2003-N */ +%token KILL_SYM +%token LANGUAGE_SYM /* SQL-2003-R */ +%token LAST_SYM /* SQL-2003-N */ +%token LE /* OPERATOR */ +%token LEADING /* SQL-2003-R */ +%token LEAVES +%token LEAVE_SYM +%token LEFT /* SQL-2003-R */ +%token LESS_SYM +%token LEVEL_SYM +%token LEX_HOSTNAME +%token LIKE /* SQL-2003-R */ +%token LIMIT +%token LINEAR_SYM +%token LINES +%token LINESTRING +%token LIST_SYM +%token LOAD +%token LOCAL_SYM /* SQL-2003-R */ +%token LOCATOR_SYM /* SQL-2003-N */ +%token LOCKS_SYM +%token LOCK_SYM +%token LOGFILE_SYM +%token LOGS_SYM +%token LONGBLOB +%token LONGTEXT +%token LONG_NUM +%token LONG_SYM +%token LOOP_SYM +%token LOW_PRIORITY +%token LT /* OPERATOR */ +%token MASTER_CONNECT_RETRY_SYM +%token MASTER_HOST_SYM +%token MASTER_LOG_FILE_SYM +%token MASTER_LOG_POS_SYM +%token MASTER_PASSWORD_SYM +%token MASTER_PORT_SYM +%token MASTER_SERVER_ID_SYM +%token MASTER_SSL_CAPATH_SYM +%token MASTER_SSL_CA_SYM +%token MASTER_SSL_CERT_SYM +%token MASTER_SSL_CIPHER_SYM +%token MASTER_SSL_KEY_SYM +%token MASTER_SSL_SYM +%token MASTER_SYM +%token MASTER_USER_SYM +%token MATCH /* SQL-2003-R */ +%token MAX_CONNECTIONS_PER_HOUR +%token MAX_QUERIES_PER_HOUR +%token MAX_ROWS +%token MAX_SIZE_SYM +%token MAX_SYM /* SQL-2003-N */ +%token MAX_UPDATES_PER_HOUR +%token MAX_USER_CONNECTIONS_SYM +%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MEDIUMBLOB +%token MEDIUMINT +%token MEDIUMTEXT +%token MEDIUM_SYM +%token MEMORY_SYM +%token MERGE_SYM /* SQL-2003-R */ +%token MICROSECOND_SYM /* MYSQL-FUNC */ +%token MIGRATE_SYM +%token MINUTE_MICROSECOND_SYM +%token MINUTE_SECOND_SYM +%token MINUTE_SYM /* SQL-2003-R */ +%token MIN_ROWS +%token MIN_SYM /* SQL-2003-N */ +%token MODE_SYM +%token MODIFIES_SYM /* SQL-2003-R */ +%token MODIFY_SYM +%token MOD_SYM /* SQL-2003-N */ +%token MONTH_SYM /* SQL-2003-R */ +%token MULTILINESTRING +%token MULTIPOINT +%token MULTIPOLYGON +%token MUTEX_SYM +%token NAMES_SYM /* SQL-2003-N */ +%token NAME_SYM /* SQL-2003-N */ +%token NATIONAL_SYM /* SQL-2003-R */ +%token NATURAL /* SQL-2003-R */ +%token NCHAR_STRING +%token NCHAR_SYM /* SQL-2003-R */ +%token NDBCLUSTER_SYM +%token NE /* OPERATOR */ +%token NEG +%token NEW_SYM /* SQL-2003-R */ +%token NEXT_SYM /* SQL-2003-N */ +%token NODEGROUP_SYM +%token NONE_SYM /* SQL-2003-R */ +%token NOT2_SYM +%token NOT_SYM /* SQL-2003-R */ +%token NOW_SYM +%token NO_SYM /* SQL-2003-R */ +%token NO_WAIT_SYM +%token NO_WRITE_TO_BINLOG +%token NULL_SYM /* SQL-2003-R */ +%token NUM +%token NUMERIC_SYM /* SQL-2003-R */ +%token NVARCHAR_SYM +%token OFFSET_SYM +%token OLD_PASSWORD +%token ON /* SQL-2003-R */ +%token ONE_SHOT_SYM +%token ONE_SYM +%token OPEN_SYM /* SQL-2003-R */ +%token OPTIMIZE +%token OPTIONS_SYM +%token OPTION /* SQL-2003-N */ +%token OPTIONALLY +%token OR2_SYM +%token ORDER_SYM /* SQL-2003-R */ +%token OR_OR_SYM /* OPERATOR */ +%token OR_SYM /* SQL-2003-R */ +%token OUTER +%token OUTFILE +%token OUT_SYM /* SQL-2003-R */ +%token OWNER_SYM +%token PACK_KEYS_SYM +%token PARAM_MARKER +%token PARSER_SYM +%token PARTIAL /* SQL-2003-N */ +%token PARTITIONING_SYM +%token PARTITIONS_SYM +%token PARTITION_SYM /* SQL-2003-R */ +%token PASSWORD +%token PHASE_SYM +%token PLUGINS_SYM +%token PLUGIN_SYM +%token POINT_SYM +%token POLYGON +%token PORT_SYM +%token POSITION_SYM /* SQL-2003-N */ +%token PRECISION /* SQL-2003-R */ +%token PREPARE_SYM /* SQL-2003-R */ +%token PRESERVE_SYM +%token PREV_SYM +%token PRIMARY_SYM /* SQL-2003-R */ +%token PRIVILEGES /* SQL-2003-N */ +%token PROCEDURE /* SQL-2003-R */ +%token PROCESS +%token PROCESSLIST_SYM +%token PURGE +%token QUARTER_SYM +%token QUERY_SYM +%token QUICK +%token RANGE_SYM /* SQL-2003-R */ +%token READS_SYM /* SQL-2003-R */ +%token READ_ONLY_SYM +%token READ_SYM /* SQL-2003-N */ +%token READ_WRITE_SYM +%token REAL /* SQL-2003-R */ +%token REBUILD_SYM +%token RECOVER_SYM +%token REDOFILE_SYM +%token REDO_BUFFER_SIZE_SYM +%token REDUNDANT_SYM +%token REFERENCES /* SQL-2003-R */ +%token REGEXP +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM +%token RELAY_THREAD +%token RELEASE_SYM /* SQL-2003-R */ +%token RELOAD +%token REMOVE_SYM +%token RENAME +%token REORGANIZE_SYM +%token REPAIR +%token REPEATABLE_SYM /* SQL-2003-N */ +%token REPEAT_SYM /* MYSQL-FUNC */ +%token REPLACE /* MYSQL-FUNC */ +%token REPLICATION +%token REQUIRE_SYM +%token RESET_SYM +%token RESOURCES +%token RESTORE_SYM +%token RESTRICT +%token RESUME_SYM +%token RETURNS_SYM /* SQL-2003-R */ +%token RETURN_SYM /* SQL-2003-R */ +%token REVOKE /* SQL-2003-R */ +%token RIGHT /* SQL-2003-R */ +%token ROLLBACK_SYM /* SQL-2003-R */ +%token ROLLUP_SYM /* SQL-2003-R */ +%token ROUTINE_SYM /* SQL-2003-N */ +%token ROWS_SYM /* SQL-2003-R */ +%token ROW_FORMAT_SYM +%token ROW_SYM /* SQL-2003-R */ +%token RTREE_SYM +%token SAVEPOINT_SYM /* SQL-2003-R */ +%token SCHEDULE_SYM +%token SECOND_MICROSECOND_SYM +%token SECOND_SYM /* SQL-2003-R */ +%token SECURITY_SYM /* SQL-2003-N */ +%token SELECT_SYM /* SQL-2003-R */ +%token SENSITIVE_SYM /* FUTURE-USE */ +%token SEPARATOR_SYM +%token SERIALIZABLE_SYM /* SQL-2003-N */ +%token SERIAL_SYM +%token SESSION_SYM /* SQL-2003-N */ +%token SERVER_SYM +%token SERVER_OPTIONS +%token SET /* SQL-2003-R */ +%token SET_VAR +%token SHARE_SYM +%token SHIFT_LEFT /* OPERATOR */ +%token SHIFT_RIGHT /* OPERATOR */ +%token SHOW +%token SHUTDOWN +%token SIGNED_SYM +%token SIMPLE_SYM /* SQL-2003-N */ +%token SLAVE +%token SMALLINT /* SQL-2003-R */ +%token SNAPSHOT_SYM +%token SOCKET_SYM +%token SONAME_SYM +%token SOUNDS_SYM +%token SPATIAL_SYM +%token SPECIFIC_SYM /* SQL-2003-R */ +%token SQLEXCEPTION_SYM /* SQL-2003-R */ +%token SQLSTATE_SYM /* SQL-2003-R */ +%token SQLWARNING_SYM /* SQL-2003-R */ +%token SQL_BIG_RESULT +%token SQL_BUFFER_RESULT +%token SQL_CACHE_SYM +%token SQL_CALC_FOUND_ROWS +%token SQL_NO_CACHE_SYM +%token SQL_SMALL_RESULT +%token SQL_SYM /* SQL-2003-R */ +%token SQL_THREAD +%token SSL_SYM +%token STARTING +%token STARTS_SYM +%token START_SYM /* SQL-2003-R */ +%token STATUS_SYM +%token STDDEV_SAMP_SYM /* SQL-2003-N */ +%token STD_SYM +%token STOP_SYM +%token STORAGE_SYM +%token STRAIGHT_JOIN +%token STRING_SYM +%token SUBDATE_SYM +%token SUBJECT_SYM +%token SUBPARTITIONS_SYM +%token SUBPARTITION_SYM +%token SUBSTRING /* SQL-2003-N */ +%token SUM_SYM /* SQL-2003-N */ +%token SUPER_SYM +%token SUSPEND_SYM +%token SYSDATE +%token TABLES +%token TABLESPACE +%token TABLE_REF_PRIORITY +%token TABLE_SYM /* SQL-2003-R */ +%token TEMPORARY /* SQL-2003-N */ +%token TEMPTABLE_SYM +%token TERMINATED +%token TEXT_STRING +%token TEXT_SYM +%token THAN_SYM +%token THEN_SYM /* SQL-2003-R */ +%token TIMESTAMP /* SQL-2003-R */ +%token TIMESTAMP_ADD +%token TIMESTAMP_DIFF +%token TIME_SYM /* SQL-2003-R */ +%token TINYBLOB +%token TINYINT +%token TINYTEXT +%token TO_SYM /* SQL-2003-R */ +%token TRAILING /* SQL-2003-R */ +%token TRANSACTION_SYM +%token TRIGGERS_SYM +%token TRIGGER_SYM /* SQL-2003-R */ +%token TRIM /* SQL-2003-N */ +%token TRUE_SYM /* SQL-2003-R */ +%token TRUNCATE_SYM +%token TYPES_SYM +%token TYPE_SYM /* SQL-2003-N */ +%token UDF_RETURNS_SYM +%token ULONGLONG_NUM +%token UNCOMMITTED_SYM /* SQL-2003-N */ +%token UNDEFINED_SYM +%token UNDERSCORE_CHARSET +%token UNDOFILE_SYM +%token UNDO_BUFFER_SIZE_SYM +%token UNDO_SYM /* FUTURE-USE */ +%token UNICODE_SYM +%token UNINSTALL_SYM +%token UNION_SYM /* SQL-2003-R */ +%token UNIQUE_SYM +%token UNIQUE_USERS +%token UNKNOWN_SYM /* SQL-2003-R */ +%token UNLOCK_SYM +%token UNSIGNED +%token UNTIL_SYM +%token UPDATE_SYM /* SQL-2003-R */ +%token UPGRADE_SYM +%token USAGE /* SQL-2003-N */ +%token USER /* SQL-2003-R */ +%token USE_FRM +%token USE_SYM +%token USING /* SQL-2003-R */ +%token UTC_DATE_SYM +%token UTC_TIMESTAMP_SYM +%token UTC_TIME_SYM +%token VALUES /* SQL-2003-R */ +%token VALUE_SYM /* SQL-2003-R */ +%token VARBINARY +%token VARCHAR /* SQL-2003-R */ +%token VARIABLES +%token VARIANCE_SYM +%token VARYING /* SQL-2003-R */ +%token VAR_SAMP_SYM +%token VIEW_SYM /* SQL-2003-N */ +%token WAIT_SYM +%token WARNINGS +%token WEEK_SYM +%token WHEN_SYM /* SQL-2003-R */ +%token WHERE /* SQL-2003-R */ +%token WHILE_SYM +%token WITH /* SQL-2003-R */ +%token WORK_SYM /* SQL-2003-N */ +%token WRAPPER_SYM +%token WRITE_SYM /* SQL-2003-N */ +%token X509_SYM +%token XA_SYM +%token XOR +%token YEAR_MONTH_SYM +%token YEAR_SYM /* SQL-2003-R */ +%token ZEROFILL + +%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT +/* A dummy token to force the priority of table_ref production in a join. */ +%left TABLE_REF_PRIORITY +%left SET_VAR +%left OR_OR_SYM OR_SYM OR2_SYM XOR +%left AND_SYM AND_AND_SYM +%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE +%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM +%left '|' +%left '&' +%left SHIFT_LEFT SHIFT_RIGHT +%left '-' '+' +%left '*' '/' '%' DIV_SYM MOD_SYM +%left '^' +%left NEG '~' +%right NOT_SYM NOT2_SYM +%right BINARY COLLATE_SYM + +%type <lex_str> + IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM + LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text + UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal + NCHAR_STRING opt_component key_cache_name + sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty + +%type <lex_str_ptr> + opt_table_alias + +%type <table> + table_ident table_ident_nodb references xid + +%type <simple_string> + remember_name remember_end opt_ident opt_db text_or_password + opt_constraint constraint + +%type <string> + text_string opt_gconcat_separator + +%type <num> + type int_type real_type order_dir lock_option + udf_type if_exists opt_local opt_table_options table_options + table_option opt_if_not_exists opt_no_write_to_binlog + delete_option opt_temporary all_or_any opt_distinct + opt_ignore_leaves fulltext_options spatial_type union_option + start_transaction_opts opt_chain opt_release + union_opt select_derived_init option_type2 + opt_natural_language_mode opt_query_expansion + opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment + ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt + +%type <ulong_num> + ulong_num real_ulong_num merge_insert_types + +%type <ulonglong_number> + ulonglong_num real_ulonglong_num size_number + +%type <p_elem_value> + part_bit_expr + +%type <lock_type> + replace_lock_option opt_low_priority insert_lock_option load_data_lock + +%type <item> + literal text_literal insert_ident order_ident + simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr + variable variable_aux bool_term bool_factor bool_test bool_pri + predicate bit_expr bit_term bit_factor value_expr term factor + table_wild simple_expr udf_expr + expr_or_default set_expr_or_default interval_expr + param_marker geometry_function + signed_literal now_or_signed_literal opt_escape + sp_opt_default + simple_ident_nospvar simple_ident_q + field_or_var limit_option + part_func_expr + function_call_keyword + function_call_nonkeyword + function_call_generic + function_call_conflict + +%type <item_num> + NUM_literal + +%type <item_list> + expr_list udf_expr_list udf_expr_list2 when_list + ident_list ident_list_arg opt_expr_list + +%type <var_type> + option_type opt_var_type opt_var_ident_type + +%type <key_type> + key_type opt_unique_or_fulltext constraint_key_type + +%type <key_alg> + btree_or_rtree + +%type <string_list> + key_usage_list using_list + +%type <key_part> + key_part + +%type <table_list> + join_table_list join_table + table_factor table_ref + select_derived derived_table_list + +%type <date_time_type> date_time_type; +%type <interval> interval + +%type <interval_time_st> interval_time_st + +%type <db_type> storage_engines + +%type <row_type> row_types + +%type <tx_isolation> isolation_types + +%type <ha_rkey_mode> handler_rkey_mode + +%type <cast_type> cast_type + +%type <udf_type> udf_func_type + +%type <symbol> keyword keyword_sp + +%type <lex_user> user grant_user + +%type <charset> + opt_collate + charset_name + charset_name_or_default + old_or_new_charset_name + old_or_new_charset_name_or_default + collation_name + collation_name_or_default + +%type <variable> internal_variable_name + +%type <select_lex> subselect subselect_init + get_select_lex + +%type <boolfunc2creator> comp_op + +%type <NONE> + query verb_clause create change select do drop insert replace insert2 + insert_values update delete truncate rename + show describe load alter optimize keycache preload flush + reset purge begin commit rollback savepoint release + slave master_def master_defs master_file_def slave_until_opts + repair restore backup analyze check start checksum + field_list field_list_item field_spec kill column_def key_def + keycache_list assign_to_keycache preload_list preload_keys + select_item_list select_item values_list no_braces + opt_limit_clause delete_limit_clause fields opt_values values + procedure_list procedure_list2 procedure_item + when_list2 expr_list2 udf_expr_list3 handler + opt_precision opt_ignore opt_column opt_restrict + grant revoke set lock unlock string_list field_options field_option + field_opt_list opt_binary table_lock_list table_lock + ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use + opt_delete_options opt_delete_option varchar nchar nvarchar + opt_outer table_list table_name opt_option opt_place + opt_attribute opt_attribute_list attribute column_list column_list_id + opt_column_list grant_privileges grant_ident grant_list grant_option + object_privilege object_privilege_list user_list rename_list + clear_privileges flush_options flush_option + equal optional_braces opt_key_definition key_usage_list2 + opt_mi_check_type opt_to mi_check_types normal_join + db_to_db table_to_table_list table_to_table opt_table_list opt_as + handler_rkey_function handler_read_or_scan + single_multi table_wild_list table_wild_one opt_wild + union_clause union_list + precision subselect_start opt_and charset + subselect_end select_var_list select_var_list_init help opt_len + opt_extended_describe + prepare prepare_src execute deallocate + statement sp_suid + sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa + load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec + definer view_replace_or_algorithm view_replace view_algorithm_opt + view_algorithm view_or_trigger_or_sp_or_event + view_or_trigger_or_sp_or_event_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail + install uninstall partition_entry binlog_base64_event + init_key_options key_options key_opts key_opt key_using_alg + server_def server_options_list server_option +END_OF_INPUT + +%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt +%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return +%type <NONE> sp_proc_stmt_if sp_proc_stmt_case_simple sp_proc_stmt_case +%type <NONE> sp_labeled_control sp_proc_stmt_unlabeled sp_proc_stmt_leave +%type <NONE> sp_proc_stmt_iterate +%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close + +%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list +%type <spcondtype> sp_cond sp_hcond +%type <spblock> sp_decls sp_decl +%type <lex> sp_cursor_stmt +%type <spname> sp_name + +%type <NONE> + '-' '+' '*' '/' '%' '(' ')' + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM +%% + + +query: + END_OF_INPUT + { + THD *thd= YYTHD; + if (!thd->bootstrap && + (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + { + my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); + YYABORT; + } + else + { + thd->lex->sql_command= SQLCOM_EMPTY_QUERY; + } + } + | verb_clause END_OF_INPUT {}; + +verb_clause: + statement + | begin + ; + +/* Verb clauses, except begin */ +statement: + alter + | analyze + | backup + | binlog_base64_event + | call + | change + | check + | checksum + | commit + | create + | deallocate + | delete + | describe + | do + | drop + | execute + | flush + | grant + | handler + | help + | insert + | install + | kill + | load + | lock + | optimize + | keycache + | partition_entry + | preload + | prepare + | purge + | release + | rename + | repair + | replace + | reset + | restore + | revoke + | rollback + | savepoint + | select + | set + | show + | slave + | start + | truncate + | uninstall + | unlock + | update + | use + | xa + ; + +deallocate: + deallocate_or_drop PREPARE_SYM ident + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (lex->stmt_prepare_mode) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; + lex->prepared_stmt_name= $3; + }; + +deallocate_or_drop: + DEALLOCATE_SYM | + DROP + ; + + +prepare: + PREPARE_SYM ident FROM prepare_src + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (lex->stmt_prepare_mode) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_PREPARE; + lex->prepared_stmt_name= $2; + }; + +prepare_src: + TEXT_STRING_sys + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + lex->prepared_stmt_code= $1; + lex->prepared_stmt_code_is_varref= FALSE; + } + | '@' ident_or_text + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + lex->prepared_stmt_code= $2; + lex->prepared_stmt_code_is_varref= TRUE; + }; + +execute: + EXECUTE_SYM ident + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (lex->stmt_prepare_mode) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_EXECUTE; + lex->prepared_stmt_name= $2; + } + execute_using + {} + ; + +execute_using: + /* nothing */ + | USING execute_var_list + ; + +execute_var_list: + execute_var_list ',' execute_var_ident + | execute_var_ident + ; + +execute_var_ident: '@' ident_or_text + { + LEX *lex=Lex; + LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING)); + if (!lexstr || lex->prepared_stmt_params.push_back(lexstr)) + YYABORT; + } + ; + +/* help */ + +help: + HELP_SYM + { + if (Lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); + YYABORT; + } + } + ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_HELP; + lex->help_arg= $3.str; + }; + +/* change master */ + +change: + CHANGE MASTER_SYM TO_SYM + { + LEX *lex = Lex; + lex->sql_command = SQLCOM_CHANGE_MASTER; + bzero((char*) &lex->mi, sizeof(lex->mi)); + } + master_defs + {} + ; + +master_defs: + master_def + | master_defs ',' master_def; + +master_def: + MASTER_HOST_SYM EQ TEXT_STRING_sys + { + Lex->mi.host = $3.str; + } + | + MASTER_USER_SYM EQ TEXT_STRING_sys + { + Lex->mi.user = $3.str; + } + | + MASTER_PASSWORD_SYM EQ TEXT_STRING_sys + { + Lex->mi.password = $3.str; + } + | + MASTER_PORT_SYM EQ ulong_num + { + Lex->mi.port = $3; + } + | + MASTER_CONNECT_RETRY_SYM EQ ulong_num + { + Lex->mi.connect_retry = $3; + } + | MASTER_SSL_SYM EQ ulong_num + { + Lex->mi.ssl= $3 ? + LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE; + } + | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_ca= $3.str; + } + | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_capath= $3.str; + } + | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cert= $3.str; + } + | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_cipher= $3.str; + } + | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys + { + Lex->mi.ssl_key= $3.str; + } + | + master_file_def + ; + +master_file_def: + MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys + { + Lex->mi.log_file_name = $3.str; + } + | MASTER_LOG_POS_SYM EQ ulonglong_num + { + Lex->mi.pos = $3; + /* + If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it + instead of causing subsequent errors. + We need to do it in this file, because only there we know that + MASTER_LOG_POS has been explicitely specified. On the contrary + in change_master() (sql_repl.cc) we cannot distinguish between 0 + (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified), + whereas we want to distinguish (specified 0 means "read the binlog + from 0" (4 in fact), unspecified means "don't change the position + (keep the preceding value)"). + */ + Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos); + } + | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys + { + Lex->mi.relay_log_name = $3.str; + } + | RELAY_LOG_POS_SYM EQ ulong_num + { + Lex->mi.relay_log_pos = $3; + /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ + Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + } + ; + +/* create a table */ + +create: + CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident + { + THD *thd= YYTHD; + LEX *lex=Lex; + lex->sql_command= SQLCOM_CREATE_TABLE; + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + (using_update_log ? + TL_READ_NO_INSERT: + TL_READ))) + YYABORT; + lex->create_list.empty(); + lex->key_list.empty(); + lex->col_list.empty(); + lex->change=NullS; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + lex->create_info.options=$2 | $4; + lex->create_info.db_type= lex->thd->variables.table_type; + lex->create_info.default_table_charset= NULL; + lex->name.str= 0; + lex->name.length= 0; + lex->like_name= 0; + } + create2 + { Lex->current_select= &Lex->select_lex; } + | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON + table_ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_CREATE_INDEX; + if (!lex->current_select->add_table_to_list(lex->thd, $7, + NULL, + TL_OPTION_UPDATING)) + YYABORT; + lex->create_list.empty(); + lex->key_list.empty(); + lex->col_list.empty(); + lex->change=NullS; + } + '(' key_list ')' key_options + { + LEX *lex=Lex; + if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->key_list.push_back(new Key($2, $4.str, &lex->key_create_info, 0, + lex->col_list)); + lex->col_list.empty(); + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + opt_create_database_options + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CREATE_DB; + lex->name= $4; + lex->create_info.options=$3; + } + | CREATE + { + Lex->create_view_mode= VIEW_CREATE_NEW; + Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + Lex->create_view_suid= TRUE; + } + view_or_trigger_or_sp_or_event + {} + | CREATE USER clear_privileges grant_list + { + Lex->sql_command = SQLCOM_CREATE_USER; + } + | CREATE LOGFILE_SYM GROUP logfile_group_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; + } + | CREATE TABLESPACE tablespace_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; + } + | CREATE server_def + { + Lex->sql_command= SQLCOM_CREATE_SERVER; + } + ; +server_def: + SERVER_SYM ident_or_text FOREIGN DATA_SYM WRAPPER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + Lex->server_options.server_name= $2.str; + Lex->server_options.server_name_length= $2.length; + Lex->server_options.scheme= $6.str; + } + ; + +server_options_list: + server_option + | server_options_list ',' server_option + ; + +server_option: + USER TEXT_STRING_sys + { + Lex->server_options.username= $2.str; + } + | + HOST_SYM TEXT_STRING_sys + { + Lex->server_options.host= $2.str; + } + | + DATABASE TEXT_STRING_sys + { + Lex->server_options.db= $2.str; + } + | + OWNER_SYM TEXT_STRING_sys + { + Lex->server_options.owner= $2.str; + } + | + PASSWORD TEXT_STRING_sys + { + Lex->server_options.password= $2.str; + } + | + SOCKET_SYM TEXT_STRING_sys + { + Lex->server_options.socket= $2.str; + } + | + PORT_SYM ulong_num + { + Lex->server_options.port= $2; + } + ; + +event_tail: + EVENT_SYM opt_if_not_exists sp_name + /* + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value + */ + { + Lex->create_info.options= $2; + + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + YYABORT; + Lex->event_parse_data->identifier= $3; + + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + + Lex->sql_command= SQLCOM_CREATE_EVENT; + /* We need that for disallowing subqueries */ + } + ON SCHEDULE_SYM ev_schedule_time + opt_ev_on_completion + opt_ev_status + opt_ev_comment + DO_SYM ev_sql_stmt + { + /* + Restore flag if it was cleared above + $1 - EVENT_SYM + $2 - opt_if_not_exists + $3 - sp_name + $4 - the block above + */ + YYTHD->client_capabilities |= $<ulong_num>4; + + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_CREATE_EVENT; + } + ; + +ev_schedule_time: EVERY_SYM expr interval + { + Lex->event_parse_data->item_expression= $2; + Lex->event_parse_data->interval= $3; + } + ev_starts + ev_ends + | AT_SYM expr + { + Lex->event_parse_data->item_execute_at= $2; + } + ; + +opt_ev_status: /* empty */ { $$= 0; } + | ENABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::ENABLED; + $$= 1; + } + | DISABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::DISABLED; + $$= 1; + } + ; + +ev_starts: /* empty */ + { + Lex->event_parse_data->item_starts= new Item_func_now_local(); + } + | STARTS_SYM expr + { + Lex->event_parse_data->item_starts= $2; + } + ; + +ev_ends: /* empty */ + | ENDS_SYM expr + { + Lex->event_parse_data->item_ends= $2; + } + ; + +opt_ev_on_completion: /* empty */ { $$= 0; } + | ev_on_completion + ; + +ev_on_completion: + ON COMPLETION_SYM PRESERVE_SYM + { + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_PRESERVE; + $$= 1; + } + | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM + { + Lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; + $$= 1; + } + ; + +opt_ev_comment: /* empty */ { $$= 0; } + | COMMENT_SYM TEXT_STRING_sys + { + Lex->comment= Lex->event_parse_data->comment= $2; + $$= 1; + } + ; + +ev_sql_stmt: + { + LEX *lex= Lex; + + /* + This stops the following : + - CREATE EVENT ... DO CREATE EVENT ...; + - ALTER EVENT ... DO CREATE EVENT ...; + - CREATE EVENT ... DO ALTER EVENT DO ....; + - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| + This allows: + - CREATE EVENT ... DO DROP EVENT yyy; + - CREATE EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO DROP EVENT yyy; + - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END| + (the nested ALTER EVENT can have anything but DO clause) + - CREATE PROCEDURE ... BEGIN DROP EVENT ... END| + */ + if (lex->sphead) + { + my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); + YYABORT; + } + + if (!(lex->sphead= new sp_head())) + YYABORT; + + lex->sphead->reset_thd_mem_root(YYTHD); + lex->sphead->init(lex); + lex->sphead->init_sp_name(YYTHD, Lex->event_parse_data->identifier); + + lex->sphead->m_type= TYPE_ENUM_PROCEDURE; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + + lex->sphead->m_body_begin= lex->ptr; + + Lex->event_parse_data->body_begin= lex->ptr; + + } + ev_sql_stmt_inner + { + LEX *lex=Lex; + + /* return back to the original memory root ASAP */ + lex->sphead->init_strings(YYTHD, lex); + lex->sphead->restore_thd_mem_root(YYTHD); + + lex->sp_chistics.suid= SP_IS_SUID; //always the definer! + + Lex->event_parse_data->init_body(YYTHD); + } + ; + +ev_sql_stmt_inner: + sp_proc_stmt_statement + | sp_proc_stmt_return + | sp_proc_stmt_if + | sp_proc_stmt_case_simple + | sp_proc_stmt_case + | sp_labeled_control + | sp_proc_stmt_unlabeled + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + ; + + +clear_privileges: + /* Nothing */ + { + LEX *lex=Lex; + lex->users_list.empty(); + lex->columns.empty(); + lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; + lex->select_lex.db= 0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero((char *)&(lex->mqh),sizeof(lex->mqh)); + } + ; + +sp_name: + ident '.' ident + { + if (!$1.str || check_db_name(&$1)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); + YYABORT; + } + if (check_routine_name($3)) + { + my_error(ER_SP_WRONG_NAME, MYF(0), $3.str); + YYABORT; + } + $$= new sp_name($1, $3); + $$->init_qname(YYTHD); + } + | ident + { + THD *thd= YYTHD; + LEX_STRING db; + if (check_routine_name($1)) + { + my_error(ER_SP_WRONG_NAME, MYF(0), $1.str); + YYABORT; + } + if (thd->copy_db_to(&db.str, &db.length)) + YYABORT; + $$= new sp_name(db, $1); + if ($$) + $$->init_qname(YYTHD); + } + ; + +create_function_tail: + RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys + { + THD *thd= YYTHD; + LEX *lex=Lex; + if (lex->definer != NULL) + { + /* + DEFINER is a concept meaningful when interpreting SQL code. + UDF functions are compiled. + Using DEFINER with UDF has therefore no semantic, + and is considered a parsing error. + */ + my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); + YYABORT; + } + if (is_native_function(thd, & lex->spname->m_name)) + { + my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), + lex->spname->m_name.str); + YYABORT; + } + lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.name = lex->spname->m_name; + lex->udf.returns=(Item_result) $2; + lex->udf.dl=$4.str; + } + | '(' + { + LEX *lex= Lex; + sp_head *sp; + + /* + First check if AGGREGATE was used, in that case it's a + syntax error. + */ + if (lex->udf.type == UDFTYPE_AGGREGATE) + { + my_error(ER_SP_NO_AGGREGATE, MYF(0)); + YYABORT; + } + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); + YYABORT; + } + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + sp->init_sp_name(YYTHD, lex->spname); + + sp->m_type= TYPE_ENUM_FUNCTION; + lex->sphead= sp; + /* + We have to turn off CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_fdparam_list ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + } + RETURNS_SYM + { + LEX *lex= Lex; + lex->charset= NULL; + lex->length= lex->dec= NULL; + lex->interval_list.empty(); + lex->type= 0; + } + type + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + /* + This was disabled in 5.1.12. See bug #20701 + When collation support in SP is implemented, then this test + should be removed. + */ + if (($8 == FIELD_TYPE_STRING || $8 == MYSQL_TYPE_VARCHAR) + && (lex->type & BINCMP_FLAG)) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation"); + YYABORT; + } + + if (sp->fill_field_definition(YYTHD, lex, + (enum enum_field_types) $8, + &sp->m_return_field_def)) + YYABORT; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + THD *thd= YYTHD; + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->is_not_allowed_in_function("function")) + YYABORT; + + lex->sql_command= SQLCOM_CREATE_SPFUNCTION; + sp->init_strings(thd, lex); + if (!(sp->m_flags & sp_head::HAS_RETURN)) + { + my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); + YYABORT; + } + if (is_native_function(thd, & sp->m_name)) + { + /* + This warning will be printed when + [1] A client query is parsed, + [2] A stored function is loaded by db_load_routine. + Printing the warning for [2] is intentional, to cover the + following scenario: + - A user define a SF 'foo' using MySQL 5.N + - An application uses select foo(), and works. + - MySQL 5.{N+1} defines a new native function 'foo', as + part of a new feature. + - MySQL 5.{N+1} documentation is updated, and should mention + that there is a potential incompatible change in case of + existing stored function named 'foo'. + - The user deploys 5.{N+1}. At this point, 'select foo()' + means something different, and the user code is most likely + broken (it's only safe if the code is 'select db.foo()'). + With a warning printed when the SF is loaded (which has to occur + before the call), the warning will provide a hint explaining + the root cause of a later failure of 'select foo()'. + With no warning printed, the user code will fail with no + apparent reason. + Printing a warning each time db_load_routine is executed for + an ambiguous function is annoying, since that can happen a lot, + but in practice should not happen unless there *are* name + collisions. + If a collision exists, it should not be silenced but fixed. + */ + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NATIVE_FCT_NAME_COLLISION, + ER(ER_NATIVE_FCT_NAME_COLLISION), + sp->m_name.str); + } + /* Restore flag if it was cleared above */ + thd->client_capabilities |= $<ulong_num>2; + sp->restore_thd_mem_root(thd); + } + ; + +sp_a_chistics: + /* Empty */ {} + | sp_a_chistics sp_chistic {} + ; + +sp_c_chistics: + /* Empty */ {} + | sp_c_chistics sp_c_chistic {} + ; + +/* Characteristics for both create and alter */ +sp_chistic: + COMMENT_SYM TEXT_STRING_sys + { Lex->sp_chistics.comment= $2; } + | LANGUAGE_SYM SQL_SYM + { /* Just parse it, we only have one language for now. */ } + | NO_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_NO_SQL; } + | CONTAINS_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; } + | READS_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; } + | MODIFIES_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; } + | sp_suid + { } + ; + +/* Create characteristics */ +sp_c_chistic: + sp_chistic { } + | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; } + | not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; } + ; + +sp_suid: + SQL_SYM SECURITY_SYM DEFINER_SYM + { + Lex->sp_chistics.suid= SP_IS_SUID; + } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { + Lex->sp_chistics.suid= SP_IS_NOT_SUID; + } + ; + +call: + CALL_SYM sp_name + { + LEX *lex = Lex; + + lex->sql_command= SQLCOM_CALL; + lex->spname= $2; + lex->value_list.empty(); + sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE); + } + opt_sp_cparam_list {} + ; + +/* CALL parameters */ +opt_sp_cparam_list: + /* Empty */ + | '(' opt_sp_cparams ')' + ; + +opt_sp_cparams: + /* Empty */ + | sp_cparams + ; + +sp_cparams: + sp_cparams ',' expr + { + Lex->value_list.push_back($3); + } + | expr + { + Lex->value_list.push_back($1); + } + ; + +/* Stored FUNCTION parameter declaration list */ +sp_fdparam_list: + /* Empty */ + | sp_fdparams + ; + +sp_fdparams: + sp_fdparams ',' sp_fdparam + | sp_fdparam + ; + +sp_init_param: + /* Empty */ + { + LEX *lex= Lex; + + lex->length= 0; + lex->dec= 0; + lex->type= 0; + + lex->default_value= 0; + lex->on_update_value= 0; + + lex->comment= null_lex_str; + lex->charset= NULL; + + lex->interval_list.empty(); + lex->uint_geom_type= 0; + } + ; + +sp_fdparam: + ident sp_init_param type + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$1, TRUE)) + { + my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); + YYABORT; + } + sp_variable_t *spvar= spc->push_variable(&$1, + (enum enum_field_types)$3, + sp_param_in); + + if (lex->sphead->fill_field_definition(YYTHD, lex, + (enum enum_field_types) $3, + &spvar->field_def)) + { + YYABORT; + } + spvar->field_def.field_name= spvar->name.str; + spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; + } + ; + +/* Stored PROCEDURE parameter declaration list */ +sp_pdparam_list: + /* Empty */ + | sp_pdparams + ; + +sp_pdparams: + sp_pdparams ',' sp_pdparam + | sp_pdparam + ; + +sp_pdparam: + sp_opt_inout sp_init_param ident type + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$3, TRUE)) + { + my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); + YYABORT; + } + sp_variable_t *spvar= spc->push_variable(&$3, + (enum enum_field_types)$4, + (sp_param_mode_t)$1); + + if (lex->sphead->fill_field_definition(YYTHD, lex, + (enum enum_field_types) $4, + &spvar->field_def)) + { + YYABORT; + } + spvar->field_def.field_name= spvar->name.str; + spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; + } + ; + +sp_opt_inout: + /* Empty */ { $$= sp_param_in; } + | IN_SYM { $$= sp_param_in; } + | OUT_SYM { $$= sp_param_out; } + | INOUT_SYM { $$= sp_param_inout; } + ; + +sp_proc_stmts: + /* Empty */ {} + | sp_proc_stmts sp_proc_stmt ';' + ; + +sp_proc_stmts1: + sp_proc_stmt ';' {} + | sp_proc_stmts1 sp_proc_stmt ';' + ; + +sp_decls: + /* Empty */ + { + $$.vars= $$.conds= $$.hndlrs= $$.curs= 0; + } + | sp_decls sp_decl ';' + { + /* We check for declarations out of (standard) order this way + because letting the grammar rules reflect it caused tricky + shift/reduce conflicts with the wrong result. (And we get + better error handling this way.) */ + if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs)) + { /* Variable or condition following cursor or handler */ + my_message(ER_SP_VARCOND_AFTER_CURSHNDLR, + ER(ER_SP_VARCOND_AFTER_CURSHNDLR), MYF(0)); + YYABORT; + } + if ($2.curs && $1.hndlrs) + { /* Cursor following handler */ + my_message(ER_SP_CURSOR_AFTER_HANDLER, + ER(ER_SP_CURSOR_AFTER_HANDLER), MYF(0)); + YYABORT; + } + $$.vars= $1.vars + $2.vars; + $$.conds= $1.conds + $2.conds; + $$.hndlrs= $1.hndlrs + $2.hndlrs; + $$.curs= $1.curs + $2.curs; + } + ; + +sp_decl: + DECLARE_SYM sp_decl_idents + { + LEX *lex= Lex; + + lex->sphead->reset_lex(YYTHD); + lex->spcont->declare_var_boundary($2); + } + type + sp_opt_default + { + LEX *lex= Lex; + sp_pcontext *pctx= lex->spcont; + uint num_vars= pctx->context_var_count(); + enum enum_field_types var_type= (enum enum_field_types) $4; + Item *dflt_value_item= $5; + create_field *create_field_op; + + if (!dflt_value_item) + { + dflt_value_item= new Item_null(); + /* QQ Set to the var_type with null_value? */ + } + + for (uint i = num_vars-$2 ; i < num_vars ; i++) + { + uint var_idx= pctx->var_context2runtime(i); + sp_variable_t *spvar= pctx->find_variable(var_idx); + + if (!spvar) + YYABORT; + + spvar->type= var_type; + spvar->dflt= dflt_value_item; + + if (lex->sphead->fill_field_definition(YYTHD, lex, var_type, + &spvar->field_def)) + { + YYABORT; + } + + spvar->field_def.field_name= spvar->name.str; + spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; + + /* The last instruction is responsible for freeing LEX. */ + + lex->sphead->add_instr( + new sp_instr_set(lex->sphead->instructions(), pctx, var_idx, + dflt_value_item, var_type, lex, + (i == num_vars - 1))); + } + + pctx->declare_var_boundary(0); + lex->sphead->restore_lex(YYTHD); + + $$.vars= $2; + $$.conds= $$.hndlrs= $$.curs= 0; + } + | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_cond(&$2, TRUE)) + { + my_error(ER_SP_DUP_COND, MYF(0), $2.str); + YYABORT; + } + YYTHD->lex->spcont->push_cond(&$2, $5); + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_instr_hpush_jump *i= + new sp_instr_hpush_jump(sp->instructions(), ctx, $2, + ctx->current_var_count()); + + sp->add_instr(i); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->m_flags|= sp_head::IN_HANDLER; + } + sp_hcond_list sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */ + sp_instr_hreturn *i; + + if ($2 == SP_HANDLER_CONTINUE) + { + i= new sp_instr_hreturn(sp->instructions(), ctx, + ctx->current_var_count()); + sp->add_instr(i); + } + else + { /* EXIT or UNDO handler, just jump to the end of the block */ + i= new sp_instr_hreturn(sp->instructions(), ctx, 0); + + sp->add_instr(i); + sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ + } + lex->sphead->backpatch(hlab); + sp->m_flags&= ~sp_head::IN_HANDLER; + $$.vars= $$.conds= $$.curs= 0; + $$.hndlrs= $6; + ctx->add_handlers($6); + } + | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint offp; + sp_instr_cpush *i; + + if (ctx->find_cursor(&$2, &offp, TRUE)) + { + my_error(ER_SP_DUP_CURS, MYF(0), $2.str); + delete $5; + YYABORT; + } + i= new sp_instr_cpush(sp->instructions(), ctx, $5, + ctx->current_cursor_count()); + sp->add_instr(i); + ctx->push_cursor(&$2); + $$.vars= $$.conds= $$.hndlrs= 0; + $$.curs= 1; + } + ; + +sp_cursor_stmt: + { + Lex->sphead->reset_lex(YYTHD); + + /* + We use statement here just be able to get a better + error message. Using 'select' works too, but will then + result in a generic "syntax error" if a non-select + statement is given. + */ + } + statement + { + LEX *lex= Lex; + + if (lex->sql_command != SQLCOM_SELECT && + !(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND)) + { + my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY), + MYF(0)); + YYABORT; + } + if (lex->result) + { + my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), + MYF(0)); + YYABORT; + } + lex->sp_lex_in_use= TRUE; + $$= lex; + lex->sphead->restore_lex(YYTHD); + } + ; + +sp_handler_type: + EXIT_SYM { $$= SP_HANDLER_EXIT; } + | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; } +/* | UNDO_SYM { QQ No yet } */ + ; + +sp_hcond_list: + sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + + if (ctx->find_handler($1)) + { + my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); + YYABORT; + } + else + { + sp_instr_hpush_jump *i= + (sp_instr_hpush_jump *)sp->last_instruction(); + + i->add_condition($1); + ctx->push_handler($1); + $$= 1; + } + } + | sp_hcond_list ',' sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + + if (ctx->find_handler($3)) + { + my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); + YYABORT; + } + else + { + sp_instr_hpush_jump *i= + (sp_instr_hpush_jump *)sp->last_instruction(); + + i->add_condition($3); + ctx->push_handler($3); + $$= $1 + 1; + } + } + ; + +sp_cond: + ulong_num + { /* mysql errno */ + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::number; + $$->mysqlerr= $1; + } + | SQLSTATE_SYM opt_value TEXT_STRING_literal + { /* SQLSTATE */ + if (!sp_cond_check(&$3)) + { + my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); + YYABORT; + } + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::state; + memcpy($$->sqlstate, $3.str, 5); + $$->sqlstate[5]= '\0'; + } + ; + +opt_value: + /* Empty */ {} + | VALUE_SYM {} + ; + +sp_hcond: + sp_cond + { + $$= $1; + } + | ident /* CONDITION name */ + { + $$= Lex->spcont->find_cond(&$1); + if ($$ == NULL) + { + my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); + YYABORT; + } + } + | SQLWARNING_SYM /* SQLSTATEs 01??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::warning; + } + | not FOUND_SYM /* SQLSTATEs 02??? */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::notfound; + } + | SQLEXCEPTION_SYM /* All other SQLSTATEs */ + { + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::exception; + } + ; + +sp_decl_idents: + ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$1, TRUE)) + { + my_error(ER_SP_DUP_VAR, MYF(0), $1.str); + YYABORT; + } + spc->push_variable(&$1, (enum_field_types)0, sp_param_in); + $$= 1; + } + | sp_decl_idents ',' ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$3, TRUE)) + { + my_error(ER_SP_DUP_VAR, MYF(0), $3.str); + YYABORT; + } + spc->push_variable(&$3, (enum_field_types)0, sp_param_in); + $$= $1 + 1; + } + ; + +sp_opt_default: + /* Empty */ { $$ = NULL; } + | DEFAULT expr { $$ = $2; } + ; + +sp_proc_stmt: + sp_proc_stmt_statement + | sp_proc_stmt_return + | sp_proc_stmt_if + | sp_proc_stmt_case_simple + | sp_proc_stmt_case + | sp_labeled_control + | sp_proc_stmt_unlabeled + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + ; + +sp_proc_stmt_if: + IF { Lex->sphead->new_cont_backpatch(NULL); } + sp_if END IF + { Lex->sphead->do_cont_backpatch(); } + ; + +sp_proc_stmt_statement: + { + LEX *lex= Lex; + + lex->sphead->reset_lex(YYTHD); + lex->sphead->m_tmp_query= lex->tok_start; + } + statement + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + sp->m_flags|= sp_get_flags_for_command(lex); + if (lex->sql_command == SQLCOM_CHANGE_DB) + { /* "USE db" doesn't work in a procedure */ + my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); + YYABORT; + } + /* + Don't add an instruction for SET statements, since all + instructions for them were already added during processing + of "set" rule. + */ + DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION || + lex->var_list.is_empty()); + if (lex->sql_command != SQLCOM_SET_OPTION) + { + sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), + lex->spcont, lex); + + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) + i->m_query.length= lex->ptr - sp->m_tmp_query; + else + i->m_query.length= lex->tok_end - sp->m_tmp_query; + i->m_query.str= strmake_root(YYTHD->mem_root, + (char *)sp->m_tmp_query, + i->m_query.length); + sp->add_instr(i); + } + sp->restore_lex(YYTHD); + } + ; + +sp_proc_stmt_return: + RETURN_SYM + { Lex->sphead->reset_lex(YYTHD); } + expr + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->m_type != TYPE_ENUM_FUNCTION) + { + my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0)); + YYABORT; + } + else + { + sp_instr_freturn *i; + + i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3, + sp->m_return_field_def.sql_type, lex); + sp->add_instr(i); + sp->m_flags|= sp_head::HAS_RETURN; + } + sp->restore_lex(YYTHD); + } + ; + +sp_proc_stmt_case_simple: + CASE_SYM WHEN_SYM + { + Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE; + Lex->sphead->new_cont_backpatch(NULL); + } + sp_case END CASE_SYM { Lex->sphead->do_cont_backpatch(); } + ; + +sp_proc_stmt_case: + CASE_SYM + { + Lex->sphead->reset_lex(YYTHD); + Lex->sphead->new_cont_backpatch(NULL); + } + expr WHEN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *parsing_ctx= lex->spcont; + int case_expr_id= parsing_ctx->register_case_expr(); + sp_instr_set_case_expr *i; + + if (parsing_ctx->push_case_expr_id(case_expr_id)) + YYABORT; + + i= new sp_instr_set_case_expr(sp->instructions(), + parsing_ctx, + case_expr_id, + $3, + lex); + sp->add_cont_backpatch(i); + sp->add_instr(i); + sp->m_flags|= sp_head::IN_SIMPLE_CASE; + sp->restore_lex(YYTHD); + } + sp_case END CASE_SYM + { + Lex->spcont->pop_case_expr_id(); + Lex->sphead->do_cont_backpatch(); + } + ; + +sp_proc_stmt_unlabeled: + { /* Unlabeled controls get a secret label. */ + LEX *lex= Lex; + + lex->spcont->push_label((char *)"", lex->sphead->instructions()); + } + sp_unlabeled_control + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_proc_stmt_leave: + LEAVE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp = lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); + + if (! lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str); + YYABORT; + } + else + { + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx, TRUE); /* Exclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); + n= ctx->diff_cursors(lab->ctx, TRUE); /* Exclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); + i= new sp_instr_jump(ip, ctx); + sp->push_backpatch(i, lab); /* Jumping forward */ + sp->add_instr(i); + } + } + ; + +sp_proc_stmt_iterate: + ITERATE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); + + if (! lab || lab->type != SP_LAB_ITER) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); + YYABORT; + } + else + { + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); + n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); + i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ + sp->add_instr(i); + } + } + ; + +sp_proc_stmt_open: + OPEN_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_copen *i; + + if (! lex->spcont->find_cursor(&$2, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); + YYABORT; + } + i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } + ; + +sp_proc_stmt_fetch: + FETCH_SYM sp_opt_fetch_noise ident INTO + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cfetch *i; + + if (! lex->spcont->find_cursor(&$3, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); + YYABORT; + } + i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } + sp_fetch_list + { } + ; + +sp_proc_stmt_close: + CLOSE_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cclose *i; + + if (! lex->spcont->find_cursor(&$2, &offset)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); + YYABORT; + } + i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); + sp->add_instr(i); + } + ; + +sp_opt_fetch_noise: + /* Empty */ + | NEXT_SYM FROM + | FROM + ; + +sp_fetch_list: + ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + if (!spc || !(spv = spc->find_variable(&$1))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); + YYABORT; + } + else + { + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + + i->add_to_varlist(spv); + } + } + | + sp_fetch_list ',' ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + if (!spc || !(spv = spc->find_variable(&$3))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); + YYABORT; + } + else + { + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + + i->add_to_varlist(spv); + } + } + ; + +sp_if: + { Lex->sphead->reset_lex(YYTHD); } + expr THEN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, + $2, lex); + + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->add_cont_backpatch(i); + sp->add_instr(i); + sp->restore_lex(YYTHD); + } + sp_proc_stmts1 + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i = new sp_instr_jump(ip, ctx); + + sp->add_instr(i); + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_elseifs + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_elseifs: + /* Empty */ + | ELSEIF_SYM sp_if + | ELSE sp_proc_stmts1 + ; + +sp_case: + { Lex->sphead->reset_lex(YYTHD); } + expr THEN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i; + + if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE)) + i= new sp_instr_jump_if_not(ip, ctx, $2, lex); + else + { /* Simple case: <caseval> = <whenval> */ + + Item_case_expr *var; + Item *expr; + + var= new Item_case_expr(ctx->get_current_case_expr_id()); + +#ifndef DBUG_OFF + if (var) + var->m_sp= sp; +#endif + + expr= new Item_func_eq(var, $2); + + i= new sp_instr_jump_if_not(ip, ctx, expr, lex); + } + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + sp->add_cont_backpatch(i); + sp->add_instr(i); + sp->restore_lex(YYTHD); + } + sp_proc_stmts1 + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i = new sp_instr_jump(ip, ctx); + + sp->add_instr(i); + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + } + sp_whens + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_whens: + /* Empty */ + { + sp_head *sp= Lex->sphead; + uint ip= sp->instructions(); + sp_instr_error *i= new sp_instr_error(ip, Lex->spcont, + ER_SP_CASE_NOT_FOUND); + + sp->add_instr(i); + } + | ELSE sp_proc_stmts1 {} + | WHEN_SYM sp_case {} + ; + +sp_labeled_control: + label_ident ':' + { + LEX *lex= Lex; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($1.str); + + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); + YYABORT; + } + else + { + lab= lex->spcont->push_label($1.str, + lex->sphead->instructions()); + lab->type= SP_LAB_ITER; + } + } + sp_unlabeled_control sp_opt_label + { + LEX *lex= Lex; + + if ($5.str) + { + sp_label_t *lab= lex->spcont->find_label($5.str); + + if (!lab || + my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) + { + my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); + YYABORT; + } + } + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_opt_label: + /* Empty */ { $$= null_lex_str; } + | label_ident { $$= $1; } + ; + +sp_unlabeled_control: + BEGIN_SYM + { /* QQ This is just a dummy for grouping declarations and statements + together. No [[NOT] ATOMIC] yet, and we need to figure out how + make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ + LEX *lex= Lex; + sp_label_t *lab= lex->spcont->last_label(); + + lab->type= SP_LAB_BEGIN; + lex->spcont= lex->spcont->push_context(); + } + sp_decls + sp_proc_stmts + END + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + + sp->backpatch(ctx->last_label()); /* We always have a label */ + if ($3.hndlrs) + sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx, + $3.hndlrs)); + if ($3.curs) + sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx, + $3.curs)); + lex->spcont= ctx->pop_context(); + } + | LOOP_SYM + sp_proc_stmts1 END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); + + lex->sphead->add_instr(i); + } + | WHILE_SYM + { Lex->sphead->reset_lex(YYTHD); } + expr DO_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + $3, lex); + + /* Jumping forward */ + sp->push_backpatch(i, lex->spcont->last_label()); + sp->new_cont_backpatch(i); + sp->add_instr(i); + sp->restore_lex(YYTHD); + } + sp_proc_stmts1 END WHILE_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); + + lex->sphead->add_instr(i); + lex->sphead->do_cont_backpatch(); + } + | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM + { Lex->sphead->reset_lex(YYTHD); } + expr END REPEAT_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + $5, lab->ip, + lex); + lex->sphead->add_instr(i); + lex->sphead->restore_lex(YYTHD); + /* We can shortcut the cont_backpatch here */ + i->m_cont_dest= ip+1; + } + ; + +trg_action_time: + BEFORE_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; } + | AFTER_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; } + ; + +trg_event: + INSERT + { Lex->trg_chistics.event= TRG_EVENT_INSERT; } + | UPDATE_SYM + { Lex->trg_chistics.event= TRG_EVENT_UPDATE; } + | DELETE_SYM + { Lex->trg_chistics.event= TRG_EVENT_DELETE; } + ; +/* + This part of the parser contains common code for all TABLESPACE + commands. + CREATE TABLESPACE name ... + ALTER TABLESPACE name CHANGE DATAFILE ... + ALTER TABLESPACE name ADD DATAFILE ... + ALTER TABLESPACE name access_mode + CREATE LOGFILE GROUP name ... + ALTER LOGFILE GROUP name ADD UNDOFILE .. + ALTER LOGFILE GROUP name ADD REDOFILE .. + DROP TABLESPACE name + DROP LOGFILE GROUP name +*/ +change_tablespace_access: + tablespace_name + ts_access_mode + ; + +change_tablespace_info: + tablespace_name + CHANGE ts_datafile + change_ts_option_list + ; + +tablespace_info: + tablespace_name + ADD ts_datafile + opt_logfile_group_name + tablespace_option_list + ; + +opt_logfile_group_name: + /* empty */ {} + | USE_SYM LOGFILE_SYM GROUP ident + { + LEX *lex= Lex; + lex->alter_tablespace_info->logfile_group_name= $4.str; + }; + +alter_tablespace_info: + tablespace_name + ADD ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE; + } + | + tablespace_name + DROP ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE; + }; + +logfile_group_info: + logfile_group_name + add_log_file + logfile_group_option_list + ; + +alter_logfile_group_info: + logfile_group_name + add_log_file + alter_logfile_group_option_list + ; + +add_log_file: + ADD lg_undofile + | ADD lg_redofile + ; + +change_ts_option_list: + /* empty */ {} + change_ts_options + ; + +change_ts_options: + change_ts_option + | change_ts_options change_ts_option + | change_ts_options ',' change_ts_option + ; + +change_ts_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + ; + +tablespace_option_list: + /* empty */ {} + tablespace_options + ; + +tablespace_options: + tablespace_option + | tablespace_options tablespace_option + | tablespace_options ',' tablespace_option + ; + +tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_extent_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_tablespace_option_list: + /* empty */ {} + alter_tablespace_options + ; + +alter_tablespace_options: + alter_tablespace_option + | alter_tablespace_options alter_tablespace_option + | alter_tablespace_options ',' alter_tablespace_option + ; + +alter_tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_engine + | ts_wait + ; + +logfile_group_option_list: + /* empty */ {} + logfile_group_options + ; + +logfile_group_options: + logfile_group_option + | logfile_group_options logfile_group_option + | logfile_group_options ',' logfile_group_option + ; + +logfile_group_option: + opt_ts_initial_size + | opt_ts_undo_buffer_size + | opt_ts_redo_buffer_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_logfile_group_option_list: + /* empty */ {} + alter_logfile_group_options + ; + +alter_logfile_group_options: + alter_logfile_group_option + | alter_logfile_group_options alter_logfile_group_option + | alter_logfile_group_options ',' alter_logfile_group_option + ; + +alter_logfile_group_option: + opt_ts_initial_size + | opt_ts_engine + | ts_wait + ; + + +ts_datafile: + DATAFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->data_file_name= $2.str; + }; + +lg_undofile: + UNDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_file_name= $2.str; + }; + +lg_redofile: + REDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_file_name= $2.str; + }; + +tablespace_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= new st_alter_tablespace(); + lex->alter_tablespace_info->tablespace_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + }; + +logfile_group_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= new st_alter_tablespace(); + lex->alter_tablespace_info->logfile_group_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + }; + +ts_access_mode: + READ_ONLY_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY; + } + | READ_WRITE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE; + } + | NOT_SYM ACCESSIBLE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE; + }; + +opt_ts_initial_size: + INITIAL_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->initial_size= $3; + }; + +opt_ts_autoextend_size: + AUTOEXTEND_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->autoextend_size= $3; + }; + +opt_ts_max_size: + MAX_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->max_size= $3; + }; + +opt_ts_extent_size: + EXTENT_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->extent_size= $3; + }; + +opt_ts_undo_buffer_size: + UNDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_buffer_size= $3; + }; + +opt_ts_redo_buffer_size: + REDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_buffer_size= $3; + }; + +opt_ts_nodegroup: + NODEGROUP_SYM opt_equal real_ulong_num + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP) + { + my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP"); + YYABORT; + } + lex->alter_tablespace_info->nodegroup_id= $3; + }; + +opt_ts_comment: + COMMENT_SYM opt_equal TEXT_STRING_sys + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->ts_comment != NULL) + { + my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT"); + YYABORT; + } + lex->alter_tablespace_info->ts_comment= $3.str; + }; + +opt_ts_engine: + opt_storage ENGINE_SYM opt_equal storage_engines + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->storage_engine != NULL) + { + my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0), + "STORAGE ENGINE"); + YYABORT; + } + lex->alter_tablespace_info->storage_engine= $4; + }; + +opt_ts_wait: + /* empty */ + | ts_wait + ; + +ts_wait: + WAIT_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->wait_until_completed= TRUE; + } + | NO_WAIT_SYM + { + LEX *lex= Lex; + if (!(lex->alter_tablespace_info->wait_until_completed)) + { + my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT"); + YYABORT; + } + lex->alter_tablespace_info->wait_until_completed= FALSE; + }; + +size_number: + real_ulong_num { $$= $1;} + | IDENT + { + ulonglong number, test_number; + uint text_shift_number= 0; + longlong prefix_number; + char *start_ptr= $1.str; + uint str_len= $1.length; + char *end_ptr= start_ptr + str_len; + int error; + prefix_number= my_strtoll10(start_ptr, &end_ptr, &error); + if ((start_ptr + str_len - 1) == end_ptr) + { + switch (end_ptr[0]) + { + case 'g': + case 'G': + text_shift_number+=10; + case 'm': + case 'M': + text_shift_number+=10; + case 'k': + case 'K': + text_shift_number+=10; + break; + default: + { + my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); + YYABORT; + } + } + if (prefix_number >> 31) + { + my_error(ER_SIZE_OVERFLOW_ERROR, MYF(0)); + YYABORT; + } + number= prefix_number << text_shift_number; + } + else + { + my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); + YYABORT; + } + $$= number; + } + ; + +/* + End tablespace part +*/ + +create2: + '(' create2a {} + | opt_create_table_options + opt_partitioning {} + create3 {} + | LIKE table_ident + { + LEX *lex=Lex; + THD *thd= lex->thd; + if (!(lex->like_name= $2)) + YYABORT; + if ($2->db.str == NULL && + thd->copy_db_to(&($2->db.str), &($2->db.length))) + { + YYABORT; + } + } + | '(' LIKE table_ident ')' + { + LEX *lex=Lex; + THD *thd= lex->thd; + if (!(lex->like_name= $3)) + YYABORT; + if ($3->db.str == NULL && + thd->copy_db_to(&($3->db.str), &($3->db.length))) + { + YYABORT; + } + } + ; + +create2a: + field_list ')' opt_create_table_options + opt_partitioning {} + create3 {} + | opt_partitioning {} + create_select ')' + { Select->set_braces(1);} union_opt {} + ; + +create3: + /* empty */ {} + | opt_duplicate opt_as create_select + { Select->set_braces(0);} union_clause {} + | opt_duplicate opt_as '(' create_select ')' + { Select->set_braces(1);} union_opt {} + ; + +/* + This part of the parser is about handling of the partition information. + + It's first version was written by Mikael Ronström with lots of answers to + questions provided by Antony Curtis. + + The partition grammar can be called from three places. + 1) CREATE TABLE ... PARTITION .. + 2) ALTER TABLE table_name PARTITION ... + 3) PARTITION ... + + The first place is called when a new table is created from a MySQL client. + The second place is called when a table is altered with the ALTER TABLE + command from a MySQL client. + The third place is called when opening an frm file and finding partition + info in the .frm file. It is necessary to avoid allowing PARTITION to be + an allowed entry point for SQL client queries. This is arranged by setting + some state variables before arriving here. + + To be able to handle errors we will only set error code in this code + and handle the error condition in the function calling the parser. This + is necessary to ensure we can also handle errors when calling the parser + from the openfrm function. +*/ +opt_partitioning: + /* empty */ {} + | partitioning + ; + +partitioning: + PARTITION_SYM + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + LEX *lex= Lex; + lex->part_info= new partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + YYABORT; + } + if (lex->sql_command == SQLCOM_ALTER_TABLE) + { + lex->alter_info.flags|= ALTER_PARTITION; + } +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + "partitioning", "--with-partition"); + YYABORT; +#endif + + } + partition + ; + +partition_entry: + PARTITION_SYM + { + LEX *lex= Lex; + if (!lex->part_info) + { + yyerror(ER(ER_PARTITION_ENTRY_ERROR)); + YYABORT; + } + /* + We enter here when opening the frm file to translate + partition info string into part_info data structure. + */ + } + partition {} + ; + +partition: + BY part_type_def opt_no_parts {} opt_sub_part {} part_defs + ; + +part_type_def: + opt_linear KEY_SYM '(' part_field_list ')' + { + LEX *lex= Lex; + lex->part_info->list_of_part_fields= TRUE; + lex->part_info->part_type= HASH_PARTITION; + } + | opt_linear HASH_SYM + { Lex->part_info->part_type= HASH_PARTITION; } + part_func {} + | RANGE_SYM + { Lex->part_info->part_type= RANGE_PARTITION; } + part_func {} + | LIST_SYM + { Lex->part_info->part_type= LIST_PARTITION; } + part_func {} + ; + +opt_linear: + /* empty */ {} + | LINEAR_SYM + { Lex->part_info->linear_hash_ind= TRUE;} + ; + +part_field_list: + /* empty */ {} + | part_field_item_list {} + ; + +part_field_item_list: + part_field_item {} + | part_field_item_list ',' part_field_item {} + ; + +part_field_item: + ident + { + if (Lex->part_info->part_field_list.push_back($1.str)) + { + mem_alloc_error(1); + YYABORT; + } + } + ; + +part_func: + '(' remember_name part_func_expr remember_end ')' + { + LEX *lex= Lex; + uint expr_len= (uint)($4 - $2) - 1; + lex->part_info->list_of_part_fields= FALSE; + lex->part_info->part_expr= $3; + lex->part_info->part_func_string= (char* ) sql_memdup($2+1, expr_len); + lex->part_info->part_func_len= expr_len; + } + ; + +sub_part_func: + '(' remember_name part_func_expr remember_end ')' + { + LEX *lex= Lex; + uint expr_len= (uint)($4 - $2) - 1; + lex->part_info->list_of_subpart_fields= FALSE; + lex->part_info->subpart_expr= $3; + lex->part_info->subpart_func_string= (char* ) sql_memdup($2+1, expr_len); + lex->part_info->subpart_func_len= expr_len; + } + ; + + +opt_no_parts: + /* empty */ {} + | PARTITIONS_SYM real_ulong_num + { + uint no_parts= $2; + LEX *lex= Lex; + if (no_parts == 0) + { + my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); + YYABORT; + } + + lex->part_info->no_parts= no_parts; + lex->part_info->use_default_no_partitions= FALSE; + } + ; + +opt_sub_part: + /* empty */ {} + | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func + { Lex->part_info->subpart_type= HASH_PARTITION; } + opt_no_subparts {} + | SUBPARTITION_SYM BY opt_linear KEY_SYM + '(' sub_part_field_list ')' + { + LEX *lex= Lex; + lex->part_info->subpart_type= HASH_PARTITION; + lex->part_info->list_of_subpart_fields= TRUE; + } + opt_no_subparts {} + ; + +sub_part_field_list: + sub_part_field_item {} + | sub_part_field_list ',' sub_part_field_item {} + ; + +sub_part_field_item: + ident + { + if (Lex->part_info->subpart_field_list.push_back($1.str)) + { + mem_alloc_error(1); + YYABORT; + } + } + ; + +part_func_expr: + bit_expr + { + LEX *lex= Lex; + bool not_corr_func; + not_corr_func= !lex->safe_to_cache_query; + lex->safe_to_cache_query= 1; + if (not_corr_func) + { + yyerror(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); + YYABORT; + } + $$=$1; + } + ; + +opt_no_subparts: + /* empty */ {} + | SUBPARTITIONS_SYM real_ulong_num + { + uint no_parts= $2; + LEX *lex= Lex; + if (no_parts == 0) + { + my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); + YYABORT; + } + lex->part_info->no_subparts= no_parts; + lex->part_info->use_default_no_subpartitions= FALSE; + } + ; + +part_defs: + /* empty */ + {} + | '(' part_def_list ')' + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + uint count_curr_parts= part_info->partitions.elements; + if (part_info->no_parts != 0) + { + if (part_info->no_parts != + count_curr_parts) + { + yyerror(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); + YYABORT; + } + } + else if (count_curr_parts > 0) + { + part_info->no_parts= count_curr_parts; + } + part_info->count_curr_subparts= 0; + } + ; + +part_def_list: + part_definition {} + | part_def_list ',' part_definition {} + ; + +part_definition: + PARTITION_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *p_elem= new partition_element(); + uint part_id= part_info->partitions.elements; + + if (!p_elem || part_info->partitions.push_back(p_elem)) + { + mem_alloc_error(sizeof(partition_element)); + YYABORT; + } + p_elem->part_state= PART_NORMAL; + part_info->curr_part_elem= p_elem; + part_info->current_partition= p_elem; + part_info->use_default_partitions= FALSE; + part_info->use_default_no_partitions= FALSE; + } + part_name {} + opt_part_values {} + opt_part_options {} + opt_sub_partition {} + ; + +part_name: + ident + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *p_elem= part_info->curr_part_elem; + p_elem->partition_name= $1.str; + } + ; + +opt_part_values: + /* empty */ + { + LEX *lex= Lex; + if (!is_partition_management(lex)) + { + if (lex->part_info->part_type == RANGE_PARTITION) + { + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + YYABORT; + } + if (lex->part_info->part_type == LIST_PARTITION) + { + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "LIST", "IN"); + YYABORT; + } + } + else + lex->part_info->part_type= HASH_PARTITION; + } + | VALUES LESS_SYM THAN_SYM part_func_max + { + LEX *lex= Lex; + if (!is_partition_management(lex)) + { + if (Lex->part_info->part_type != RANGE_PARTITION) + { + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN"); + YYABORT; + } + } + else + lex->part_info->part_type= RANGE_PARTITION; + } + | VALUES IN_SYM '(' part_list_func ')' + { + LEX *lex= Lex; + if (!is_partition_management(lex)) + { + if (Lex->part_info->part_type != LIST_PARTITION) + { + my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "IN"); + YYABORT; + } + } + else + lex->part_info->part_type= LIST_PARTITION; + } + ; + +part_func_max: + max_value_sym + { + LEX *lex= Lex; + if (lex->part_info->defined_max_value) + { + yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); + YYABORT; + } + lex->part_info->defined_max_value= TRUE; + lex->part_info->curr_part_elem->max_value= TRUE; + lex->part_info->curr_part_elem->range_value= LONGLONG_MAX; + } + | part_range_func + { + if (Lex->part_info->defined_max_value) + { + yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); + YYABORT; + } + if (Lex->part_info->curr_part_elem->has_null_value) + { + yyerror(ER(ER_NULL_IN_VALUES_LESS_THAN)); + YYABORT; + } + } + ; + +max_value_sym: + MAX_VALUE_SYM + | '(' MAX_VALUE_SYM ')' + ; + +part_range_func: + '(' part_bit_expr ')' + { + partition_info *part_info= Lex->part_info; + if (!($2->unsigned_flag)) + part_info->curr_part_elem->signed_flag= TRUE; + part_info->curr_part_elem->range_value= $2->value; + } + ; + +part_list_func: + part_list_item {} + | part_list_func ',' part_list_item {} + ; + +part_list_item: + part_bit_expr + { + part_elem_value *value_ptr= $1; + partition_info *part_info= Lex->part_info; + if (!value_ptr->unsigned_flag) + part_info->curr_part_elem->signed_flag= TRUE; + if (!value_ptr->null_value && + part_info->curr_part_elem-> + list_val_list.push_back(value_ptr)) + { + mem_alloc_error(sizeof(part_elem_value)); + YYABORT; + } + } + ; + +part_bit_expr: + bit_expr + { + Item *part_expr= $1; + bool not_corr_func; + int part_expression_ok= 1; + LEX *lex= Lex; + THD *thd= YYTHD; + longlong item_value; + Name_resolution_context *context= &lex->current_select->context; + TABLE_LIST *save_list= context->table_list; + const char *save_where= thd->where; + + context->table_list= 0; + thd->where= "partition function"; + + part_elem_value *value_ptr= + (part_elem_value*)sql_alloc(sizeof(part_elem_value)); + if (!value_ptr) + { + mem_alloc_error(sizeof(part_elem_value)); + YYABORT; + } + if (part_expr->walk(&Item::check_partition_func_processor, 0, + NULL)) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + YYABORT; + } + if (part_expr->fix_fields(YYTHD, (Item**)0) || + ((context->table_list= save_list), FALSE) || + (!part_expr->const_item()) || + (!lex->safe_to_cache_query)) + { + my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); + YYABORT; + } + thd->where= save_where; + value_ptr->value= part_expr->val_int(); + value_ptr->unsigned_flag= TRUE; + if (!part_expr->unsigned_flag && + value_ptr->value < 0) + value_ptr->unsigned_flag= FALSE; + if ((value_ptr->null_value= part_expr->null_value)) + { + if (Lex->part_info->curr_part_elem->has_null_value) + { + my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); + YYABORT; + } + Lex->part_info->curr_part_elem->has_null_value= TRUE; + } + else if (part_expr->result_type() != INT_RESULT && + !part_expr->null_value) + { + yyerror(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); + YYABORT; + } + $$= value_ptr; + } + ; + +opt_sub_partition: + /* empty */ + { + if (Lex->part_info->no_subparts != 0 && + !Lex->part_info->use_default_subpartitions) + { + yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + YYABORT; + } + } + | '(' sub_part_list ')' + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (part_info->no_subparts != 0) + { + if (part_info->no_subparts != + part_info->count_curr_subparts) + { + yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + YYABORT; + } + } + else if (part_info->count_curr_subparts > 0) + { + if (part_info->partitions.elements > 1) + { + yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + YYABORT; + } + part_info->no_subparts= part_info->count_curr_subparts; + } + part_info->count_curr_subparts= 0; + } + ; + +sub_part_list: + sub_part_definition {} + | sub_part_list ',' sub_part_definition {} + ; + +sub_part_definition: + SUBPARTITION_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + partition_element *curr_part= part_info->current_partition; + partition_element *sub_p_elem= new partition_element(curr_part); + if (!sub_p_elem || + curr_part->subpartitions.push_back(sub_p_elem)) + { + mem_alloc_error(sizeof(partition_element)); + YYABORT; + } + part_info->curr_part_elem= sub_p_elem; + part_info->use_default_subpartitions= FALSE; + part_info->use_default_no_subpartitions= FALSE; + part_info->count_curr_subparts++; + } + sub_name opt_part_options {} + ; + +sub_name: + ident_or_text + { Lex->part_info->curr_part_elem->partition_name= $1.str; } + ; + +opt_part_options: + /* empty */ {} + | opt_part_option_list {} + ; + +opt_part_option_list: + opt_part_option_list opt_part_option {} + | opt_part_option {} + ; + +opt_part_option: + TABLESPACE opt_equal ident_or_text + { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } + | opt_storage ENGINE_SYM opt_equal storage_engines + { + LEX *lex= Lex; + lex->part_info->curr_part_elem->engine_type= $4; + lex->part_info->default_engine_type= $4; + } + | NODEGROUP_SYM opt_equal real_ulong_num + { Lex->part_info->curr_part_elem->nodegroup_id= $3; } + | MAX_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_max_rows= $3; } + | MIN_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_min_rows= $3; } + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->data_file_name= $4.str; } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->index_file_name= $4.str; } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->part_comment= $3.str; } + ; + +/* + End of partition parser part +*/ + +create_select: + SELECT_SYM + { + LEX *lex=Lex; + lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ; + if (lex->sql_command == SQLCOM_INSERT) + lex->sql_command= SQLCOM_INSERT_SELECT; + else if (lex->sql_command == SQLCOM_REPLACE) + lex->sql_command= SQLCOM_REPLACE_SELECT; + /* + The following work only with the local list, the 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_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_select_from + { + /* + The following work only with the local list, the global list + is created correctly in this case + */ + Lex->current_select->table_list.push_front(&Lex->save_list); + } + ; + +opt_as: + /* empty */ {} + | AS {}; + +opt_create_database_options: + /* empty */ {} + | create_database_options {}; + +create_database_options: + create_database_option {} + | create_database_options create_database_option {}; + +create_database_option: + default_collation {} + | default_charset {}; + +opt_table_options: + /* empty */ { $$= 0; } + | table_options { $$= $1;}; + +table_options: + table_option { $$=$1; } + | table_option table_options { $$= $1 | $2; }; + +table_option: + TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }; + +opt_if_not_exists: + /* empty */ { $$= 0; } + | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }; + +opt_create_table_options: + /* empty */ + | create_table_options; + +create_table_options_space_separated: + create_table_option + | create_table_option create_table_options_space_separated; + +create_table_options: + create_table_option + | create_table_option create_table_options + | create_table_option ',' create_table_options; + +create_table_option: + ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } + | TYPE_SYM opt_equal storage_engines + { + Lex->create_info.db_type= $3; + WARN_DEPRECATED(yythd, "5.2", "TYPE=storage_engine", + "'ENGINE=storage_engine'"); + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + } + | MAX_ROWS opt_equal ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;} + | MIN_ROWS opt_equal ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;} + | AVG_ROW_LENGTH opt_equal ulong_num { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;} + | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; } + | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; } + | AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} + | PACK_KEYS_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_PACK_KEYS; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; + break; + default: + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | PACK_KEYS_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } + | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; } + | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } + | UNION_SYM opt_equal '(' table_list ')' + { + /* Move the union list to the merge_list */ + LEX *lex=Lex; + 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_local); + lex->select_lex.table_list.elements=1; + 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; + } + | default_charset + | default_collation + | INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; } + | TABLESPACE ident {Lex->create_info.tablespace= $2.str;} + | STORAGE_SYM DISK_SYM {Lex->create_info.store_on_disk= TRUE;} + | STORAGE_SYM MEMORY_SYM {Lex->create_info.store_on_disk= FALSE;} + | CONNECTION_SYM opt_equal TEXT_STRING_sys { Lex->create_info.connect_string.str= $3.str; Lex->create_info.connect_string.length= $3.length; Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION; } + | KEY_BLOCK_SIZE opt_equal ulong_num + { + Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; + Lex->create_info.key_block_size= $3; + } + ; + +default_charset: + opt_default charset opt_equal charset_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !my_charset_same(cinfo->default_table_charset,$4)) + { + my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), + "CHARACTER SET ", cinfo->default_table_charset->csname, + "CHARACTER SET ", $4->csname); + YYABORT; + } + Lex->create_info.default_table_charset= $4; + Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + }; + +default_collation: + opt_default COLLATE_SYM opt_equal collation_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !my_charset_same(cinfo->default_table_charset,$4)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $4->name, cinfo->default_table_charset->csname); + YYABORT; + } + Lex->create_info.default_table_charset= $4; + Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + }; + +storage_engines: + ident_or_text + { + $$ = ha_resolve_by_name(YYTHD, &$1); + if ($$ == NULL) + if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); + YYABORT; + } + else + { + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_UNKNOWN_STORAGE_ENGINE, + ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str); + } + }; + +row_types: + DEFAULT { $$= ROW_TYPE_DEFAULT; } + | FIXED_SYM { $$= ROW_TYPE_FIXED; } + | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } + | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } + | COMPACT_SYM { $$= ROW_TYPE_COMPACT; }; + +merge_insert_types: + NO_SYM { $$= MERGE_INSERT_DISABLED; } + | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }; + +opt_select_from: + opt_limit_clause {} + | select_from select_lock_type; + +udf_func_type: + /* empty */ { $$ = UDFTYPE_FUNCTION; } + | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; + +udf_type: + STRING_SYM {$$ = (int) STRING_RESULT; } + | REAL {$$ = (int) REAL_RESULT; } + | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; } + | INT_SYM {$$ = (int) INT_RESULT; }; + +field_list: + field_list_item + | field_list ',' field_list_item; + + +field_list_item: + column_def + | key_def + ; + +column_def: + field_spec opt_check_constraint + | field_spec references + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + ; + +key_def: + key_type opt_ident key_alg '(' key_list ')' key_options + { + LEX *lex=Lex; + if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->key_list.push_back(new Key($1,$2, &lex->key_create_info, 0, + lex->col_list)); + lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint constraint_key_type opt_ident key_alg + '(' key_list ')' key_options + { + LEX *lex=Lex; + const char *key_name= $3 ? $3 : $1; + lex->key_list.push_back(new Key($2, key_name, &lex->key_create_info, 0, + lex->col_list)); + lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references + { + LEX *lex=Lex; + lex->key_list.push_back(new foreign_key($4 ? $4:$1, lex->col_list, + $8, + lex->ref_list, + lex->fk_delete_opt, + lex->fk_update_opt, + lex->fk_match_option)); + lex->key_list.push_back(new Key(Key::MULTIPLE, $4 ? $4 : $1, + &default_key_create_info, 1, + lex->col_list)); + lex->col_list.empty(); /* Alloced by sql_alloc */ + + /* Only used for ALTER TABLE. Ignored otherwise. */ + lex->alter_info.flags|= ALTER_FOREIGN_KEY; + } + | constraint opt_check_constraint + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + | opt_constraint check_constraint + { + Lex->col_list.empty(); /* Alloced by sql_alloc */ + } + ; + +opt_check_constraint: + /* empty */ + | check_constraint + ; + +check_constraint: + CHECK_SYM expr + ; + +opt_constraint: + /* empty */ { $$=(char*) 0; } + | constraint { $$= $1; } + ; + +constraint: + CONSTRAINT opt_ident { $$=$2; } + ; + +field_spec: + field_ident + { + LEX *lex=Lex; + lex->length=lex->dec=0; lex->type=0; + lex->default_value= lex->on_update_value= 0; + lex->comment=null_lex_str; + lex->charset=NULL; + } + type opt_attribute + { + LEX *lex=Lex; + if (add_field_to_list(lex->thd, $1.str, + (enum enum_field_types) $3, + lex->length,lex->dec,lex->type, + lex->default_value, lex->on_update_value, + &lex->comment, + lex->change,&lex->interval_list,lex->charset, + lex->uint_geom_type)) + YYABORT; + }; + +type: + int_type opt_len field_options { $$=$1; } + | real_type opt_precision field_options { $$=$1; } + | FLOAT_SYM float_options field_options { $$=FIELD_TYPE_FLOAT; } + | BIT_SYM { Lex->length= (char*) "1"; + $$=FIELD_TYPE_BIT; } + | BIT_SYM '(' NUM ')' { Lex->length= $3.str; + $$=FIELD_TYPE_BIT; } + | BOOL_SYM { Lex->length=(char*) "1"; + $$=FIELD_TYPE_TINY; } + | BOOLEAN_SYM { Lex->length=(char*) "1"; + $$=FIELD_TYPE_TINY; } + | char '(' NUM ')' opt_binary { Lex->length=$3.str; + $$=FIELD_TYPE_STRING; } + | char opt_binary { Lex->length=(char*) "1"; + $$=FIELD_TYPE_STRING; } + | nchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; + $$=FIELD_TYPE_STRING; + Lex->charset=national_charset_info; } + | nchar opt_bin_mod { Lex->length=(char*) "1"; + $$=FIELD_TYPE_STRING; + Lex->charset=national_charset_info; } + | BINARY '(' NUM ')' { Lex->length=$3.str; + Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_STRING; } + | BINARY { Lex->length= (char*) "1"; + Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_STRING; } + | varchar '(' NUM ')' opt_binary { Lex->length=$3.str; + $$= MYSQL_TYPE_VARCHAR; } + | nvarchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; + $$= MYSQL_TYPE_VARCHAR; + Lex->charset=national_charset_info; } + | VARBINARY '(' NUM ')' { Lex->length=$3.str; + Lex->charset=&my_charset_bin; + $$= MYSQL_TYPE_VARCHAR; } + | YEAR_SYM opt_len field_options { $$=FIELD_TYPE_YEAR; } + | DATE_SYM { $$=FIELD_TYPE_DATE; } + | TIME_SYM { $$=FIELD_TYPE_TIME; } + | TIMESTAMP opt_len + { + if (YYTHD->variables.sql_mode & MODE_MAXDB) + $$=FIELD_TYPE_DATETIME; + else + { + /* + Unlike other types TIMESTAMP fields are NOT NULL by default. + */ + Lex->type|= NOT_NULL_FLAG; + $$=FIELD_TYPE_TIMESTAMP; + } + } + | DATETIME { $$=FIELD_TYPE_DATETIME; } + | TINYBLOB { Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_TINY_BLOB; } + | BLOB_SYM opt_len { Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_BLOB; } + | spatial_type + { +#ifdef HAVE_SPATIAL + Lex->charset=&my_charset_bin; + Lex->uint_geom_type= (uint)$1; + $$=FIELD_TYPE_GEOMETRY; +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + YYABORT; +#endif + } + | MEDIUMBLOB { Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_MEDIUM_BLOB; } + | LONGBLOB { Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_LONG_BLOB; } + | LONG_SYM VARBINARY { Lex->charset=&my_charset_bin; + $$=FIELD_TYPE_MEDIUM_BLOB; } + | LONG_SYM varchar opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } + | TINYTEXT opt_binary { $$=FIELD_TYPE_TINY_BLOB; } + | TEXT_SYM opt_len opt_binary { $$=FIELD_TYPE_BLOB; } + | MEDIUMTEXT opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } + | LONGTEXT opt_binary { $$=FIELD_TYPE_LONG_BLOB; } + | DECIMAL_SYM float_options field_options + { $$=FIELD_TYPE_NEWDECIMAL;} + | NUMERIC_SYM float_options field_options + { $$=FIELD_TYPE_NEWDECIMAL;} + | FIXED_SYM float_options field_options + { $$=FIELD_TYPE_NEWDECIMAL;} + | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary + { $$=FIELD_TYPE_ENUM; } + | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary + { $$=FIELD_TYPE_SET; } + | LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } + | SERIAL_SYM + { + $$=FIELD_TYPE_LONGLONG; + Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG | + UNIQUE_FLAG); + } + ; + +spatial_type: + GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } + | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } + | POINT_SYM { Lex->length= (char*)"21"; + $$= Field::GEOM_POINT; + } + | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } + | LINESTRING { $$= Field::GEOM_LINESTRING; } + | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } + | POLYGON { $$= Field::GEOM_POLYGON; } + | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; } + ; + +char: + CHAR_SYM {} + ; + +nchar: + NCHAR_SYM {} + | NATIONAL_SYM CHAR_SYM {} + ; + +varchar: + char VARYING {} + | VARCHAR {} + ; + +nvarchar: + NATIONAL_SYM VARCHAR {} + | NVARCHAR_SYM {} + | NCHAR_SYM VARCHAR {} + | NATIONAL_SYM CHAR_SYM VARYING {} + | NCHAR_SYM VARYING {} + ; + +int_type: + INT_SYM { $$=FIELD_TYPE_LONG; } + | TINYINT { $$=FIELD_TYPE_TINY; } + | SMALLINT { $$=FIELD_TYPE_SHORT; } + | MEDIUMINT { $$=FIELD_TYPE_INT24; } + | BIGINT { $$=FIELD_TYPE_LONGLONG; }; + +real_type: + REAL { $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ? + FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; } + | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; } + | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }; + + +float_options: + /* empty */ { Lex->dec=Lex->length= (char*)0; } + | '(' NUM ')' { Lex->length=$2.str; Lex->dec= (char*)0; } + | precision {}; + +precision: + '(' NUM ',' NUM ')' + { + LEX *lex=Lex; + lex->length=$2.str; lex->dec=$4.str; + }; + +field_options: + /* empty */ {} + | field_opt_list {}; + +field_opt_list: + field_opt_list field_option {} + | field_option {}; + +field_option: + SIGNED_SYM {} + | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }; + +opt_len: + /* empty */ { Lex->length=(char*) 0; } /* use default length */ + | '(' NUM ')' { Lex->length= $2.str; }; + +opt_precision: + /* empty */ {} + | precision {}; + +opt_attribute: + /* empty */ {} + | opt_attribute_list {}; + +opt_attribute_list: + opt_attribute_list attribute {} + | attribute; + +attribute: + NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } + | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; } + | DEFAULT now_or_signed_literal { Lex->default_value=$2; } + | ON UPDATE_SYM NOW_SYM optional_braces + { Lex->on_update_value= new Item_func_now_local(); } + | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } + | SERIAL_SYM DEFAULT VALUE_SYM + { + LEX *lex=Lex; + lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | opt_primary KEY_SYM + { + LEX *lex=Lex; + lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } + | COLLATE_SYM collation_name + { + if (Lex->charset && !my_charset_same(Lex->charset,$2)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $2->name,Lex->charset->csname); + YYABORT; + } + else + { + Lex->charset=$2; + } + } + ; + +now_or_signed_literal: + NOW_SYM optional_braces { $$= new Item_func_now_local(); } + | signed_literal { $$=$1; } + ; + +charset: + CHAR_SYM SET {} + | CHARSET {} + ; + +charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); + YYABORT; + } + } + | BINARY { $$= &my_charset_bin; } + ; + +charset_name_or_default: + charset_name { $$=$1; } + | DEFAULT { $$=NULL; } ; + + +old_or_new_charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) && + !($$=get_old_charset_by_name($1.str))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); + YYABORT; + } + } + | BINARY { $$= &my_charset_bin; } + ; + +old_or_new_charset_name_or_default: + old_or_new_charset_name { $$=$1; } + | DEFAULT { $$=NULL; } ; + +collation_name: + ident_or_text + { + if (!($$=get_charset_by_name($1.str,MYF(0)))) + { + my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str); + YYABORT; + } + }; + +opt_collate: + /* empty */ { $$=NULL; } + | COLLATE_SYM collation_name_or_default { $$=$2; } + ; + +collation_name_or_default: + collation_name { $$=$1; } + | DEFAULT { $$=NULL; } ; + +opt_default: + /* empty */ {} + | DEFAULT {}; + +opt_binary: + /* empty */ { Lex->charset=NULL; } + | ASCII_SYM opt_bin_mod { Lex->charset=&my_charset_latin1; } + | BYTE_SYM { Lex->charset=&my_charset_bin; } + | UNICODE_SYM opt_bin_mod + { + if (!(Lex->charset=get_charset_by_csname("ucs2", + MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); + YYABORT; + } + } + | charset charset_name opt_bin_mod { Lex->charset=$2; } + | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; }; + +opt_bin_mod: + /* empty */ { } + | BINARY { Lex->type|= BINCMP_FLAG; }; + +opt_bin_charset: + /* empty */ { Lex->charset= NULL; } + | ASCII_SYM { Lex->charset=&my_charset_latin1; } + | UNICODE_SYM + { + if (!(Lex->charset=get_charset_by_csname("ucs2", + MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); + YYABORT; + } + } + | charset charset_name { Lex->charset=$2; } ; + +opt_primary: + /* empty */ + | PRIMARY_SYM + ; + +references: + REFERENCES table_ident + { + LEX *lex=Lex; + lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0; + lex->ref_list.empty(); + } + opt_ref_list + { + $$=$2; + }; + +opt_ref_list: + /* empty */ opt_on_delete {} + | '(' ref_list ')' opt_on_delete {}; + +ref_list: + ref_list ',' ident { Lex->ref_list.push_back(new key_part_spec($3.str)); } + | ident { Lex->ref_list.push_back(new key_part_spec($1.str)); }; + + +opt_on_delete: + /* empty */ {} + | opt_on_delete_list {}; + +opt_on_delete_list: + opt_on_delete_list opt_on_delete_item {} + | opt_on_delete_item {}; + +opt_on_delete_item: + ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; } + | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; } + | MATCH FULL { Lex->fk_match_option= foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL { Lex->fk_match_option= foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM { Lex->fk_match_option= foreign_key::FK_MATCH_SIMPLE; }; + +delete_option: + RESTRICT { $$= (int) foreign_key::FK_OPTION_RESTRICT; } + | CASCADE { $$= (int) foreign_key::FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= (int) foreign_key::FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= (int) foreign_key::FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= (int) foreign_key::FK_OPTION_DEFAULT; }; + +key_type: + key_or_index { $$= Key::MULTIPLE; } + | FULLTEXT_SYM opt_key_or_index { $$= Key::FULLTEXT; } + | SPATIAL_SYM opt_key_or_index + { +#ifdef HAVE_SPATIAL + $$= Key::SPATIAL; +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + YYABORT; +#endif + }; + +constraint_key_type: + PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } + | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; }; + +key_or_index: + KEY_SYM {} + | INDEX_SYM {}; + +opt_key_or_index: + /* empty */ {} + | key_or_index + ; + +keys_or_index: + KEYS {} + | INDEX_SYM {} + | INDEXES {}; + +opt_unique_or_fulltext: + /* empty */ { $$= Key::MULTIPLE; } + | UNIQUE_SYM { $$= Key::UNIQUE; } + | FULLTEXT_SYM { $$= Key::FULLTEXT;} + | SPATIAL_SYM + { +#ifdef HAVE_SPATIAL + $$= Key::SPATIAL; +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + YYABORT; +#endif + } + ; + +init_key_options: + { + Lex->key_create_info= default_key_create_info; + } + ; + +/* + For now, key_alg initializies lex->key_create_info. + In the future, when all key options are after key definition, + we can remove key_alg and move init_key_options to key_options +*/ + +key_alg: + /* empty */ init_key_options + | init_key_options key_using_alg + ; + +key_options: + /* empty */ {} + | key_opts + ; + +key_opts: + key_opt + | key_opts key_opt + ; + +key_using_alg: + USING btree_or_rtree { Lex->key_create_info.algorithm= $2; } + | TYPE_SYM btree_or_rtree { Lex->key_create_info.algorithm= $2; } + ; + +key_opt: + key_using_alg + | KEY_BLOCK_SIZE opt_equal ulong_num + { Lex->key_create_info.block_size= $3; } + | WITH PARSER_SYM IDENT_sys + { + if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN)) + Lex->key_create_info.parser_name= $3; + else + { + my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str); + YYABORT; + } + } + ; + + +btree_or_rtree: + BTREE_SYM { $$= HA_KEY_ALG_BTREE; } + | RTREE_SYM + { + $$= HA_KEY_ALG_RTREE; + } + | HASH_SYM { $$= HA_KEY_ALG_HASH; }; + +key_list: + key_list ',' key_part order_dir { Lex->col_list.push_back($3); } + | key_part order_dir { Lex->col_list.push_back($1); }; + +key_part: + ident { $$=new key_part_spec($1.str); } + | ident '(' NUM ')' + { + int key_part_len= atoi($3.str); + if (!key_part_len) + { + my_error(ER_KEY_PART_0, MYF(0), $1.str); + } + $$=new key_part_spec($1.str,(uint) key_part_len); + }; + +opt_ident: + /* empty */ { $$=(char*) 0; } /* Defaultlength */ + | field_ident { $$=$1.str; }; + +opt_component: + /* empty */ { $$= null_lex_str; } + | '.' ident { $$= $2; }; + +string_list: + text_string { Lex->interval_list.push_back($1); } + | string_list ',' text_string { Lex->interval_list.push_back($3); }; + +/* +** Alter table +*/ + +alter: + ALTER opt_ignore TABLE_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->name.str= 0; + lex->name.length= 0; + lex->sql_command= SQLCOM_ALTER_TABLE; + lex->duplicates= DUP_ERROR; + if (!lex->select_lex.add_table_to_list(thd, $4, NULL, + TL_OPTION_UPDATING)) + YYABORT; + lex->create_list.empty(); + lex->key_list.empty(); + lex->col_list.empty(); + lex->select_lex.init_order(); + lex->like_name= 0; + lex->select_lex.db= + ((TABLE_LIST*) lex->select_lex.table_list.first)->db; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + lex->create_info.db_type= 0; + lex->create_info.default_table_charset= NULL; + lex->create_info.row_type= ROW_TYPE_NOT_USED; + lex->alter_info.reset(); + lex->alter_info.flags= 0; + lex->no_write_to_binlog= 0; + } + alter_commands + {} + | ALTER DATABASE ident_or_empty + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + opt_create_database_options + { + LEX *lex=Lex; + THD *thd= Lex->thd; + lex->sql_command=SQLCOM_ALTER_DB; + lex->name= $3; + if (lex->name.str == NULL && + thd->copy_db_to(&lex->name.str, &lex->name.length)) + YYABORT; + } + | ALTER PROCEDURE sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); + YYABORT; + } + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->spname= $3; + } + | ALTER FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); + YYABORT; + } + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->spname= $3; + } + | ALTER view_algorithm_opt definer view_suid + VIEW_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_CREATE_VIEW; + lex->create_view_mode= VIEW_ALTER; + /* first table in list is target VIEW name */ + lex->select_lex.add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING); + } + view_list_opt AS view_select view_check_option + {} + | ALTER EVENT_SYM sp_name + /* + BE CAREFUL when you add a new rule to update the block where + YYTHD->client_capabilities is set back to original value + */ + { + /* + It is safe to use Lex->spname because + ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO + is not allowed. Lex->spname is used in the case of RENAME TO + If it had to be supported spname had to be added to + Event_parse_data. + */ + + if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) + YYABORT; + Lex->event_parse_data->identifier= $3; + + /* + We have to turn off CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + Lex->sql_command= SQLCOM_ALTER_EVENT; + } + ev_alter_on_schedule_completion + opt_ev_rename_to + opt_ev_status + opt_ev_comment + opt_ev_sql_stmt + { + /* + $1 - ALTER + $2 - EVENT_SYM + $3 - sp_name + $4 - the block above + */ + YYTHD->client_capabilities |= $<ulong_num>4; + + if (!($5 || $6 || $7 || $8 || $9)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_ALTER_EVENT; + } + | ALTER TABLESPACE alter_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE; + } + | ALTER LOGFILE_SYM GROUP alter_logfile_group_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP; + } + | ALTER TABLESPACE change_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE; + } + | ALTER TABLESPACE change_tablespace_access + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; + } + | ALTER SERVER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')' + { + LEX *lex= Lex; + Lex->sql_command= SQLCOM_ALTER_SERVER; + Lex->server_options.server_name= $3.str; + Lex->server_options.server_name_length= $3.length; + } + ; + +ev_alter_on_schedule_completion: /* empty */ { $$= 0;} + | ON SCHEDULE_SYM ev_schedule_time { $$= 1; } + | ev_on_completion { $$= 1; } + | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; } + ; + +opt_ev_rename_to: /* empty */ { $$= 0;} + | RENAME TO_SYM sp_name + { + /* + Use lex's spname to hold the new name. + The original name is in the Event_parse_data object + */ + Lex->spname= $3; + $$= 1; + } + ; + +opt_ev_sql_stmt: /* empty*/ { $$= 0;} + | DO_SYM ev_sql_stmt { $$= 1; } + ; + + +ident_or_empty: + /* empty */ { $$.str= 0; $$.length= 0; } + | ident { $$= $1; }; + +alter_commands: + | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } + | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } + | alter_list + opt_partitioning + | alter_list + remove_partitioning + | remove_partitioning + | partitioning +/* + This part was added for release 5.1 by Mikael Ronström. + From here we insert a number of commands to manage the partitions of a + partitioned table such as adding partitions, dropping partitions, + reorganising partitions in various manners. In future releases the list + will be longer and also include moving partitions to a + new table and so forth. +*/ + | add_partition_rule + | DROP PARTITION_SYM alt_part_name_list + { + Lex->alter_info.flags|= ALTER_DROP_PARTITION; + } + | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_REBUILD_PARTITION; + lex->no_write_to_binlog= $3; + } + | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_OPTIMIZE_PARTITION; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + } + opt_no_write_to_binlog opt_mi_check_type + | ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_ANALYZE_PARTITION; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + } + opt_mi_check_type + | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_CHECK_PARTITION; + lex->check_opt.init(); + } + opt_mi_check_type + | REPAIR PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_REPAIR_PARTITION; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + } + opt_mi_repair_type + | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num + { + LEX *lex= Lex; + lex->alter_info.flags|= ALTER_COALESCE_PARTITION; + lex->no_write_to_binlog= $3; + lex->alter_info.no_parts= $4; + } + | reorg_partition_rule + ; + +remove_partitioning: + REMOVE_SYM PARTITIONING_SYM + { + Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING; + } + ; + +all_or_alt_part_name_list: + ALL + { + Lex->alter_info.flags|= ALTER_ALL_PARTITION; + } + | alt_part_name_list + ; + +add_partition_rule: + ADD PARTITION_SYM opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + YYABORT; + } + lex->alter_info.flags|= ALTER_ADD_PARTITION; + lex->no_write_to_binlog= $3; + } + add_part_extra + {} + ; + +add_part_extra: + | '(' part_def_list ')' + { + LEX *lex= Lex; + lex->part_info->no_parts= lex->part_info->partitions.elements; + } + | PARTITIONS_SYM real_ulong_num + { + LEX *lex= Lex; + lex->part_info->no_parts= $2; + } + ; + +reorg_partition_rule: + REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + YYABORT; + } + lex->no_write_to_binlog= $3; + } + reorg_parts_rule + ; + +reorg_parts_rule: + /* empty */ + { + Lex->alter_info.flags|= ALTER_TABLE_REORG; + } + | + alt_part_name_list + { + Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION; + } + INTO '(' part_def_list ')' + { + LEX *lex= Lex; + lex->part_info->no_parts= lex->part_info->partitions.elements; + } + ; + +alt_part_name_list: + alt_part_name_item {} + | alt_part_name_list ',' alt_part_name_item {} + ; + +alt_part_name_item: + ident + { + if (Lex->alter_info.partition_names.push_back($1.str)) + { + mem_alloc_error(1); + YYABORT; + } + } + ; + +/* + End of management of partition commands +*/ + +alter_list: + alter_list_item + | alter_list ',' alter_list_item + ; + +add_column: + ADD opt_column + { + LEX *lex=Lex; + lex->change=0; + lex->alter_info.flags|= ALTER_ADD_COLUMN; + }; + +alter_list_item: + add_column column_def opt_place { } + | ADD key_def + { + Lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | add_column '(' field_list ')' + { + Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; + } + | CHANGE opt_column field_ident + { + LEX *lex=Lex; + lex->change= $3.str; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + } + field_spec opt_place + | MODIFY_SYM opt_column field_ident + { + LEX *lex=Lex; + lex->length=lex->dec=0; lex->type=0; + lex->default_value= lex->on_update_value= 0; + lex->comment=null_lex_str; + lex->charset= NULL; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + } + type opt_attribute + { + LEX *lex=Lex; + if (add_field_to_list(lex->thd,$3.str, + (enum enum_field_types) $5, + lex->length,lex->dec,lex->type, + lex->default_value, lex->on_update_value, + &lex->comment, + $3.str, &lex->interval_list, lex->charset, + lex->uint_geom_type)) + YYABORT; + } + opt_place + | DROP opt_column field_ident opt_restrict + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, + $3.str)); + lex->alter_info.flags|= ALTER_DROP_COLUMN; + } + | DROP FOREIGN KEY_SYM opt_ident + { + Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY; + } + | DROP PRIMARY_SYM KEY_SYM + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + primary_key_name)); + lex->alter_info.flags|= ALTER_DROP_INDEX; + } + | DROP key_or_index field_ident + { + LEX *lex=Lex; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + lex->alter_info.flags|= ALTER_DROP_INDEX; + } + | DISABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= DISABLE; + lex->alter_info.flags|= ALTER_KEYS_ONOFF; + } + | ENABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= ENABLE; + lex->alter_info.flags|= ALTER_KEYS_ONOFF; + } + | ALTER opt_column field_ident SET DEFAULT signed_literal + { + LEX *lex=Lex; + lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6)); + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + } + | ALTER opt_column field_ident DROP DEFAULT + { + LEX *lex=Lex; + lex->alter_info.alter_list.push_back(new Alter_column($3.str, + (Item*) 0)); + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + THD *thd= lex->thd; + uint dummy; + lex->select_lex.db=$3->db.str; + if (lex->select_lex.db == NULL && + thd->copy_db_to(&lex->select_lex.db, &dummy)) + { + YYABORT; + } + if (check_table_name($3->table.str,$3->table.length) || + $3->db.str && check_db_name(&$3->db)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); + YYABORT; + } + lex->name= $3->table; + lex->alter_info.flags|= ALTER_RENAME; + } + | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate + { + if (!$4) + { + THD *thd= YYTHD; + $4= thd->variables.collation_database; + } + $5= $5 ? $5 : $4; + if (!my_charset_same($4,$5)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $5->name, $4->csname); + YYABORT; + } + LEX *lex= Lex; + lex->create_info.table_charset= + lex->create_info.default_table_charset= $5; + lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | + HA_CREATE_USED_DEFAULT_CHARSET); + lex->alter_info.flags|= ALTER_CONVERT; + } + | create_table_options_space_separated + { + LEX *lex=Lex; + lex->alter_info.flags|= ALTER_OPTIONS; + } + | FORCE_SYM + { + Lex->alter_info.flags|= ALTER_FORCE; + } + | order_clause + { + LEX *lex=Lex; + lex->alter_info.flags|= ALTER_ORDER; + }; + +opt_column: + /* empty */ {} + | COLUMN_SYM {}; + +opt_ignore: + /* empty */ { Lex->ignore= 0;} + | IGNORE_SYM { Lex->ignore= 1;} + ; + +opt_restrict: + /* empty */ { Lex->drop_mode= DROP_DEFAULT; } + | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } + | CASCADE { Lex->drop_mode= DROP_CASCADE; } + ; + +opt_place: + /* empty */ {} + | AFTER_SYM ident { store_position_for_column($2.str); } + | FIRST_SYM { store_position_for_column(first_keyword); }; + +opt_to: + /* empty */ {} + | TO_SYM {} + | EQ {} + | AS {}; + +/* + SLAVE START and SLAVE STOP are deprecated. We keep them for compatibility. +*/ + +slave: + START_SYM SLAVE slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* We'll use mi structure for UNTIL options */ + bzero((char*) &lex->mi, sizeof(lex->mi)); + /* If you change this code don't forget to update SLAVE START too */ + } + slave_until + {} + | STOP_SYM SLAVE slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + /* If you change this code don't forget to update SLAVE STOP too */ + } + | SLAVE START_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* We'll use mi structure for UNTIL options */ + bzero((char*) &lex->mi, sizeof(lex->mi)); + } + slave_until + {} + | SLAVE STOP_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + } + ; + + +start: + START_SYM TRANSACTION_SYM start_transaction_opts + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_BEGIN; + lex->start_transaction_opt= $3; + } + ; + +start_transaction_opts: + /*empty*/ { $$ = 0; } + | WITH CONSISTENT_SYM SNAPSHOT_SYM + { + $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; + } + ; + +slave_thread_opts: + { Lex->slave_thd_opt= 0; } + slave_thread_opt_list + {} + ; + +slave_thread_opt_list: + slave_thread_opt + | slave_thread_opt_list ',' slave_thread_opt + ; + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } + | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } + ; + +slave_until: + /*empty*/ {} + | UNTIL_SYM slave_until_opts + { + LEX *lex=Lex; + if ((lex->mi.log_file_name || lex->mi.pos) && + (lex->mi.relay_log_name || lex->mi.relay_log_pos) || + !((lex->mi.log_file_name && lex->mi.pos) || + (lex->mi.relay_log_name && lex->mi.relay_log_pos))) + { + my_message(ER_BAD_SLAVE_UNTIL_COND, + ER(ER_BAD_SLAVE_UNTIL_COND), MYF(0)); + YYABORT; + } + + } + ; + +slave_until_opts: + master_file_def + | slave_until_opts ',' master_file_def ; + + +restore: + RESTORE_SYM table_or_tables + { + Lex->sql_command = SQLCOM_RESTORE_TABLE; + WARN_DEPRECATED(yythd, "5.2", "RESTORE TABLE", + "MySQL Administrator (mysqldump, mysql)"); + } + table_list FROM TEXT_STRING_sys + { + Lex->backup_dir = $6.str; + }; + +backup: + BACKUP_SYM table_or_tables + { + Lex->sql_command = SQLCOM_BACKUP_TABLE; + WARN_DEPRECATED(yythd, "5.2", "BACKUP TABLE", + "MySQL Administrator (mysqldump, mysql)"); + } + table_list TO_SYM TEXT_STRING_sys + { + Lex->backup_dir = $6.str; + }; + +checksum: + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + } + table_list opt_checksum_type + {} + ; + +opt_checksum_type: + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + ; + +repair: + REPAIR opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPAIR; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_repair_type + {} + ; + +opt_mi_repair_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_repair_types {}; + +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {}; + +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }; + +analyze: + ANALYZE_SYM opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_ANALYZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; + +binlog_base64_event: + BINLOG_SYM TEXT_STRING_sys + { + Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; + Lex->comment= $2; + } + ; + +check: + CHECK_SYM table_or_tables + { + LEX *lex=Lex; + + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK"); + YYABORT; + } + lex->sql_command = SQLCOM_CHECK; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; + +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {}; + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {}; + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }; + +optimize: + OPTIMIZE opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_OPTIMIZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + } + table_list opt_mi_check_type + {} + ; + +opt_no_write_to_binlog: + /* empty */ { $$= 0; } + | NO_WRITE_TO_BINLOG { $$= 1; } + | LOCAL_SYM { $$= 1; } + ; + +rename: + RENAME table_or_tables + { + Lex->sql_command= SQLCOM_RENAME_TABLE; + } + table_to_table_list + {} + | RENAME DATABASE + { + Lex->db_list.empty(); + Lex->sql_command= SQLCOM_RENAME_DB; + } + db_to_db + {} + | RENAME USER clear_privileges rename_list + { + Lex->sql_command = SQLCOM_RENAME_USER; + } + ; + +rename_list: + user TO_SYM user + { + if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3)) + YYABORT; + } + | rename_list ',' user TO_SYM user + { + if (Lex->users_list.push_back($3) || Lex->users_list.push_back($5)) + YYABORT; + } + ; + +table_to_table_list: + table_to_table + | table_to_table_list ',' table_to_table; + +table_to_table: + table_ident TO_SYM table_ident + { + LEX *lex=Lex; + SELECT_LEX *sl= lex->current_select; + if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, + TL_IGNORE) || + !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, + TL_IGNORE)) + YYABORT; + }; + +db_to_db: + ident TO_SYM ident + { + LEX *lex=Lex; + if (Lex->db_list.push_back((LEX_STRING*) + sql_memdup(&$1, sizeof(LEX_STRING))) || + Lex->db_list.push_back((LEX_STRING*) + sql_memdup(&$3, sizeof(LEX_STRING)))) + YYABORT; + }; + +keycache: + CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; + lex->ident= $5; + } + ; + +keycache_list: + assign_to_keycache + | keycache_list ',' assign_to_keycache; + +assign_to_keycache: + table_ident cache_keys_spec + { + LEX *lex=Lex; + SELECT_LEX *sel= &lex->select_lex; + if (!sel->add_table_to_list(lex->thd, $1, NULL, 0, + TL_READ, + sel->get_use_index(), + (List<String> *)0)) + YYABORT; + } + ; + +key_cache_name: + ident { $$= $1; } + | DEFAULT { $$ = default_key_cache_base; } + ; + +preload: + LOAD INDEX_SYM INTO CACHE_SYM + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_PRELOAD_KEYS; + } + preload_list + {} + ; + +preload_list: + preload_keys + | preload_list ',' preload_keys; + +preload_keys: + table_ident cache_keys_spec opt_ignore_leaves + { + LEX *lex=Lex; + SELECT_LEX *sel= &lex->select_lex; + if (!sel->add_table_to_list(lex->thd, $1, NULL, $3, + TL_READ, + sel->get_use_index(), + (List<String> *)0)) + YYABORT; + } + ; + +cache_keys_spec: + { Select->interval_list.empty(); } + cache_key_list_or_empty + { + LEX *lex=Lex; + SELECT_LEX *sel= &lex->select_lex; + sel->use_index= sel->interval_list; + } + ; + +cache_key_list_or_empty: + /* empty */ { Lex->select_lex.use_index_ptr= 0; } + | opt_key_or_index '(' key_usage_list2 ')' + { + SELECT_LEX *sel= &Lex->select_lex; + sel->use_index_ptr= &sel->use_index; + } + ; + +opt_ignore_leaves: + /* empty */ + { $$= 0; } + | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; } + ; + +/* + Select : retrieve data from table +*/ + + +select: + select_init + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + } + ; + +/* Need select_init2 for subselects. */ +select_init: + SELECT_SYM select_init2 + | + '(' select_paren ')' union_opt; + +select_paren: + SELECT_SYM select_part2 + { + LEX *lex= Lex; + SELECT_LEX * sel= lex->current_select; + if (sel->set_braces(1)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if (sel->linkage == UNION_TYPE && + !sel->master_unit()->first_select()->braces && + sel->master_unit()->first_select()->linkage == + UNION_TYPE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + /* select in braces, can't contain global parameters */ + if (sel->master_unit()->fake_select_lex) + sel->master_unit()->global_parameters= + sel->master_unit()->fake_select_lex; + } + | '(' select_paren ')'; + +select_init2: + select_part2 + { + LEX *lex= Lex; + SELECT_LEX * sel= lex->current_select; + if (lex->current_select->set_braces(0)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if (sel->linkage == UNION_TYPE && + sel->master_unit()->first_select()->braces) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + } + union_clause + ; + +select_part2: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + select_into select_lock_type; + +select_into: + opt_order_clause opt_limit_clause {} + | into + | select_from + | into select_from + | select_from into; + +select_from: + FROM join_table_list where_clause group_clause having_clause + opt_order_clause opt_limit_clause procedure_clause + | FROM DUAL_SYM where_clause opt_limit_clause + /* oracle compatibility: oracle always requires FROM clause, + and DUAL is system table without fields. + Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? + Hmmm :) */ + ; + +select_options: + /* empty*/ + | select_option_list + { + if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) + { + my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"); + YYABORT; + } + } + ; + +select_option_list: + select_option_list select_option + | select_option; + +select_option: + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY + { + if (check_simple_select()) + YYABORT; + Lex->lock_option= TL_READ_HIGH_PRIORITY; + } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT + { + if (check_simple_select()) + YYABORT; + Select->options|= OPTION_BUFFER_RESULT; + } + | SQL_CALC_FOUND_ROWS + { + if (check_simple_select()) + YYABORT; + Select->options|= OPTION_FOUND_ROWS; + } + | SQL_NO_CACHE_SYM + { + Lex->safe_to_cache_query=0; + Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; + } + | SQL_CACHE_SYM + { + /* Honor this flag only if SQL_NO_CACHE wasn't specified. */ + if (Lex->select_lex.sql_cache != SELECT_LEX::SQL_NO_CACHE) + { + Lex->safe_to_cache_query=1; + Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; + } + } + | ALL { Select->options|= SELECT_ALL; } + ; + +select_lock_type: + /* empty */ + | FOR_SYM UPDATE_SYM + { + LEX *lex=Lex; + lex->current_select->set_lock_for_tables(TL_WRITE); + lex->safe_to_cache_query=0; + } + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + { + LEX *lex=Lex; + lex->current_select-> + set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); + lex->safe_to_cache_query=0; + } + ; + +select_item_list: + select_item_list ',' select_item + | select_item + | '*' + { + THD *thd= YYTHD; + if (add_item_to_list(thd, + new Item_field(&thd->lex->current_select-> + context, + NULL, NULL, "*"))) + YYABORT; + (thd->lex->current_select->with_wild)++; + }; + + +select_item: + remember_name select_item2 remember_end select_alias + { + if (add_item_to_list(YYTHD, $2)) + YYABORT; + if ($4.str) + { + $2->is_autogenerated_name= FALSE; + $2->set_name($4.str, $4.length, system_charset_info); + } + else if (!$2->name) { + char *str = $1; + if (str[-1] == '`') + str--; + $2->set_name(str,(uint) ($3 - str), YYTHD->charset()); + } + }; + +remember_name: + { $$=(char*) Lex->tok_start; }; + +remember_end: + { $$=(char*) Lex->tok_end; }; + +select_item2: + table_wild { $$=$1; } /* table.* */ + | expr { $$=$1; }; + +select_alias: + /* empty */ { $$=null_lex_str;} + | AS ident { $$=$2; } + | AS TEXT_STRING_sys { $$=$2; } + | ident { $$=$1; } + | TEXT_STRING_sys { $$=$1; } + ; + +optional_braces: + /* empty */ {} + | '(' ')' {}; + +/* all possible expressions */ +expr: + bool_term { Select->expr_list.push_front(new List<Item>); } + bool_or_expr + { + List<Item> *list= Select->expr_list.pop(); + if (list->elements) + { + list->push_front($1); + $$= new Item_cond_or(*list); + /* optimize construction of logical OR to reduce + amount of objects for complex expressions */ + } + else + $$= $1; + delete list; + } + ; + +bool_or_expr: + /* empty */ + | bool_or_expr or bool_term + { Select->expr_list.head()->push_back($3); } + ; + +bool_term: + bool_term XOR bool_term { $$= new Item_cond_xor($1,$3); } + | bool_factor { Select->expr_list.push_front(new List<Item>); } + bool_and_expr + { + List<Item> *list= Select->expr_list.pop(); + if (list->elements) + { + list->push_front($1); + $$= new Item_cond_and(*list); + /* optimize construction of logical AND to reduce + amount of objects for complex expressions */ + } + else + $$= $1; + delete list; + } + ; + +bool_and_expr: + /* empty */ + | bool_and_expr and bool_factor + { Select->expr_list.head()->push_back($3); } + ; + +bool_factor: + NOT_SYM bool_factor { $$= negate_expression(YYTHD, $2); } + | bool_test ; + +bool_test: + bool_pri IS TRUE_SYM { $$= is_truth_value(YYTHD, $1,1,0); } + | bool_pri IS not TRUE_SYM { $$= is_truth_value(YYTHD, $1,0,0); } + | bool_pri IS FALSE_SYM { $$= is_truth_value(YYTHD, $1,0,1); } + | bool_pri IS not FALSE_SYM { $$= is_truth_value(YYTHD, $1,1,1); } + | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); } + | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); } + | bool_pri ; + +bool_pri: + bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); } + | bool_pri IS not NULL_SYM { $$= new Item_func_isnotnull($1); } + | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); } + | bool_pri comp_op predicate %prec EQ + { $$= (*$2)(0)->create($1,$3); } + | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ + { $$= all_any_subquery_creator($1, $2, $3, $5); } + | predicate ; + +predicate: + bit_expr IN_SYM '(' subselect ')' + { $$= new Item_in_subselect($1, $4); } + | bit_expr not IN_SYM '(' subselect ')' + { $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); } + | bit_expr IN_SYM '(' expr ')' + { + $$= new Item_func_eq($1, $4); + } + | bit_expr IN_SYM '(' expr ',' expr_list ')' + { + $6->push_front($4); + $6->push_front($1); + $$= new Item_func_in(*$6); + } + | bit_expr not IN_SYM '(' expr ')' + { + $$= new Item_func_ne($1, $5); + } + | bit_expr not IN_SYM '(' expr ',' expr_list ')' + { + $7->push_front($5); + $7->push_front($1); + Item_func_in *item = new Item_func_in(*$7); + item->negate(); + $$= item; + } + | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + { $$= new Item_func_between($1,$3,$5); } + | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } + | bit_expr SOUNDS_SYM LIKE bit_expr + { $$= new Item_func_eq(new Item_func_soundex($1), + new Item_func_soundex($4)); } + | bit_expr LIKE simple_expr opt_escape + { $$= new Item_func_like($1,$3,$4,Lex->escape_used); } + | bit_expr not LIKE simple_expr opt_escape + { $$= new Item_func_not(new Item_func_like($1,$4,$5, Lex->escape_used)); } + | bit_expr REGEXP bit_expr { $$= new Item_func_regex($1,$3); } + | bit_expr not REGEXP bit_expr + { $$= negate_expression(YYTHD, new Item_func_regex($1,$4)); } + | bit_expr ; + +bit_expr: + bit_expr '|' bit_term { $$= new Item_func_bit_or($1,$3); } + | bit_term ; + +bit_term: + bit_term '&' bit_factor { $$= new Item_func_bit_and($1,$3); } + | bit_factor ; + +bit_factor: + bit_factor SHIFT_LEFT value_expr + { $$= new Item_func_shift_left($1,$3); } + | bit_factor SHIFT_RIGHT value_expr + { $$= new Item_func_shift_right($1,$3); } + | value_expr ; + +value_expr: + value_expr '+' term { $$= new Item_func_plus($1,$3); } + | value_expr '-' term { $$= new Item_func_minus($1,$3); } + | value_expr '+' interval_expr interval + { $$= new Item_date_add_interval($1,$3,$4,0); } + | value_expr '-' interval_expr interval + { $$= new Item_date_add_interval($1,$3,$4,1); } + | term ; + +term: + term '*' factor { $$= new Item_func_mul($1,$3); } + | term '/' factor { $$= new Item_func_div($1,$3); } + | term '%' factor { $$= new Item_func_mod($1,$3); } + | term DIV_SYM factor { $$= new Item_func_int_div($1,$3); } + | term MOD_SYM factor { $$= new Item_func_mod($1,$3); } + | factor ; + +factor: + factor '^' simple_expr { $$= new Item_func_bit_xor($1,$3); } + | simple_expr ; + +or: OR_SYM | OR2_SYM; +and: AND_SYM | AND_AND_SYM; +not: NOT_SYM | NOT2_SYM; +not2: '!' | NOT2_SYM; + +comp_op: EQ { $$ = &comp_eq_creator; } + | GE { $$ = &comp_ge_creator; } + | GT_SYM { $$ = &comp_gt_creator; } + | LE { $$ = &comp_le_creator; } + | LT { $$ = &comp_lt_creator; } + | NE { $$ = &comp_ne_creator; } + ; + +all_or_any: ALL { $$ = 1; } + | ANY_SYM { $$ = 0; } + ; + +interval_expr: + INTERVAL_SYM expr { $$=$2; } + ; + +simple_expr: + simple_ident + | function_call_keyword + | function_call_nonkeyword + | function_call_generic + | function_call_conflict + | simple_expr COLLATE_SYM ident_or_text %prec NEG + { + THD *thd= YYTHD; + Item *i1= new (thd->mem_root) Item_string($3.str, + $3.length, + thd->charset()); + $$= new (thd->mem_root) Item_func_set_collation($1, i1); + } + | literal + | param_marker + | variable + | sum_expr + | simple_expr OR_OR_SYM simple_expr + { $$= new (YYTHD->mem_root) Item_func_concat($1, $3); } + | '+' simple_expr %prec NEG { $$= $2; } + | '-' simple_expr %prec NEG + { $$= new (YYTHD->mem_root) Item_func_neg($2); } + | '~' simple_expr %prec NEG + { $$= new (YYTHD->mem_root) Item_func_bit_neg($2); } + | not2 simple_expr %prec NEG + { $$= negate_expression(YYTHD, $2); } + | '(' subselect ')' + { + $$= new (YYTHD->mem_root) Item_singlerow_subselect($2); + } + | '(' expr ')' { $$= $2; } + | '(' expr ',' expr_list ')' + { + $4->push_front($2); + $$= new (YYTHD->mem_root) Item_row(*$4); + } + | ROW_SYM '(' expr ',' expr_list ')' + { + $5->push_front($3); + $$= new (YYTHD->mem_root) Item_row(*$5); + } + | EXISTS '(' subselect ')' + { + $$= new (YYTHD->mem_root) Item_exists_subselect($3); + } + | '{' ident expr '}' { $$= $3; } + | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' + { + $2->push_front($5); + Item_func_match *i1= new (YYTHD->mem_root) Item_func_match(*$2, $6); + Select->add_ftfunc_to_list(i1); + $$= i1; + } + | BINARY simple_expr %prec NEG + { + $$= create_func_cast(YYTHD, $2, ITEM_CAST_CHAR, -1, 0, + &my_charset_bin); + } + | CAST_SYM '(' expr AS cast_type ')' + { + LEX *lex= Lex; + $$= create_func_cast(YYTHD, $3, $5, + lex->length ? atoi(lex->length) : -1, + lex->dec ? atoi(lex->dec) : 0, + lex->charset); + if (!$$) + YYABORT; + } + | CASE_SYM opt_expr WHEN_SYM when_list opt_else END + { $$= new (YYTHD->mem_root) Item_func_case(* $4, $2, $5 ); } + | CONVERT_SYM '(' expr ',' cast_type ')' + { + $$= create_func_cast(YYTHD, $3, $5, + Lex->length ? atoi(Lex->length) : -1, + Lex->dec ? atoi(Lex->dec) : 0, + Lex->charset); + if (!$$) + YYABORT; + } + | CONVERT_SYM '(' expr USING charset_name ')' + { $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5); } + | DEFAULT '(' simple_ident ')' + { + if ($3->is_splocal()) + { + Item_splocal *il= static_cast<Item_splocal *>($3); + + my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str); + YYABORT; + } + $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(), + $3); + } + | VALUES '(' simple_ident_nospvar ')' + { $$= new (YYTHD->mem_root) Item_insert_value(Lex->current_context(), + $3); } + | interval_expr interval '+' expr + /* we cannot put interval before - */ + { $$= new (YYTHD->mem_root) Item_date_add_interval($4,$1,$2,0); } + | interval_expr + { + if ($1->type() != Item::ROW_ITEM) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + $$= new (YYTHD->mem_root) Item_func_interval((Item_row *)$1); + } + | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' + { + $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); + } + ; + +/* + Function call syntax using official SQL 2003 keywords. + Because the function name is an official token, + a dedicated grammar rule is needed in the parser. + There is no potential for conflicts +*/ +function_call_keyword: + CHAR_SYM '(' expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_char(*$3); } + | CHAR_SYM '(' expr_list USING charset_name ')' + { $$= new (YYTHD->mem_root) Item_func_char(*$3, $5); } + | CURRENT_USER optional_braces + { + $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context()); + Lex->safe_to_cache_query= 0; + } + | DATE_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_date_typecast($3); } + | DAY_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_dayofmonth($3); } + | HOUR_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_hour($3); } + | INSERT '(' expr ',' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_insert($3,$5,$7,$9); } + | LEFT '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_left($3,$5); } + | MINUTE_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_minute($3); } + | MONTH_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_month($3); } + | RIGHT '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_right($3,$5); } + | SECOND_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_second($3); } + | TIME_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_time_typecast($3); } + | TIMESTAMP '(' expr ')' + { $$= new (YYTHD->mem_root) Item_datetime_typecast($3); } + | TIMESTAMP '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_add_time($3, $5, 1, 0); } + | TRIM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($3); } + | TRIM '(' LEADING expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_ltrim($6,$4); } + | TRIM '(' TRAILING expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_rtrim($6,$4); } + | TRIM '(' BOTH expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($6,$4); } + | TRIM '(' LEADING FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_ltrim($5); } + | TRIM '(' TRAILING FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_rtrim($5); } + | TRIM '(' BOTH FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($5); } + | TRIM '(' expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_trim($5,$3); } + | USER '(' ')' + { + $$= new (YYTHD->mem_root) Item_func_user(); + Lex->safe_to_cache_query=0; + } + | YEAR_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_year($3); } + ; + +/* + Function calls using non reserved keywords, with special syntaxic forms. + Dedicated grammar rules are needed because of the syntax, + but also have the potential to cause incompatibilities with other + parts of the language. + MAINTAINER: + The only reasons a function should be added here are: + - for compatibility reasons with another SQL syntax (CURDATE), + - for typing reasons (GET_FORMAT) + Any other 'Syntaxic sugar' enhancements should be *STRONGLY* + discouraged. +*/ +function_call_nonkeyword: + ADDDATE_SYM '(' expr ',' expr ')' + { + $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5, + INTERVAL_DAY, 0); + } + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 0); } + | CURDATE optional_braces + { + $$= new (YYTHD->mem_root) Item_func_curdate_local(); + Lex->safe_to_cache_query=0; + } + | CURTIME optional_braces + { + $$= new (YYTHD->mem_root) Item_func_curtime_local(); + Lex->safe_to_cache_query=0; + } + | CURTIME '(' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_curtime_local($3); + Lex->safe_to_cache_query=0; + } + | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,0); } + | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,1); } + | EXTRACT_SYM '(' interval FROM expr ')' + { $$=new (YYTHD->mem_root) Item_extract( $3, $5); } + | GET_FORMAT '(' date_time_type ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_get_format($3, $5); } + | NOW_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_now_local(); + Lex->safe_to_cache_query=0; + } + | NOW_SYM '(' expr ')' + { + $$= new (YYTHD->mem_root) Item_func_now_local($3); + Lex->safe_to_cache_query=0; + } + | POSITION_SYM '(' bit_expr IN_SYM expr ')' + { $$ = new (YYTHD->mem_root) Item_func_locate($5,$3); } + | SUBDATE_SYM '(' expr ',' expr ')' + { + $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5, + INTERVAL_DAY, 1); + } + | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1); } + | SUBSTRING '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } + | SUBSTRING '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } + | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7); } + | SUBSTRING '(' expr FROM expr ')' + { $$= new (YYTHD->mem_root) Item_func_substr($3,$5); } + | SYSDATE optional_braces + { + if (global_system_variables.sysdate_is_now == 0) + $$= new (YYTHD->mem_root) Item_func_sysdate_local(); + else + $$= new (YYTHD->mem_root) Item_func_now_local(); + Lex->safe_to_cache_query=0; + } + | SYSDATE '(' expr ')' + { + if (global_system_variables.sysdate_is_now == 0) + $$= new (YYTHD->mem_root) Item_func_sysdate_local($3); + else + $$= new (YYTHD->mem_root) Item_func_now_local($3); + Lex->safe_to_cache_query=0; + } + | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0); } + | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3); } + | UTC_DATE_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_curdate_utc(); + Lex->safe_to_cache_query=0; + } + | UTC_TIME_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_curtime_utc(); + Lex->safe_to_cache_query=0; + } + | UTC_TIMESTAMP_SYM optional_braces + { + $$= new (YYTHD->mem_root) Item_func_now_utc(); + Lex->safe_to_cache_query=0; + } + ; + +/* + Functions calls using a non reserved keywork, and using a regular syntax. + Because the non reserved keyword is used in another part of the grammar, + a dedicated rule is needed here. +*/ +function_call_conflict: + ASCII_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_ascii($3); } + | CHARSET '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_charset($3); } + | COALESCE '(' expr_list ')' + { $$= new (YYTHD->mem_root) Item_func_coalesce(* $3); } + | COLLATION_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_collation($3); } + | DATABASE '(' ')' + { + $$= new (YYTHD->mem_root) Item_func_database(); + Lex->safe_to_cache_query=0; + } + | IF '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_if($3,$5,$7); } + | MICROSECOND_SYM '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_microsecond($3); } + | MOD_SYM '(' expr ',' expr ')' + { $$ = new (YYTHD->mem_root) Item_func_mod( $3, $5); } + | OLD_PASSWORD '(' expr ')' + { $$= new (YYTHD->mem_root) Item_func_old_password($3); } + | PASSWORD '(' expr ')' + { + THD *thd= YYTHD; + Item* i1; + if (thd->variables.old_passwords) + i1= new (thd->mem_root) Item_func_old_password($3); + else + i1= new (thd->mem_root) Item_func_password($3); + $$= i1; + } + | QUARTER_SYM '(' expr ')' + { $$ = new (YYTHD->mem_root) Item_func_quarter($3); } + | REPEAT_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_repeat($3,$5); } + | REPLACE '(' expr ',' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_replace($3,$5,$7); } + | TRUNCATE_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_round($3,$5,1); } + | WEEK_SYM '(' expr ')' + { + THD *thd= YYTHD; + Item *i1= new (thd->mem_root) Item_int((char*) "0", + thd->variables.default_week_format, + 1); + + $$= new (thd->mem_root) Item_func_week($3, i1); + } + | WEEK_SYM '(' expr ',' expr ')' + { $$= new (YYTHD->mem_root) Item_func_week($3,$5); } + | geometry_function + { +#ifdef HAVE_SPATIAL + $$= $1; +#else + my_error(ER_FEATURE_DISABLED, MYF(0), + sym_group_geom.name, sym_group_geom.needed_define); + YYABORT; +#endif + } + ; + +geometry_function: + CONTAINS_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_rel($3, $5, + Item_func::SP_CONTAINS_FUNC)); + } + | GEOMETRYCOLLECTION '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_geometrycollection, + Geometry::wkb_point)); + } + | LINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_linestring, + Geometry::wkb_point)); + } + | MULTILINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multilinestring, + Geometry::wkb_linestring)); + } + | MULTIPOINT '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multipoint, + Geometry::wkb_point)); + } + | MULTIPOLYGON '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_multipolygon, + Geometry::wkb_polygon)); + } + | POINT_SYM '(' expr ',' expr ')' + { $$= GEOM_NEW(YYTHD, Item_func_point($3,$5)); } + | POLYGON '(' expr_list ')' + { + $$= GEOM_NEW(YYTHD, + Item_func_spatial_collection(* $3, + Geometry::wkb_polygon, + Geometry::wkb_linestring)); + } + ; + +/* + Regular function calls. + The function name is *not* a token, and therefore is guaranteed to not + introduce side effects to the language in general. + MAINTAINER: + All the new functions implemented for new features should fit into + this category. The place to implement the function itself is + in sql/item_create.cc +*/ +function_call_generic: + IDENT_sys '(' + { +#ifdef HAVE_DLOPEN + udf_func *udf= 0; + LEX *lex= Lex; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) + { + if (lex->current_select->inc_in_sum_expr()) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + } + /* Temporary placing the result of find_udf in $3 */ + lex->current_select->udf_list.push_front(udf); +#endif + } + udf_expr_list ')' + { + THD *thd= YYTHD; + LEX *lex= Lex; + Create_func *builder; + Item *item= NULL; + + /* + Implementation note: + names are resolved with the following order: + - MySQL native functions, + - User Defined Functions, + - Stored Functions (assuming the current <use> database) + + This will be revised with WL#2128 (SQL PATH) + */ + builder= find_native_function_builder(thd, $1); + if (builder) + { + item= builder->create(thd, $1, $4); + } + else + { +#ifdef HAVE_DLOPEN + /* Retrieving the result of find_udf */ + udf_func *udf; + LEX *lex= Lex; + + if (NULL != (udf= lex->current_select->udf_list.pop())) + { + if (udf->type == UDFTYPE_AGGREGATE) + { + Select->in_sum_expr--; + } + + item= Create_udf_func::s_singleton.create(thd, udf, $4); + } + else +#endif + { + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $4); + } + } + + if (! ($$= item)) + { + YYABORT; + } + } + | ident '.' ident '(' opt_expr_list ')' + { + THD *thd= YYTHD; + Create_qfunc *builder; + Item *item= NULL; + + /* + The following in practice calls: + <code>Create_sp_func::create()</code> + and builds a stored function. + + However, it's important to maintain the interface between the + parser and the implementation in item_create.cc clean, + since this will change with WL#2128 (SQL PATH): + - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native + funtion version(), + - MySQL.version() is the SQL 2003 syntax for the native function + version() (a vendor can specify any schema). + */ + + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create(thd, $1, $3, $5); + + if (! ($$= item)) + { + YYABORT; + } + } + ; + +fulltext_options: + opt_natural_language_mode opt_query_expansion + { $$= $1 | $2; } + | IN_SYM BOOLEAN_SYM MODE_SYM + { $$= FT_BOOL; } + ; + +opt_natural_language_mode: + /* nothing */ { $$= FT_NL; } + | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; } + ; + +opt_query_expansion: + /* nothing */ { $$= 0; } + | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; } + ; + +udf_expr_list: + /* empty */ { $$= NULL; } + | udf_expr_list2 { $$= $1;} + ; + +udf_expr_list2: + { Select->expr_list.push_front(new List<Item>); } + udf_expr_list3 + { $$= Select->expr_list.pop(); } + ; + +udf_expr_list3: + udf_expr + { + Select->expr_list.head()->push_back($1); + } + | udf_expr_list3 ',' udf_expr + { + Select->expr_list.head()->push_back($3); + } + ; + +udf_expr: + remember_name expr remember_end select_alias + { + udf_func *udf= Select->udf_list.head(); + /* + Use Item::name as a storage for the attribute value of user + defined function argument. It is safe to use Item::name + because the syntax will not allow having an explicit name here. + See WL#1017 re. udf attributes. + */ + if ($4.str) + { + if (!udf) + { + /* + Disallow using AS to specify explicit names for the arguments + of stored routine calls + */ + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + + $2->is_autogenerated_name= FALSE; + $2->set_name($4.str, $4.length, system_charset_info); + } + else if (udf) + $2->set_name($1, (uint) ($3 - $1), YYTHD->charset()); + $$= $2; + } + ; + +sum_expr: + AVG_SYM '(' in_sum_expr ')' + { $$=new Item_sum_avg($3); } + | AVG_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_avg_distinct($4); } + | BIT_AND '(' in_sum_expr ')' + { $$=new Item_sum_and($3); } + | BIT_OR '(' in_sum_expr ')' + { $$=new Item_sum_or($3); } + | BIT_XOR '(' in_sum_expr ')' + { $$=new Item_sum_xor($3); } + | COUNT_SYM '(' opt_all '*' ')' + { $$=new Item_sum_count(new Item_int((int32) 0L,1)); } + | COUNT_SYM '(' in_sum_expr ')' + { $$=new Item_sum_count($3); } + | COUNT_SYM '(' DISTINCT + { Select->in_sum_expr++; } + expr_list + { Select->in_sum_expr--; } + ')' + { $$=new Item_sum_count_distinct(* $5); } + | GROUP_UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' in_sum_expr ')' + { $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); } + | MIN_SYM '(' in_sum_expr ')' + { $$=new Item_sum_min($3); } +/* + According to ANSI SQL, DISTINCT is allowed and has + no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...) + is processed like an ordinary MIN | MAX() + */ + | MIN_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_min($4); } + | MAX_SYM '(' in_sum_expr ')' + { $$=new Item_sum_max($3); } + | MAX_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_max($4); } + | STD_SYM '(' in_sum_expr ')' + { $$=new Item_sum_std($3, 0); } + | VARIANCE_SYM '(' in_sum_expr ')' + { $$=new Item_sum_variance($3, 0); } + | STDDEV_SAMP_SYM '(' in_sum_expr ')' + { $$=new Item_sum_std($3, 1); } + | VAR_SAMP_SYM '(' in_sum_expr ')' + { $$=new Item_sum_variance($3, 1); } + | SUM_SYM '(' in_sum_expr ')' + { $$=new Item_sum_sum($3); } + | SUM_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_sum_distinct($4); } + | GROUP_CONCAT_SYM '(' opt_distinct + { Select->in_sum_expr++; } + expr_list opt_gorder_clause + opt_gconcat_separator + ')' + { + SELECT_LEX *sel= Select; + sel->in_sum_expr--; + $$=new Item_func_group_concat(Lex->current_context(), $3, $5, + sel->gorder_list, $7); + $5->empty(); + }; + +variable: + '@' + { + if (! Lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + YYABORT; + } + } + variable_aux + { + $$= $3; + } + ; + +variable_aux: + ident_or_text SET_VAR expr + { + $$= new Item_func_set_user_var($1, $3); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + } + | ident_or_text + { + $$= new Item_func_get_user_var($1); + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_RAND); + } + | '@' opt_var_ident_type ident_or_text opt_component + { + if ($3.str && $4.str && check_reserved_words(&$3)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if (!($$= get_system_var(YYTHD, $2, $3, $4))) + YYABORT; + } + ; + +opt_distinct: + /* empty */ { $$ = 0; } + |DISTINCT { $$ = 1; }; + +opt_gconcat_separator: + /* empty */ { $$ = new (YYTHD->mem_root) String(",",1,default_charset_info); } + |SEPARATOR_SYM text_string { $$ = $2; }; + + +opt_gorder_clause: + /* empty */ + { + Select->gorder_list = NULL; + } + | order_clause + { + SELECT_LEX *select= Select; + select->gorder_list= + (SQL_LIST*) sql_memdup((char*) &select->order_list, + sizeof(st_sql_list)); + select->order_list.empty(); + }; + + +in_sum_expr: + opt_all + { + LEX *lex= Lex; + if (lex->current_select->inc_in_sum_expr()) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + } + expr + { + Select->in_sum_expr--; + $$= $3; + }; + +cast_type: + BINARY opt_len { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; } + | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; Lex->dec= 0; } + | NCHAR_SYM opt_len { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; } + | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } + ; + +opt_expr_list: + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; + +expr_list: + { Select->expr_list.push_front(new List<Item>); } + expr_list2 + { $$= Select->expr_list.pop(); }; + +expr_list2: + expr { Select->expr_list.head()->push_back($1); } + | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); }; + +ident_list_arg: + ident_list { $$= $1; } + | '(' ident_list ')' { $$= $2; }; + +ident_list: + { Select->expr_list.push_front(new List<Item>); } + ident_list2 + { $$= Select->expr_list.pop(); }; + +ident_list2: + simple_ident { Select->expr_list.head()->push_back($1); } + | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); }; + +opt_expr: + /* empty */ { $$= NULL; } + | expr { $$= $1; }; + +opt_else: + /* empty */ { $$= NULL; } + | ELSE expr { $$= $2; }; + +when_list: + { Select->when_list.push_front(new List<Item>); } + when_list2 + { $$= Select->when_list.pop(); }; + +when_list2: + expr THEN_SYM expr + { + SELECT_LEX *sel=Select; + sel->when_list.head()->push_back($1); + sel->when_list.head()->push_back($3); + } + | when_list2 WHEN_SYM expr THEN_SYM expr + { + SELECT_LEX *sel=Select; + sel->when_list.head()->push_back($3); + sel->when_list.head()->push_back($5); + }; + +/* Warning - may return NULL in case of incomplete SELECT */ +table_ref: + table_factor { $$=$1; } + | join_table + { + LEX *lex= Lex; + if (!($$= lex->current_select->nest_last_join(lex->thd))) + YYABORT; + } + ; + +join_table_list: + derived_table_list { YYERROR_UNLESS($$=$1); } + ; + +/* Warning - may return NULL in case of incomplete SELECT */ +derived_table_list: + table_ref { $$=$1; } + | derived_table_list ',' table_ref + { + YYERROR_UNLESS($1 && ($$=$3)); + } + ; + +/* + Notice that JOIN is a left-associative operation, and it must be parsed + as such, that is, the parser must process first the left join operand + then the right one. Such order of processing ensures that the parser + produces correct join trees which is essential for semantic analysis + and subsequent optimization phases. +*/ +join_table: +/* INNER JOIN variants */ + /* + Use %prec to evaluate production 'table_ref' before 'normal_join' + so that [INNER | CROSS] JOIN is properly nested as other + left-associative joins. + */ + table_ref %prec TABLE_REF_PRIORITY normal_join table_ref + { YYERROR_UNLESS($1 && ($$=$3)); } + | table_ref STRAIGHT_JOIN table_factor + { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; } + | table_ref normal_join table_ref + ON + { + YYERROR_UNLESS($1 && $3); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(YYTHD, $1, $3)) + YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + add_join_on($3,$6); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref STRAIGHT_JOIN table_factor + ON + { + YYERROR_UNLESS($1 && $3); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(YYTHD, $1, $3)) + YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + $3->straight=1; + add_join_on($3,$6); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref normal_join table_ref + USING + { + SELECT_LEX *sel= Select; + YYERROR_UNLESS($1 && $3); + } + '(' using_list ')' + { add_join_natural($1,$3,$7); $$=$3; } + | table_ref NATURAL JOIN_SYM table_factor + { + YYERROR_UNLESS($1 && ($$=$4)); + add_join_natural($1,$4,NULL); + } + +/* LEFT JOIN variants */ + | table_ref LEFT opt_outer JOIN_SYM table_ref + ON + { + YYERROR_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(YYTHD, $1, $5)) + YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + add_join_on($5,$8); + Lex->pop_context(); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + Select->parsing_place= NO_MATTER; + } + | table_ref LEFT opt_outer JOIN_SYM table_factor + { + SELECT_LEX *sel= Select; + YYERROR_UNLESS($1 && $5); + } + USING '(' using_list ')' + { add_join_natural($1,$5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } + | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor + { + YYERROR_UNLESS($1 && $6); + add_join_natural($1,$6,NULL); + $6->outer_join|=JOIN_TYPE_LEFT; + $$=$6; + } + +/* RIGHT JOIN variants */ + | table_ref RIGHT opt_outer JOIN_SYM table_ref + ON + { + YYERROR_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(YYTHD, $1, $5)) + YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + YYABORT; + add_join_on($$, $8); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref RIGHT opt_outer JOIN_SYM table_factor + { + SELECT_LEX *sel= Select; + YYERROR_UNLESS($1 && $5); + } + USING '(' using_list ')' + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + YYABORT; + add_join_natural($$,$5,$9); + } + | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor + { + YYERROR_UNLESS($1 && $6); + add_join_natural($6,$1,NULL); + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + YYABORT; + }; + +normal_join: + JOIN_SYM {} + | INNER_SYM JOIN_SYM {} + | CROSS JOIN_SYM {} + ; + +/* Warning - may return NULL in case of incomplete SELECT */ +table_factor: + { + SELECT_LEX *sel= Select; + sel->use_index_ptr=sel->ignore_index_ptr=0; + sel->table_join_options= 0; + } + table_ident opt_table_alias opt_key_definition + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (!($$= sel->add_table_to_list(lex->thd, $2, $3, + sel->get_table_join_options(), + lex->lock_option, + sel->get_use_index(), + sel->get_ignore_index()))) + YYABORT; + sel->add_joined_table($$); + } + | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref + ON + { + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(YYTHD, $3, $7)) + YYABORT; + + } + expr '}' + { + LEX *lex= Lex; + YYERROR_UNLESS($3 && $7); + add_join_on($7,$10); + Lex->pop_context(); + $7->outer_join|=JOIN_TYPE_LEFT; + $$=$7; + if (!($$= lex->current_select->nest_last_join(lex->thd))) + YYABORT; + } + | select_derived_init get_select_lex select_derived2 + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if ($1) + { + if (sel->set_braces(1)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + /* select in braces, can't contain global parameters */ + if (sel->master_unit()->fake_select_lex) + sel->master_unit()->global_parameters= + sel->master_unit()->fake_select_lex; + } + if ($2->init_nested_join(lex->thd)) + YYABORT; + $$= 0; + /* incomplete derived tables return NULL, we must be + nested in select_derived rule to be here. */ + } + | '(' get_select_lex select_derived union_opt ')' opt_table_alias + { + /* Use $2 instead of Lex->current_select as derived table will + alter value of Lex->current_select. */ + + if (!($3 || $6) && $2->embedding && + !$2->embedding->nested_join->join_list.elements) + { + /* we have a derived table ($3 == NULL) but no alias, + Since we are nested in further parentheses so we + can pass NULL to the outer level parentheses + Permits parsing of "((((select ...))) as xyz)" */ + $$= 0; + } + else + if (!$3) + { + /* Handle case of derived table, alias may be NULL if there + are no outer parentheses, add_table_to_list() will throw + error in this case */ + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + lex->current_select= sel= unit->outer_select(); + if (!($$= sel-> + add_table_to_list(lex->thd, new Table_ident(unit), $6, 0, + TL_READ,(List<String> *)0, + (List<String> *)0))) + + YYABORT; + sel->add_joined_table($$); + lex->pop_context(); + } + else + if ($4 || $6) + { + /* simple nested joins cannot have aliases or unions */ + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + else + $$= $3; + } + ; + +/* handle contents of parentheses in join expression */ +select_derived: + get_select_lex + { + LEX *lex= Lex; + if ($1->init_nested_join(lex->thd)) + YYABORT; + } + derived_table_list + { + LEX *lex= Lex; + /* for normal joins, $3 != NULL and end_nested_join() != NULL, + for derived tables, both must equal NULL */ + + if (!($$= $1->end_nested_join(lex->thd)) && $3) + YYABORT; + if (!$3 && $$) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + } + ; + +select_derived2: + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1)) + YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_select_from + ; + +get_select_lex: + /* Empty */ { $$= Select; } + ; + +select_derived_init: + SELECT_SYM + { + LEX *lex= Lex; + + if (! lex->parsing_options.allows_derived) + { + my_error(ER_VIEW_SELECT_DERIVED, MYF(0)); + YYABORT; + } + + SELECT_LEX *sel= lex->current_select; + TABLE_LIST *embedding; + if (!sel->embedding || sel->end_nested_join(lex->thd)) + { + /* we are not in parentheses */ + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + embedding= Select->embedding; + $$= embedding && + !embedding->nested_join->join_list.elements; + /* return true if we are deeply nested */ + } + ; + +opt_outer: + /* empty */ {} + | OUTER {}; + +opt_key_definition: + /* empty */ {} + | USE_SYM key_usage_list + { + SELECT_LEX *sel= Select; + sel->use_index= *$2; + sel->use_index_ptr= &sel->use_index; + } + | FORCE_SYM key_usage_list + { + SELECT_LEX *sel= Select; + sel->use_index= *$2; + sel->use_index_ptr= &sel->use_index; + sel->table_join_options|= TL_OPTION_FORCE_INDEX; + } + | IGNORE_SYM key_usage_list + { + SELECT_LEX *sel= Select; + sel->ignore_index= *$2; + sel->ignore_index_ptr= &sel->ignore_index; + }; + +key_usage_list: + key_or_index { Select->interval_list.empty(); } + '(' key_list_or_empty ')' + { $$= &Select->interval_list; } + ; + +key_list_or_empty: + /* empty */ {} + | key_usage_list2 {} + ; + +key_usage_list2: + key_usage_list2 ',' ident + { Select-> + interval_list.push_back(new (YYTHD->mem_root) String((const char*) $3.str, $3.length, + system_charset_info)); } + | ident + { Select-> + interval_list.push_back(new (YYTHD->mem_root) String((const char*) $1.str, $1.length, + system_charset_info)); } + | PRIMARY_SYM + { Select-> + interval_list.push_back(new (YYTHD->mem_root) String("PRIMARY", 7, + system_charset_info)); }; + +using_list: + ident + { + if (!($$= new List<String>)) + YYABORT; + $$->push_back(new (YYTHD->mem_root) + String((const char *) $1.str, $1.length, + system_charset_info)); + } + | using_list ',' ident + { + $1->push_back(new (YYTHD->mem_root) + String((const char *) $3.str, $3.length, + system_charset_info)); + $$= $1; + }; + +interval: + interval_time_st {} + | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } + | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } + | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } + | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } + | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } + | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } + | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } + | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } + | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } + | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } + | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }; + +interval_time_st: + DAY_SYM { $$=INTERVAL_DAY; } + | WEEK_SYM { $$=INTERVAL_WEEK; } + | HOUR_SYM { $$=INTERVAL_HOUR; } + | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; } + | MINUTE_SYM { $$=INTERVAL_MINUTE; } + | MONTH_SYM { $$=INTERVAL_MONTH; } + | QUARTER_SYM { $$=INTERVAL_QUARTER; } + | SECOND_SYM { $$=INTERVAL_SECOND; } + | YEAR_SYM { $$=INTERVAL_YEAR; } + ; + +date_time_type: + DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;} + | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;} + | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;} + | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} + ; + +table_alias: + /* empty */ + | AS + | EQ; + +opt_table_alias: + /* empty */ { $$=0; } + | table_alias ident + { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); }; + +opt_all: + /* empty */ + | ALL + ; + +where_clause: + /* empty */ { Select->where= 0; } + | WHERE + { + Select->parsing_place= IN_WHERE; + } + expr + { + SELECT_LEX *select= Select; + select->where= $3; + select->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +having_clause: + /* empty */ + | HAVING + { + Select->parsing_place= IN_HAVING; + } + expr + { + SELECT_LEX *sel= Select; + sel->having= $3; + sel->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +opt_escape: + ESCAPE_SYM simple_expr + { + Lex->escape_used= TRUE; + $$= $2; + } + | /* empty */ + { + Lex->escape_used= FALSE; + $$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? + new Item_string("", 0, &my_charset_latin1) : + new Item_string("\\", 1, &my_charset_latin1)); + } + ; + + +/* + group by statement in select +*/ + +group_clause: + /* empty */ + | GROUP BY group_list olap_opt; + +group_list: + group_list ',' order_ident order_dir + { if (add_group_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + | order_ident order_dir + { if (add_group_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + +olap_opt: + /* empty */ {} + | WITH CUBE_SYM + { + LEX *lex=Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE", + "global union parameters"); + YYABORT; + } + lex->current_select->olap= CUBE_TYPE; + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"); + YYABORT; /* To be deleted in 5.1 */ + } + | WITH ROLLUP_SYM + { + LEX *lex= Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", + "global union parameters"); + YYABORT; + } + lex->current_select->olap= ROLLUP_TYPE; + } + ; + +/* + Order by statement in select +*/ + +opt_order_clause: + /* empty */ + | order_clause; + +order_clause: + ORDER_SYM BY + { + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); + YYABORT; + } + if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!first_sl->next_select() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(lex->thd)) + YYABORT; + } + } order_list; + +order_list: + order_list ',' order_ident order_dir + { if (add_order_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + | order_ident order_dir + { if (add_order_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + +order_dir: + /* empty */ { $$ = 1; } + | ASC { $$ =1; } + | DESC { $$ =0; }; + + +opt_limit_clause_init: + /* empty */ + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + sel->offset_limit= 0; + sel->select_limit= 0; + } + | limit_clause {} + ; + +opt_limit_clause: + /* empty */ {} + | limit_clause {} + ; + +limit_clause: + LIMIT limit_options {} + ; + +limit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; +limit_option: + param_marker + | ULONGLONG_NUM { $$= new Item_uint($1.str, $1.length); } + | LONG_NUM { $$= new Item_uint($1.str, $1.length); } + | NUM { $$= new Item_uint($1.str, $1.length); } + ; + +delete_limit_clause: + /* empty */ + { + LEX *lex=Lex; + lex->current_select->select_limit= 0; + } + | LIMIT limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $2; + sel->explicit_limit= 1; + }; + +ulong_num: + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + ; + +real_ulong_num: + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { YYABORT; } + ; + +ulonglong_num: + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + ; + +real_ulonglong_num: + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { YYABORT; } + ; + +dec_num_error: + dec_num + { yyerror(ER(ER_ONLY_INTEGERS_ALLOWED)); } + ; + +dec_num: + DECIMAL_NUM + | FLOAT_NUM + ; + +procedure_clause: + /* empty */ + | PROCEDURE ident /* Procedure name */ + { + LEX *lex=Lex; + + if (! lex->parsing_options.allows_select_procedure) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "PROCEDURE"); + YYABORT; + } + + if (&lex->select_lex != lex->current_select) + { + my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); + YYABORT; + } + lex->proc_list.elements=0; + lex->proc_list.first=0; + lex->proc_list.next= (byte**) &lex->proc_list.first; + if (add_proc_to_list(lex->thd, new Item_field(&lex-> + current_select-> + context, + NULL,NULL,$2.str))) + YYABORT; + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + '(' procedure_list ')'; + + +procedure_list: + /* empty */ {} + | procedure_list2 {}; + +procedure_list2: + procedure_list2 ',' procedure_item + | procedure_item; + +procedure_item: + remember_name expr + { + LEX *lex= Lex; + if (add_proc_to_list(lex->thd, $2)) + YYABORT; + if (!$2->name) + $2->set_name($1,(uint) ((char*) lex->tok_end - $1), + YYTHD->charset()); + } + ; + + +select_var_list_init: + { + LEX *lex=Lex; + if (!lex->describe && (!(lex->result= new select_dumpvar()))) + YYABORT; + } + select_var_list + {} + ; + +select_var_list: + select_var_list ',' select_var_ident + | select_var_ident {} + ; + +select_var_ident: + '@' ident_or_text + { + LEX *lex=Lex; + if (lex->result) + ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); + else + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); + } + | ident_or_text + { + LEX *lex=Lex; + sp_variable_t *t; + + if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); + YYABORT; + } + if (lex->result) + { + my_var *var; + ((select_dumpvar *)lex->result)-> + var_list.push_back(var= new my_var($1,1,t->offset,t->type)); +#ifndef DBUG_OFF + if (var) + var->sp= lex->sphead; +#endif + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); + } + } + ; + +into: + INTO + { + if (! Lex->parsing_options.allows_select_into) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO"); + YYABORT; + } + } + into_destination + ; + +into_destination: + OUTFILE TEXT_STRING_filesystem + { + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new sql_exchange($2.str, 0)) || + !(lex->result= new select_export(lex->exchange))) + YYABORT; + } + opt_field_term opt_line_term + | DUMPFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + if (!lex->describe) + { + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new sql_exchange($2.str,1))) + YYABORT; + if (!(lex->result= new select_dump(lex->exchange))) + YYABORT; + } + } + | select_var_list_init + { + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + ; + +/* + DO statement +*/ + +do: DO_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DO; + mysql_init_select(lex); + } + expr_list + { + Lex->insert_list= $3; + } + ; + +/* + Drop : delete tables or index or user +*/ + +drop: + DROP opt_temporary table_or_tables if_exists table_list opt_restrict + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DROP_TABLE; + lex->drop_temporary= $2; + lex->drop_if_exists= $4; + } + | DROP INDEX_SYM ident ON table_ident {} + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_INDEX; + lex->alter_info.drop_list.empty(); + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, + TL_OPTION_UPDATING)) + YYABORT; + } + | DROP DATABASE if_exists ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_DB; + lex->drop_if_exists=$3; + lex->name= $4; + } + | DROP FUNCTION_SYM if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); + YYABORT; + } + lex->sql_command = SQLCOM_DROP_FUNCTION; + lex->drop_if_exists= $3; + lex->spname= $4; + } + | DROP PROCEDURE if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); + YYABORT; + } + lex->sql_command = SQLCOM_DROP_PROCEDURE; + lex->drop_if_exists= $3; + lex->spname= $4; + } + | DROP USER clear_privileges user_list + { + Lex->sql_command = SQLCOM_DROP_USER; + } + | DROP VIEW_SYM if_exists table_list opt_restrict + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DROP_VIEW; + lex->drop_if_exists= $3; + } + | DROP EVENT_SYM if_exists sp_name + { + Lex->drop_if_exists= $3; + Lex->spname= $4; + Lex->sql_command = SQLCOM_DROP_EVENT; + } + | DROP TRIGGER_SYM if_exists sp_name + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DROP_TRIGGER; + lex->drop_if_exists= $3; + lex->spname= $4; + } + | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE; + } + | DROP LOGFILE_SYM GROUP logfile_group_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; + } + | DROP SERVER_SYM if_exists ident_or_text + { + Lex->sql_command = SQLCOM_DROP_SERVER; + Lex->drop_if_exists= $3; + Lex->server_options.server_name= $4.str; + Lex->server_options.server_name_length= $4.length; + } + ; + +table_list: + table_name + | table_list ',' table_name; + +table_name: + table_ident + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) + YYABORT; + } + ; + +if_exists: + /* empty */ { $$= 0; } + | IF EXISTS { $$= 1; } + ; + +opt_temporary: + /* empty */ { $$= 0; } + | TEMPORARY { $$= 1; } + ; +/* +** Insert : add new data to table +*/ + +insert: + INSERT + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSERT; + lex->duplicates= DUP_ERROR; + mysql_init_select(lex); + /* for subselects */ + lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; + } insert_lock_option + opt_ignore insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec opt_insert_update + {} + ; + +replace: + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + mysql_init_select(lex); + } + replace_lock_option insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec + {} + ; + +insert_lock_option: + /* empty */ + { +#ifdef HAVE_QUERY_CACHE + /* + If it is SP we do not allow insert optimisation whan result of + insert visible only after the table unlocking but everyone can + read table. + */ + $$= (Lex->sphead ? TL_WRITE :TL_WRITE_CONCURRENT_INSERT); +#else + $$= TL_WRITE_CONCURRENT_INSERT; +#endif + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; } + | HIGH_PRIORITY { $$= TL_WRITE; } + ; + +replace_lock_option: + opt_low_priority { $$= $1; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; }; + +insert2: + INTO insert_table {} + | insert_table {}; + +insert_table: + table_name + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + }; + +insert_field_spec: + insert_values {} + | '(' ')' insert_values {} + | '(' fields ')' insert_values {} + | SET + { + LEX *lex=Lex; + if (!(lex->insert_list = new List_item) || + lex->many_values.push_back(lex->insert_list)) + YYABORT; + } + ident_eq_list; + +fields: + fields ',' insert_ident { Lex->field_list.push_back($3); } + | insert_ident { Lex->field_list.push_back($1); }; + +insert_values: + VALUES values_list {} + | VALUE_SYM values_list {} + | create_select { Select->set_braces(0);} union_clause {} + | '(' create_select ')' { Select->set_braces(1);} union_opt {} + ; + +values_list: + values_list ',' no_braces + | no_braces; + +ident_eq_list: + ident_eq_list ',' ident_eq_value + | + ident_eq_value; + +ident_eq_value: + simple_ident_nospvar equal expr_or_default + { + LEX *lex=Lex; + if (lex->field_list.push_back($1) || + lex->insert_list->push_back($3)) + YYABORT; + }; + +equal: EQ {} + | SET_VAR {} + ; + +opt_equal: + /* empty */ {} + | equal {} + ; + +no_braces: + '(' + { + if (!(Lex->insert_list = new List_item)) + YYABORT; + } + opt_values ')' + { + LEX *lex=Lex; + if (lex->many_values.push_back(lex->insert_list)) + YYABORT; + }; + +opt_values: + /* empty */ {} + | values; + +values: + values ',' expr_or_default + { + if (Lex->insert_list->push_back($3)) + YYABORT; + } + | expr_or_default + { + if (Lex->insert_list->push_back($1)) + YYABORT; + } + ; + +expr_or_default: + expr { $$= $1;} + | DEFAULT {$$= new Item_default_value(Lex->current_context()); } + ; + +opt_insert_update: + /* empty */ + | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } + KEY_SYM UPDATE_SYM insert_update_list + ; + +/* Update rows in a table */ + +update: + UPDATE_SYM + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->sql_command= SQLCOM_UPDATE; + lex->lock_option= TL_UNLOCK; /* Will be set later */ + lex->duplicates= DUP_ERROR; + } + opt_low_priority opt_ignore join_table_list + SET update_list + { + LEX *lex= Lex; + if (lex->select_lex.table_list.elements > 1) + lex->sql_command= SQLCOM_UPDATE_MULTI; + else if (lex->select_lex.get_table_list()->derived) + { + /* it is single table update and it is update of derived table */ + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + lex->select_lex.get_table_list()->alias, "UPDATE"); + YYABORT; + } + /* + In case of multi-update setting write lock for all tables may + be too pessimistic. We will decrease lock level if possible in + mysql_multi_update(). + */ + Select->set_lock_for_tables($3); + } + where_clause opt_order_clause delete_limit_clause {} + ; + +update_list: + update_list ',' update_elem + | update_elem; + +update_elem: + simple_ident_nospvar equal expr_or_default + { + if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) + YYABORT; + }; + +insert_update_list: + insert_update_list ',' insert_update_elem + | insert_update_elem; + +insert_update_elem: + simple_ident_nospvar equal expr_or_default + { + LEX *lex= Lex; + if (lex->update_list.push_back($1) || + lex->value_list.push_back($3)) + YYABORT; + }; + +opt_low_priority: + /* empty */ { $$= YYTHD->update_lock_default; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; + +/* Delete rows from a table */ + +delete: + DELETE_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE; + mysql_init_select(lex); + lex->lock_option= lex->thd->update_lock_default; + lex->ignore= 0; + lex->select_lex.init_order(); + } + opt_delete_options single_multi {} + ; + +single_multi: + FROM table_ident + { + if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, + Lex->lock_option)) + YYABORT; + } + where_clause opt_order_clause + delete_limit_clause {} + | table_wild_list + { mysql_init_multi_delete(Lex); } + FROM join_table_list where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } + | FROM table_wild_list + { mysql_init_multi_delete(Lex); } + USING join_table_list where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } + ; + +table_wild_list: + table_wild_one {} + | table_wild_list ',' table_wild_one {}; + +table_wild_one: + ident opt_wild opt_table_alias + { + if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, Lex->lock_option)) + YYABORT; + } + | ident '.' ident opt_wild opt_table_alias + { + if (!Select->add_table_to_list(YYTHD, + new Table_ident(YYTHD, $1, $3, 0), + $5, + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, + Lex->lock_option)) + YYABORT; + } + ; + +opt_wild: + /* empty */ {} + | '.' '*' {}; + + +opt_delete_options: + /* empty */ {} + | opt_delete_option opt_delete_options {}; + +opt_delete_option: + QUICK { Select->options|= OPTION_QUICK; } + | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } + | IGNORE_SYM { Lex->ignore= 1; }; + +truncate: + TRUNCATE_SYM opt_table_sym table_name + { + LEX* lex= Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->select_lex.options= 0; + lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; + lex->select_lex.init_order(); + } + ; + +opt_table_sym: + /* empty */ + | TABLE_SYM; + +/* Show things */ + +show: SHOW + { + LEX *lex=Lex; + lex->wild=0; + lex->lock_option= TL_READ; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + bzero((char*) &lex->create_info,sizeof(lex->create_info)); + } + show_param + {} + ; + +show_param: + DATABASES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_DATABASES; + if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) + YYABORT; + } + | opt_full TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLES; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES)) + YYABORT; + } + | opt_full TRIGGERS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TRIGGERS; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) + YYABORT; + } + | EVENTS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_EVENTS; + lex->select_lex.db= $2; + if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS)) + YYABORT; + } + | TABLE_SYM STATUS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES)) + YYABORT; + } + | OPEN_SYM TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES)) + YYABORT; + } + | opt_full PLUGIN_SYM + { + LEX *lex= Lex; + WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'"); + lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) + YYABORT; + } + | PLUGINS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) + YYABORT; + } + | ENGINE_SYM storage_engines + { Lex->create_info.db_type= $2; } + show_engine_param + | ENGINE_SYM ALL + { Lex->create_info.db_type= NULL; } + show_engine_param + | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_FIELDS; + if ($5) + $4->change_db($5); + if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) + YYABORT; + } + | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ + TEXT_STRING_sys AND_SYM MASTER_LOG_POS_SYM EQ ulonglong_num + AND_SYM MASTER_SERVER_ID_SYM EQ + ulong_num + { + Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; + Lex->mi.log_file_name = $8.str; + Lex->mi.pos = $12; + Lex->mi.server_id = $16; + } + | master_or_binary LOGS_SYM + { + Lex->sql_command = SQLCOM_SHOW_BINLOGS; + } + | SLAVE HOSTS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; + } + | BINLOG_SYM EVENTS_SYM binlog_in binlog_from + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; + } opt_limit_clause_init + | keys_or_index from_or_in table_ident opt_db where_clause + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_KEYS; + if ($4) + $3->change_db($4); + if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) + YYABORT; + } + | COLUMN_SYM TYPES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES; + } + | TABLE_SYM TYPES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + WARN_DEPRECATED(yythd, "5.2", "SHOW TABLE TYPES", "'SHOW [STORAGE] ENGINES'"); + } + | opt_storage ENGINES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) + YYABORT; + } + | AUTHORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_AUTHORS; + } + | CONTRIBUTORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; + } + | PRIVILEGES + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_PRIVILEGES; + } + | COUNT_SYM '(' '*' ')' WARNINGS + { (void) create_select_for_variable("warning_count"); } + | COUNT_SYM '(' '*' ')' ERRORS + { (void) create_select_for_variable("error_count"); } + | WARNINGS opt_limit_clause_init + { Lex->sql_command = SQLCOM_SHOW_WARNS;} + | ERRORS opt_limit_clause_init + { Lex->sql_command = SQLCOM_SHOW_ERRORS;} + | opt_var_type STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS; + lex->option_type= $1; + if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS)) + YYABORT; + } + | INNOBASE_SYM STATUS_SYM + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_ENGINE_STATUS; + if (!(lex->create_info.db_type= + ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); + YYABORT; + } + WARN_DEPRECATED(yythd, "5.2", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'"); + } + | MUTEX_SYM STATUS_SYM + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_ENGINE_MUTEX; + if (!(lex->create_info.db_type= + ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) + { + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); + YYABORT; + } + WARN_DEPRECATED(yythd, "5.2", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'"); + } + | opt_full PROCESSLIST_SYM + { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} + | opt_var_type VARIABLES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_VARIABLES; + lex->option_type= $1; + if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) + YYABORT; + } + | charset wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CHARSETS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS)) + YYABORT; + } + | COLLATION_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_COLLATIONS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_COLLATIONS)) + YYABORT; + } + | GRANTS + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + LEX_USER *curr_user; + if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) + YYABORT; + bzero(curr_user, sizeof(st_lex_user)); + lex->grant_user= curr_user; + } + | GRANTS FOR_SYM user + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user=$3; + lex->grant_user->password=null_lex_str; + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->sql_command=SQLCOM_SHOW_CREATE_DB; + Lex->create_info.options=$3; + Lex->name= $4; + } + | CREATE TABLE_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= 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 + { + Lex->sql_command = SQLCOM_SHOW_MASTER_STAT; + } + | SLAVE STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + } + | CREATE PROCEDURE sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; + } + | CREATE FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; + } + | PROCEDURE STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_PROC; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + YYABORT; + if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) + YYABORT; + } + | FUNCTION_SYM STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_FUNC; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + YYABORT; + if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) + YYABORT; + } + | PROCEDURE CODE_SYM sp_name + { +#ifdef DBUG_OFF + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; +#else + Lex->sql_command= SQLCOM_SHOW_PROC_CODE; + Lex->spname= $3; +#endif + } + | FUNCTION_SYM CODE_SYM sp_name + { +#ifdef DBUG_OFF + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; +#else + Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; + Lex->spname= $3; +#endif + } + | CREATE EVENT_SYM sp_name + { + Lex->spname= $3; + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; + } + ; + +show_engine_param: + STATUS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; } + | MUTEX_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; } + | LOGS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; }; + +master_or_binary: + MASTER_SYM + | BINARY; + +opt_storage: + /* empty */ + | STORAGE_SYM; + +opt_db: + /* empty */ { $$= 0; } + | from_or_in ident { $$= $2.str; }; + +opt_full: + /* empty */ { Lex->verbose=0; } + | FULL { Lex->verbose=1; }; + +from_or_in: + FROM + | IN_SYM; + +binlog_in: + /* empty */ { Lex->mi.log_file_name = 0; } + | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; }; + +binlog_from: + /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } + | FROM ulonglong_num { Lex->mi.pos = $2; }; + +wild_and_where: + /* empty */ + | LIKE TEXT_STRING_sys + { Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length, + system_charset_info); } + | WHERE expr + { + Select->where= $2; + if ($2) + $2->top_level_item(); + } + ; + + +/* A Oracle compatible synonym for show */ +describe: + describe_command table_ident + { + LEX *lex= Lex; + lex->lock_option= TL_READ; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->sql_command= SQLCOM_SHOW_FIELDS; + lex->select_lex.db= 0; + lex->verbose= 0; + if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS)) + YYABORT; + } + opt_describe_column {} + | describe_command opt_extended_describe + { Lex->describe|= DESCRIBE_NORMAL; } + select + { + LEX *lex=Lex; + lex->select_lex.options|= SELECT_DESCRIBE; + } + ; + +describe_command: + DESC + | DESCRIBE; + +opt_extended_describe: + /* empty */ {} + | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } + ; + + +opt_describe_column: + /* empty */ {} + | text_string { Lex->wild= $1; } + | ident + { Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); }; + + +/* flush things */ + +flush: + FLUSH_SYM opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_FLUSH; + lex->type= 0; + lex->no_write_to_binlog= $2; + } + flush_options + {} + ; + +flush_options: + flush_options ',' flush_option + | flush_option; + +flush_option: + table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list {} + | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE_FREE; } + | HOSTS_SYM { Lex->type|= REFRESH_HOSTS; } + | PRIVILEGES { Lex->type|= REFRESH_GRANT; } + | LOGS_SYM { Lex->type|= REFRESH_LOG; } + | STATUS_SYM { Lex->type|= REFRESH_STATUS; } + | SLAVE { Lex->type|= REFRESH_SLAVE; } + | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + | DES_KEY_FILE { Lex->type|= REFRESH_DES_KEY_FILE; } + | RESOURCES { Lex->type|= REFRESH_USER_RESOURCES; }; + +opt_table_list: + /* empty */ {;} + | table_list {;}; + +reset: + RESET_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RESET; lex->type=0; + } reset_options + {} + ; + +reset_options: + reset_options ',' reset_option + | reset_option; + +reset_option: + SLAVE { Lex->type|= REFRESH_SLAVE; } + | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}; + +purge: + PURGE + { + LEX *lex=Lex; + lex->type=0; + } purge_options + {} + ; + +purge_options: + master_or_binary LOGS_SYM purge_option + ; + +purge_option: + TO_SYM TEXT_STRING_sys + { + Lex->sql_command = SQLCOM_PURGE; + Lex->to_log = $2.str; + } + | BEFORE_SYM expr + { + LEX *lex= Lex; + lex->value_list.empty(); + lex->value_list.push_front($2); + lex->sql_command= SQLCOM_PURGE_BEFORE; + } + ; + +/* kill threads */ + +kill: + KILL_SYM kill_option expr + { + LEX *lex=Lex; + lex->value_list.empty(); + lex->value_list.push_front($3); + lex->sql_command= SQLCOM_KILL; + }; + +kill_option: + /* empty */ { Lex->type= 0; } + | CONNECTION_SYM { Lex->type= 0; } + | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } + ; + +/* change database */ + +use: USE_SYM ident + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CHANGE_DB; + lex->select_lex.db= $2.str; + }; + +/* import, export of files */ + +load: LOAD DATA_SYM + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); + YYABORT; + } + lex->fname_start= lex->ptr; + } + load_data + {} + | + LOAD TABLE_SYM table_ident FROM MASTER_SYM + { + LEX *lex=Lex; + WARN_DEPRECATED(yythd, "5.2", "LOAD TABLE FROM MASTER", + "MySQL Administrator (mysqldump, mysql)"); + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE"); + YYABORT; + } + lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; + if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING)) + YYABORT; + }; + +load_data: + load_data_lock opt_local INFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_LOAD; + lex->lock_option= $1; + lex->local_file= $2; + lex->duplicates= DUP_ERROR; + lex->ignore= 0; + if (!(lex->exchange= new sql_exchange($4.str, 0))) + YYABORT; + } + opt_duplicate INTO + { + LEX *lex=Lex; + lex->fname_end= lex->ptr; + } + TABLE_SYM table_ident + { + LEX *lex=Lex; + if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, + lex->lock_option)) + YYABORT; + lex->field_list.empty(); + lex->update_list.empty(); + lex->value_list.empty(); + } + opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec + opt_load_data_set_spec + {} + | + FROM MASTER_SYM + { + Lex->sql_command = SQLCOM_LOAD_MASTER_DATA; + WARN_DEPRECATED(yythd, "5.2", "LOAD DATA FROM MASTER", + "mysqldump or future " + "BACKUP/RESTORE DATABASE facility"); + }; + +opt_local: + /* empty */ { $$=0;} + | LOCAL_SYM { $$=1;}; + +load_data_lock: + /* empty */ { $$= YYTHD->update_lock_default; } + | CONCURRENT + { +#ifdef HAVE_QUERY_CACHE + /* + Ignore this option in SP to avoid problem with query cache + */ + if (Lex->sphead != 0) + $$= YYTHD->update_lock_default; + else +#endif + $$= TL_WRITE_CONCURRENT_INSERT; + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; + + +opt_duplicate: + /* empty */ { Lex->duplicates=DUP_ERROR; } + | REPLACE { Lex->duplicates=DUP_REPLACE; } + | IGNORE_SYM { Lex->ignore= 1; }; + +opt_field_term: + /* empty */ + | COLUMNS field_term_list; + +field_term_list: + field_term_list field_term + | field_term; + +field_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->field_term= $3; + } + | OPTIONALLY ENCLOSED BY text_string + { + LEX *lex= Lex; + DBUG_ASSERT(lex->exchange != 0); + lex->exchange->enclosed= $4; + lex->exchange->opt_enclosed= 1; + } + | ENCLOSED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->enclosed= $3; + } + | ESCAPED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->escaped= $3; + }; + +opt_line_term: + /* empty */ + | LINES line_term_list; + +line_term_list: + line_term_list line_term + | line_term; + +line_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_term= $3; + } + | STARTING BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_start= $3; + }; + +opt_ignore_lines: + /* empty */ + | IGNORE_SYM NUM LINES + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->skip_lines= atol($2.str); + }; + +opt_field_or_var_spec: + /* empty */ { } + | '(' fields_or_vars ')' { } + | '(' ')' { }; + +fields_or_vars: + fields_or_vars ',' field_or_var + { Lex->field_list.push_back($3); } + | field_or_var + { Lex->field_list.push_back($1); } + ; + +field_or_var: + simple_ident_nospvar {$$= $1;} + | '@' ident_or_text + { $$= new Item_user_var_as_out_param($2); } + ; + +opt_load_data_set_spec: + /* empty */ { } + | SET insert_update_list { }; + + +/* Common definitions */ + +text_literal: + TEXT_STRING_literal + { + THD *thd= YYTHD; + $$ = new Item_string($1.str,$1.length,thd->variables.collation_connection); + } + | NCHAR_STRING + { $$= new Item_string($1.str,$1.length,national_charset_info); } + | UNDERSCORE_CHARSET TEXT_STRING + { $$ = new Item_string($2.str,$2.length,Lex->underscore_charset); } + | text_literal TEXT_STRING_literal + { ((Item_string*) $1)->append($2.str,$2.length); } + ; + +text_string: + TEXT_STRING_literal + { $$= new (YYTHD->mem_root) String($1.str,$1.length,YYTHD->variables.collation_connection); } + | HEX_NUM + { + Item *tmp= new Item_hex_string($1.str, $1.length); + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + $$= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + } + | BIN_NUM + { + Item *tmp= new Item_bin_string($1.str, $1.length); + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + $$= tmp ? tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + } + ; + +param_marker: + PARAM_MARKER + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + Item_param *item; + if (! lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + YYABORT; + } + item= new Item_param((uint) (lex->tok_start - (uchar *) thd->query)); + if (!($$= item) || lex->param_list.push_back(item)) + { + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + YYABORT; + } + } + ; + +signed_literal: + literal { $$ = $1; } + | '+' NUM_literal { $$ = $2; } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(); + } + ; + + +literal: + text_literal { $$ = $1; } + | NUM_literal { $$ = $1; } + | NULL_SYM { $$ = new Item_null(); + Lex->next_state=MY_LEX_OPERATOR_OR_IDENT;} + | FALSE_SYM { $$= new Item_int((char*) "FALSE",0,1); } + | TRUE_SYM { $$= new Item_int((char*) "TRUE",1,1); } + | HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);} + | BIN_NUM { $$= new Item_bin_string($1.str, $1.length); } + | UNDERSCORE_CHARSET HEX_NUM + { + Item *tmp= new Item_hex_string($2.str, $2.length); + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + $$= new Item_string(str ? str->ptr() : "", + str ? str->length() : 0, + Lex->underscore_charset); + } + | UNDERSCORE_CHARSET BIN_NUM + { + Item *tmp= new Item_bin_string($2.str, $2.length); + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + $$= new Item_string(str ? str->ptr() : "", + str ? str->length() : 0, + Lex->charset); + } + | DATE_SYM text_literal { $$ = $2; } + | TIME_SYM text_literal { $$ = $2; } + | TIMESTAMP text_literal { $$ = $2; }; + +NUM_literal: + NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } + | LONG_NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } + | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } + | DECIMAL_NUM + { + $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); + if (YYTHD->net.report_error) + { + YYABORT; + } + } + | FLOAT_NUM + { + $$ = new Item_float($1.str, $1.length); + if (YYTHD->net.report_error) + { + YYABORT; + } + } + ; + +/********************************************************************** +** Creating different items. +**********************************************************************/ + +insert_ident: + simple_ident_nospvar { $$=$1; } + | table_wild { $$=$1; }; + +table_wild: + ident '.' '*' + { + SELECT_LEX *sel= Select; + $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*"); + sel->with_wild++; + } + | ident '.' ident '.' '*' + { + SELECT_LEX *sel= Select; + $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str,"*"); + sel->with_wild++; + } + ; + +order_ident: + expr { $$=$1; }; + +simple_ident: + ident + { + sp_variable_t *spv; + LEX *lex = Lex; + sp_pcontext *spc = lex->spcont; + if (spc && (spv = spc->find_variable(&$1))) + { + /* We're compiling a stored procedure and found a variable */ + if (! lex->parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + YYABORT; + } + + Item_splocal *splocal; + splocal= new Item_splocal($1, spv->offset, spv->type, + lex->tok_start_prev - + lex->sphead->m_tmp_query); +#ifndef DBUG_OFF + if (splocal) + splocal->m_sp= lex->sphead; +#endif + $$ = (Item*) splocal; + lex->safe_to_cache_query=0; + } + else + { + SELECT_LEX *sel=Select; + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); + } + } + | simple_ident_q { $$= $1; } + ; + +simple_ident_nospvar: + ident + { + SELECT_LEX *sel=Select; + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str); + } + | simple_ident_q { $$= $1; } + ; + +simple_ident_q: + ident '.' ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + + /* + FIXME This will work ok in simple_ident_nospvar case because + we can't meet simple_ident_nospvar in trigger now. But it + should be changed in future. + */ + if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, $1.str, "NEW") || + !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + { + Item_trigger_field *trg_fld; + bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); + + if (lex->trg_chistics.event == TRG_EVENT_INSERT && + !new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); + YYABORT; + } + + if (lex->trg_chistics.event == TRG_EVENT_DELETE && + new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); + YYABORT; + } + + DBUG_ASSERT(!new_row || + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + const bool read_only= + !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); + if (!(trg_fld= new Item_trigger_field(Lex->current_context(), + new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + $3.str, + SELECT_ACL, + read_only))) + YYABORT; + + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + lex->trg_table_fields.link_in_list((byte *)trg_fld, + (byte**)&trg_fld->next_trg_field); + + $$= (Item *)trg_fld; + } + else + { + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $1.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str); + } + } + | '.' ident '.' ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $2.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) : + (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str); + } + | ident '.' ident '.' ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $3.str, thd->where); + } + $$= (sel->parsing_place != IN_HAVING || + sel->get_in_sum_expr() > 0) ? + (Item*) new Item_field(Lex->current_context(), + (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str, $5.str) : + (Item*) new Item_ref(Lex->current_context(), + (YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), + $3.str, $5.str); + }; + + +field_ident: + ident { $$=$1;} + | ident '.' ident '.' ident + { + TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); + YYABORT; + } + if (my_strcasecmp(table_alias_charset, $3.str, + table->table_name)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), $3.str); + YYABORT; + } + $$=$5; + } + | ident '.' ident + { + TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), $1.str); + YYABORT; + } + $$=$3; + } + | '.' ident { $$=$2;} /* For Delphi */; + +table_ident: + ident { $$=new Table_ident($1); } + | ident '.' ident { $$=new Table_ident(YYTHD, $1,$3,0);} + | '.' ident { $$=new Table_ident($2);} /* For Delphi */ + ; + +table_ident_nodb: + ident { LEX_STRING db={(char*) any_db,3}; $$=new Table_ident(YYTHD, db,$1,0); } + ; + +IDENT_sys: + IDENT { $$= $1; } + | IDENT_QUOTED + { + THD *thd= YYTHD; + if (thd->charset_is_system_charset) + { + CHARSET_INFO *cs= system_charset_info; + int dummy_error; + uint wlen= cs->cset->well_formed_len(cs, $1.str, + $1.str+$1.length, + $1.length, &dummy_error); + if (wlen < $1.length) + { + my_error(ER_INVALID_CHARACTER_STRING, MYF(0), + cs->csname, $1.str + wlen); + YYABORT; + } + $$= $1; + } + else + thd->convert_string(&$$, system_charset_info, + $1.str, $1.length, thd->charset()); + } + ; + +TEXT_STRING_sys: + TEXT_STRING + { + THD *thd= YYTHD; + if (thd->charset_is_system_charset) + $$= $1; + else + thd->convert_string(&$$, system_charset_info, + $1.str, $1.length, thd->charset()); + } + ; + +TEXT_STRING_literal: + TEXT_STRING + { + THD *thd= YYTHD; + if (thd->charset_is_collation_connection) + $$= $1; + else + thd->convert_string(&$$, thd->variables.collation_connection, + $1.str, $1.length, thd->charset()); + } + ; + + +TEXT_STRING_filesystem: + TEXT_STRING + { + THD *thd= YYTHD; + if (thd->charset_is_character_set_filesystem) + $$= $1; + else + thd->convert_string(&$$, thd->variables.character_set_filesystem, + $1.str, $1.length, thd->charset()); + } + ; + +ident: + IDENT_sys { $$=$1; } + | READ_ONLY_SYM + { + THD *thd= YYTHD; + $$.str= thd->strmake("read_only",9); + $$.length= 9; + } + | keyword + { + THD *thd= YYTHD; + $$.str= thd->strmake($1.str, $1.length); + $$.length= $1.length; + } + ; + +label_ident: + IDENT_sys { $$=$1; } + | keyword_sp + { + THD *thd= YYTHD; + $$.str= thd->strmake($1.str, $1.length); + $$.length= $1.length; + } + ; + +ident_or_text: + ident { $$=$1;} + | TEXT_STRING_sys { $$=$1;} + | LEX_HOSTNAME { $$=$1;}; + +user: + ident_or_text + { + THD *thd= YYTHD; + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + YYABORT; + $$->user = $1; + $$->host.str= (char *) "%"; + $$->host.length= 1; + + if (check_string_length(&$$->user, + ER(ER_USERNAME), USERNAME_LENGTH)) + YYABORT; + } + | ident_or_text '@' ident_or_text + { + THD *thd= YYTHD; + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + YYABORT; + $$->user = $1; $$->host=$3; + + if (check_string_length(&$$->user, + ER(ER_USERNAME), USERNAME_LENGTH) || + check_string_length(&$$->host, + ER(ER_HOSTNAME), HOSTNAME_LENGTH)) + YYABORT; + } + | CURRENT_USER optional_braces + { + if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) + YYABORT; + /* + empty LEX_USER means current_user and + will be handled in the get_current_user() function + later + */ + bzero($$, sizeof(LEX_USER)); + }; + +/* Keyword that we allow for identifiers (except SP labels) */ +keyword: + keyword_sp {} + | ASCII_SYM {} + | BACKUP_SYM {} + | BEGIN_SYM {} + | BYTE_SYM {} + | CACHE_SYM {} + | CHARSET {} + | CHECKSUM_SYM {} + | CLOSE_SYM {} + | COMMENT_SYM {} + | COMMIT_SYM {} + | CONTAINS_SYM {} + | DEALLOCATE_SYM {} + | DO_SYM {} + | END {} + | EXECUTE_SYM {} + | FLUSH_SYM {} + | HANDLER_SYM {} + | HELP_SYM {} + | HOST_SYM {} + | INSTALL_SYM {} + | LANGUAGE_SYM {} + | NO_SYM {} + | OPEN_SYM {} + | OWNER_SYM {} + | PARSER_SYM {} + | PARTITION_SYM {} + | PORT_SYM {} + | PREPARE_SYM {} + | REMOVE_SYM {} + | REPAIR {} + | RESET_SYM {} + | RESTORE_SYM {} + | ROLLBACK_SYM {} + | SAVEPOINT_SYM {} + | SECURITY_SYM {} + | SERVER_SYM {} + | SIGNED_SYM {} + | SOCKET_SYM {} + | SLAVE {} + | SONAME_SYM {} + | START_SYM {} + | STOP_SYM {} + | TRUNCATE_SYM {} + | UNICODE_SYM {} + | UNINSTALL_SYM {} + | USER {} + | WRAPPER_SYM {} + | XA_SYM {} + | UPGRADE_SYM {} + ; + +/* + * Keywords that we allow for labels in SPs. + * Anything that's the beginning of a statement or characteristics + * must be in keyword above, otherwise we get (harmful) shift/reduce + * conflicts. + */ +keyword_sp: + ACTION {} + | ADDDATE_SYM {} + | AFTER_SYM {} + | AGAINST {} + | AGGREGATE_SYM {} + | ALGORITHM_SYM {} + | ANY_SYM {} + | AT_SYM {} + | AUTHORS_SYM {} + | AUTO_INC {} + | AUTOEXTEND_SIZE_SYM {} + | AVG_ROW_LENGTH {} + | AVG_SYM {} + | BINLOG_SYM {} + | BIT_SYM {} + | BOOL_SYM {} + | BOOLEAN_SYM {} + | BTREE_SYM {} + | CASCADED {} + | CHAIN_SYM {} + | CHANGED {} + | CIPHER_SYM {} + | CLIENT_SYM {} + | COALESCE {} + | CODE_SYM {} + | COLLATION_SYM {} + | COLUMNS {} + | COMMITTED_SYM {} + | COMPACT_SYM {} + | COMPLETION_SYM {} + | COMPRESSED_SYM {} + | CONCURRENT {} + | CONSISTENT_SYM {} + | CONTRIBUTORS_SYM {} + | CUBE_SYM {} + | DATA_SYM {} + | DATAFILE_SYM {} + | DATETIME {} + | DATE_SYM {} + | DAY_SYM {} + | DEFINER_SYM {} + | DELAY_KEY_WRITE_SYM {} + | DES_KEY_FILE {} + | DIRECTORY_SYM {} + | DISABLE_SYM {} + | DISCARD {} + | DISK_SYM {} + | DUMPFILE {} + | DUPLICATE_SYM {} + | DYNAMIC_SYM {} + | ENDS_SYM {} + | ENUM {} + | ENGINE_SYM {} + | ENGINES_SYM {} + | ERRORS {} + | ESCAPE_SYM {} + | EVENT_SYM {} + | EVENTS_SYM {} + | EVERY_SYM {} + | EXPANSION_SYM {} + | EXTENDED_SYM {} + | EXTENT_SIZE_SYM {} + | FAST_SYM {} + | FOUND_SYM {} + | ENABLE_SYM {} + | FULL {} + | FILE_SYM {} + | FIRST_SYM {} + | FIXED_SYM {} + | FRAC_SECOND_SYM {} + | GEOMETRY_SYM {} + | GEOMETRYCOLLECTION {} + | GET_FORMAT {} + | GRANTS {} + | GLOBAL_SYM {} + | HASH_SYM {} + | HOSTS_SYM {} + | HOUR_SYM {} + | IDENTIFIED_SYM {} + | INVOKER_SYM {} + | IMPORT {} + | INDEXES {} + | INITIAL_SIZE_SYM {} + | ISOLATION {} + | ISSUER_SYM {} + | INNOBASE_SYM {} + | INSERT_METHOD {} + | KEY_BLOCK_SIZE {} + | LAST_SYM {} + | LEAVES {} + | LESS_SYM {} + | LEVEL_SYM {} + | LINESTRING {} + | LIST_SYM {} + | LOCAL_SYM {} + | LOCKS_SYM {} + | LOGFILE_SYM {} + | LOGS_SYM {} + | MAX_ROWS {} + | MASTER_SYM {} + | MASTER_HOST_SYM {} + | MASTER_PORT_SYM {} + | MASTER_LOG_FILE_SYM {} + | MASTER_LOG_POS_SYM {} + | MASTER_USER_SYM {} + | MASTER_PASSWORD_SYM {} + | MASTER_SERVER_ID_SYM {} + | MASTER_CONNECT_RETRY_SYM {} + | MASTER_SSL_SYM {} + | MASTER_SSL_CA_SYM {} + | MASTER_SSL_CAPATH_SYM {} + | MASTER_SSL_CERT_SYM {} + | MASTER_SSL_CIPHER_SYM {} + | MASTER_SSL_KEY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_SIZE_SYM {} + | MAX_UPDATES_PER_HOUR {} + | MAX_USER_CONNECTIONS_SYM {} + | MAX_VALUE_SYM {} + | MEDIUM_SYM {} + | MEMORY_SYM {} + | MERGE_SYM {} + | MICROSECOND_SYM {} + | MIGRATE_SYM {} + | MINUTE_SYM {} + | MIN_ROWS {} + | MODIFY_SYM {} + | MODE_SYM {} + | MONTH_SYM {} + | MULTILINESTRING {} + | MULTIPOINT {} + | MULTIPOLYGON {} + | MUTEX_SYM {} + | NAME_SYM {} + | NAMES_SYM {} + | NATIONAL_SYM {} + | NCHAR_SYM {} + | NDBCLUSTER_SYM {} + | NEXT_SYM {} + | NEW_SYM {} + | NO_WAIT_SYM {} + | NODEGROUP_SYM {} + | NONE_SYM {} + | NVARCHAR_SYM {} + | OFFSET_SYM {} + | OLD_PASSWORD {} + | ONE_SHOT_SYM {} + | ONE_SYM {} + | PACK_KEYS_SYM {} + | PARTIAL {} + | PARTITIONING_SYM {} + | PARTITIONS_SYM {} + | PASSWORD {} + | PHASE_SYM {} + | PLUGIN_SYM {} + | PLUGINS_SYM {} + | POINT_SYM {} + | POLYGON {} + | PRESERVE_SYM {} + | PREV_SYM {} + | PRIVILEGES {} + | PROCESS {} + | PROCESSLIST_SYM {} + | QUARTER_SYM {} + | QUERY_SYM {} + | QUICK {} + | REBUILD_SYM {} + | RECOVER_SYM {} + | REDO_BUFFER_SIZE_SYM {} + | REDOFILE_SYM {} + | REDUNDANT_SYM {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} + | RELAY_THREAD {} + | RELOAD {} + | REORGANIZE_SYM {} + | REPEATABLE_SYM {} + | REPLICATION {} + | RESOURCES {} + | RESUME_SYM {} + | RETURNS_SYM {} + | ROLLUP_SYM {} + | ROUTINE_SYM {} + | ROWS_SYM {} + | ROW_FORMAT_SYM {} + | ROW_SYM {} + | RTREE_SYM {} + | SCHEDULE_SYM {} + | SECOND_SYM {} + | SERIAL_SYM {} + | SERIALIZABLE_SYM {} + | SESSION_SYM {} + | SIMPLE_SYM {} + | SHARE_SYM {} + | SHUTDOWN {} + | SNAPSHOT_SYM {} + | SOUNDS_SYM {} + | SQL_CACHE_SYM {} + | SQL_BUFFER_RESULT {} + | SQL_NO_CACHE_SYM {} + | SQL_THREAD {} + | STARTS_SYM {} + | STATUS_SYM {} + | STORAGE_SYM {} + | STRING_SYM {} + | SUBDATE_SYM {} + | SUBJECT_SYM {} + | SUBPARTITION_SYM {} + | SUBPARTITIONS_SYM {} + | SUPER_SYM {} + | SUSPEND_SYM {} + | TABLES {} + | TABLESPACE {} + | TEMPORARY {} + | TEMPTABLE_SYM {} + | TEXT_SYM {} + | THAN_SYM {} + | TRANSACTION_SYM {} + | TRIGGERS_SYM {} + | TIMESTAMP {} + | TIMESTAMP_ADD {} + | TIMESTAMP_DIFF {} + | TIME_SYM {} + | TYPES_SYM {} + | TYPE_SYM {} + | UDF_RETURNS_SYM {} + | FUNCTION_SYM {} + | UNCOMMITTED_SYM {} + | UNDEFINED_SYM {} + | UNDO_BUFFER_SIZE_SYM {} + | UNDOFILE_SYM {} + | UNKNOWN_SYM {} + | UNTIL_SYM {} + | USER {} + | USE_FRM {} + | VARIABLES {} + | VIEW_SYM {} + | VALUE_SYM {} + | WARNINGS {} + | WAIT_SYM {} + | WEEK_SYM {} + | WORK_SYM {} + | X509_SYM {} + | YEAR_SYM {} + ; + +/* Option functions */ + +set: + SET opt_option + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->option_type=OPT_SESSION; + lex->var_list.empty(); + lex->one_shot_set= 0; + } + option_value_list + {} + ; + +opt_option: + /* empty */ {} + | OPTION {}; + +option_value_list: + option_type_value + | option_value_list ',' option_type_value; + +option_type_value: + { + if (Lex->sphead) + { + /* + If we are in SP we want have own LEX for each assignment. + This is mostly because it is hard for several sp_instr_set + and sp_instr_set_trigger instructions share one LEX. + (Well, it is theoretically possible but adds some extra + overhead on preparation for execution stage and IMO less + robust). + + QQ: May be we should simply prohibit group assignments in SP? + */ + LEX *lex; + Lex->sphead->reset_lex(YYTHD); + lex= Lex; + + /* Set new LEX as if we at start of set rule. */ + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->option_type=OPT_SESSION; + lex->var_list.empty(); + lex->one_shot_set= 0; + lex->sphead->m_tmp_query= lex->tok_start; + } + } + ext_option_value + { + LEX *lex= Lex; + + if (lex->sphead) + { + sp_head *sp= lex->sphead; + + if (!lex->var_list.is_empty()) + { + /* + We have assignment to user or system variable or + option setting, so we should construct sp_instr_stmt + for it. + */ + LEX_STRING qbuff; + sp_instr_stmt *i; + + if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, + lex))) + YYABORT; + + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) + qbuff.length= lex->ptr - sp->m_tmp_query; + else + qbuff.length= lex->tok_end - sp->m_tmp_query; + + if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5))) + YYABORT; + + strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query, + qbuff.length); + qbuff.length+= 4; + i->m_query= qbuff; + sp->add_instr(i); + } + lex->sphead->restore_lex(YYTHD); + } + }; + +option_type: + option_type2 {} + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +option_type2: + /* empty */ { $$= OPT_DEFAULT; } + | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; } + ; + +opt_var_type: + /* empty */ { $$=OPT_SESSION; } + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +opt_var_ident_type: + /* empty */ { $$=OPT_DEFAULT; } + | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } + | LOCAL_SYM '.' { $$=OPT_SESSION; } + | SESSION_SYM '.' { $$=OPT_SESSION; } + ; + +ext_option_value: + sys_option_value + | option_type2 option_value; + +sys_option_value: + option_type internal_variable_name equal set_expr_or_default + { + LEX *lex=Lex; + + if ($2.var == trg_new_row_fake_var) + { + /* We are in trigger and assigning value to field of new row */ + Item *it; + Item_trigger_field *trg_fld; + sp_instr_set_trigger_field *sp_fld; + LINT_INIT(sp_fld); + if ($1) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if ($4) + it= $4; + else + { + /* QQ: Shouldn't this be field's default value ? */ + it= new Item_null(); + } + + DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + if (!(trg_fld= new Item_trigger_field(Lex->current_context(), + Item_trigger_field::NEW_ROW, + $2.base_name.str, + UPDATE_ACL, FALSE)) || + !(sp_fld= new sp_instr_set_trigger_field(lex->sphead-> + instructions(), + lex->spcont, + trg_fld, + it, lex))) + YYABORT; + + /* + Let us add this item to list of all Item_trigger_field + objects in trigger. + */ + lex->trg_table_fields.link_in_list((byte *)trg_fld, + (byte **)&trg_fld->next_trg_field); + + lex->sphead->add_instr(sp_fld); + } + else if ($2.var) + { /* System variable */ + if ($1) + lex->option_type= $1; + lex->var_list.push_back(new set_var(lex->option_type, $2.var, + &$2.base_name, $4)); + } + else + { + /* An SP local variable */ + sp_pcontext *ctx= lex->spcont; + sp_variable_t *spv; + sp_instr_set *sp_set; + Item *it; + if ($1) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + + spv= ctx->find_variable(&$2.base_name); + + if ($4) + it= $4; + else if (spv->dflt) + it= spv->dflt; + else + it= new Item_null(); + sp_set= new sp_instr_set(lex->sphead->instructions(), ctx, + spv->offset, it, spv->type, lex, TRUE); + lex->sphead->add_instr(sp_set); + } + } + | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types + { + LEX *lex=Lex; + lex->option_type= $1; + lex->var_list.push_back(new set_var(lex->option_type, + find_sys_var("tx_isolation"), + &null_lex_str, + new Item_int((int32) $5))); + } + ; + +option_value: + '@' ident_or_text equal expr + { + Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); + } + | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default + { + LEX *lex=Lex; + lex->var_list.push_back(new set_var($3, $4.var, &$4.base_name, $6)); + } + | charset old_or_new_charset_name_or_default + { + THD *thd= YYTHD; + LEX *lex= Lex; + $2= $2 ? $2: global_system_variables.character_set_client; + lex->var_list.push_back(new set_var_collation_client($2,thd->variables.collation_database,$2)); + } + | NAMES_SYM equal expr + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING names; + + names.str= (char *)"names"; + names.length= 5; + if (spc && spc->find_variable(&names)) + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); + else + yyerror(ER(ER_SYNTAX_ERROR)); + + YYABORT; + } + | NAMES_SYM charset_name_or_default opt_collate + { + LEX *lex= Lex; + $2= $2 ? $2 : global_system_variables.character_set_client; + $3= $3 ? $3 : $2; + if (!my_charset_same($2,$3)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $3->name, $2->csname); + YYABORT; + } + lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); + } + | PASSWORD equal text_or_password + { + THD *thd=YYTHD; + LEX_USER *user; + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING pw; + + pw.str= (char *)"password"; + pw.length= 8; + if (spc && spc->find_variable(&pw)) + { + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); + YYABORT; + } + if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + YYABORT; + user->host=null_lex_str; + user->user.str=thd->security_ctx->priv_user; + thd->lex->var_list.push_back(new set_var_password(user, $3)); + } + | PASSWORD FOR_SYM user equal text_or_password + { + Lex->var_list.push_back(new set_var_password($3,$5)); + } + ; + +internal_variable_name: + ident + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + sp_variable_t *spv; + + /* We have to lookup here since local vars can shadow sysvars */ + if (!spc || !(spv = spc->find_variable(&$1))) + { + /* Not an SP local variable */ + sys_var *tmp=find_sys_var($1.str, $1.length); + if (!tmp) + YYABORT; + $$.var= tmp; + $$.base_name= null_lex_str; + /* + If this is time_zone variable we should open time zone + describing tables + */ + if (tmp == &sys_time_zone && + lex->add_time_zone_tables_to_query_tables(YYTHD)) + YYABORT; + else if (spc && tmp == &sys_autocommit) + { + /* + We don't allow setting AUTOCOMMIT from a stored function + or trigger. + */ + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + } + else + { + /* An SP local variable */ + $$.var= NULL; + $$.base_name= $1; + } + } + | ident '.' ident + { + LEX *lex= Lex; + if (check_reserved_words(&$1)) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, $1.str, "NEW") || + !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + { + if ($1.str[0]=='O' || $1.str[0]=='o') + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""); + YYABORT; + } + if (lex->trg_chistics.event == TRG_EVENT_DELETE) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), + "NEW", "on DELETE"); + YYABORT; + } + if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "); + YYABORT; + } + /* This special combination will denote field of NEW row */ + $$.var= trg_new_row_fake_var; + $$.base_name= $3; + } + else + { + sys_var *tmp=find_sys_var($3.str, $3.length); + if (!tmp) + YYABORT; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); + $$.var= tmp; + $$.base_name= $1; + } + } + | DEFAULT '.' ident + { + sys_var *tmp=find_sys_var($3.str, $3.length); + if (!tmp) + YYABORT; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); + $$.var= tmp; + $$.base_name.str= (char*) "default"; + $$.base_name.length= 7; + } + ; + +isolation_types: + READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } + | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } + | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + ; + +text_or_password: + TEXT_STRING { $$=$1.str;} + | PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? YYTHD->variables.old_passwords ? + Item_func_old_password::alloc(YYTHD, $3.str) : + Item_func_password::alloc(YYTHD, $3.str) : + $3.str; + } + | OLD_PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : + $3.str; + } + ; + + +set_expr_or_default: + expr { $$=$1; } + | DEFAULT { $$=0; } + | ON { $$=new Item_string("ON", 2, system_charset_info); } + | ALL { $$=new Item_string("ALL", 3, system_charset_info); } + | BINARY { $$=new Item_string("binary", 6, system_charset_info); } + ; + + +/* Lock function */ + +lock: + LOCK_SYM table_or_tables + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK"); + YYABORT; + } + lex->sql_command= SQLCOM_LOCK_TABLES; + } + table_lock_list + {} + ; + +table_or_tables: + TABLE_SYM + | TABLES; + +table_lock_list: + table_lock + | table_lock_list ',' table_lock; + +table_lock: + table_ident opt_table_alias lock_option + { + if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) + YYABORT; + } + ; + +lock_option: + READ_SYM { $$=TL_READ_NO_INSERT; } + | WRITE_SYM { $$=YYTHD->update_lock_default; } + | LOW_PRIORITY WRITE_SYM { $$=TL_WRITE_LOW_PRIORITY; } + | READ_SYM LOCAL_SYM { $$= TL_READ; } + ; + +unlock: + UNLOCK_SYM + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"); + YYABORT; + } + lex->sql_command= SQLCOM_UNLOCK_TABLES; + } + table_or_tables + {} + ; + + +/* +** Handler: direct access to ISAM functions +*/ + +handler: + HANDLER_SYM table_ident OPEN_SYM opt_table_alias + { + LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } + lex->sql_command = SQLCOM_HA_OPEN; + if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) + YYABORT; + } + | HANDLER_SYM table_ident_nodb CLOSE_SYM + { + LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } + lex->sql_command = SQLCOM_HA_CLOSE; + if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) + YYABORT; + } + | HANDLER_SYM table_ident_nodb READ_SYM + { + LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } + lex->expr_allows_subselect= FALSE; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + lex->current_select->select_limit= new Item_int((int32) 1); + lex->current_select->offset_limit= 0; + if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) + YYABORT; + } + handler_read_or_scan where_clause opt_limit_clause + { + Lex->expr_allows_subselect= TRUE; + } + ; + +handler_read_or_scan: + handler_scan_function { Lex->ident= null_lex_str; } + | ident handler_rkey_function { Lex->ident= $1; } + ; + +handler_scan_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + ; + +handler_rkey_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + | PREV_SYM { Lex->ha_read_mode = RPREV; } + | LAST_SYM { Lex->ha_read_mode = RLAST; } + | handler_rkey_mode + { + LEX *lex=Lex; + lex->ha_read_mode = RKEY; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list = new List_item)) + YYABORT; + } '(' values ')' { } + ; + +handler_rkey_mode: + EQ { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | GT_SYM { $$=HA_READ_AFTER_KEY; } + | LT { $$=HA_READ_BEFORE_KEY; } + ; + +/* GRANT / REVOKE */ + +revoke: + REVOKE clear_privileges revoke_command + {} + ; + +revoke_command: + grant_privileges ON opt_table grant_ident FROM grant_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_REVOKE; + lex->type= 0; + } + | + grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list + { + LEX *lex= Lex; + if (lex->columns.elements) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_FUNCTION; + + } + | + grant_privileges ON PROCEDURE grant_ident FROM grant_list + { + LEX *lex= Lex; + if (lex->columns.elements) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_PROCEDURE; + } + | + ALL opt_privileges ',' GRANT OPTION FROM grant_list + { + Lex->sql_command = SQLCOM_REVOKE_ALL; + } + ; + +grant: + GRANT clear_privileges grant_command + {} + ; + +grant_command: + grant_privileges ON opt_table grant_ident TO_SYM grant_list + require_clause grant_options + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT; + lex->type= 0; + } + | + grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list + require_clause grant_options + { + LEX *lex= Lex; + if (lex->columns.elements) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_FUNCTION; + } + | + grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list + require_clause grant_options + { + LEX *lex= Lex; + if (lex->columns.elements) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_PROCEDURE; + } + ; + +opt_table: + /* Empty */ + | TABLE_SYM ; + +grant_privileges: + object_privilege_list { } + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } + ; + +opt_privileges: + /* empty */ + | PRIVILEGES + ; + +object_privilege_list: + object_privilege + | object_privilege_list ',' object_privilege; + +object_privilege: + SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} + | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} + | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} + | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} + | DELETE_SYM { Lex->grant |= DELETE_ACL;} + | USAGE {} + | INDEX_SYM { Lex->grant |= INDEX_ACL;} + | ALTER { Lex->grant |= ALTER_ACL;} + | CREATE { Lex->grant |= CREATE_ACL;} + | DROP { Lex->grant |= DROP_ACL;} + | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} + | RELOAD { Lex->grant |= RELOAD_ACL;} + | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} + | PROCESS { Lex->grant |= PROCESS_ACL;} + | FILE_SYM { Lex->grant |= FILE_ACL;} + | GRANT OPTION { Lex->grant |= GRANT_ACL;} + | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} + | 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; } + | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } + | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } + | CREATE USER { Lex->grant |= CREATE_USER_ACL; } + | EVENT_SYM { Lex->grant |= EVENT_ACL;} + | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } + ; + + +opt_and: + /* empty */ {} + | AND_SYM {} + ; + +require_list: + require_list_element opt_and require_list + | require_list_element + ; + +require_list_element: + SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT"); + YYABORT; + } + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER"); + YYABORT; + } + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + { + my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER"); + YYABORT; + } + lex->ssl_cipher=$2.str; + } + ; + +grant_ident: + '*' + { + LEX *lex= Lex; + THD *thd= lex->thd; + uint dummy; + if (thd->copy_db_to(&lex->current_select->db, &dummy)) + YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + { + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, + ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); + YYABORT; + } + } + | ident '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = $1.str; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + { + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, + ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); + YYABORT; + } + } + | '*' '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = NULL; + if (lex->grant == GLOBAL_ACLS) + lex->grant= GLOBAL_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + { + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, + ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); + YYABORT; + } + } + | table_ident + { + LEX *lex=Lex; + if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL,0)) + YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = TABLE_ACLS & ~GRANT_ACL; + } + ; + + +user_list: + user { if (Lex->users_list.push_back($1)) YYABORT;} + | user_list ',' user + { + if (Lex->users_list.push_back($3)) + YYABORT; + } + ; + + +grant_list: + grant_user { if (Lex->users_list.push_back($1)) YYABORT;} + | grant_list ',' grant_user + { + if (Lex->users_list.push_back($3)) + YYABORT; + } + ; + + +grant_user: + user IDENTIFIED_SYM BY TEXT_STRING + { + $$=$1; $1->password=$4; + if ($4.length) + { + if (YYTHD->variables.old_passwords) + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); + if (buff) + make_scrambled_password_323(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + else + { + char *buff= + (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); + if (buff) + make_scrambled_password(buff, $4.str); + $1->password.str= buff; + $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + } + } + } + | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING + { $$= $1; $1->password= $5; } + | user + { $$= $1; $1->password= null_lex_str; } + ; + + +opt_column_list: + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')'; + +column_list: + column_list ',' column_list_id + | column_list_id; + +column_list_id: + ident + { + String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info); + List_iterator <LEX_COLUMN> iter(Lex->columns); + class LEX_COLUMN *point; + LEX *lex=Lex; + while ((point=iter++)) + { + if (!my_strcasecmp(system_charset_info, + point->column.ptr(), new_str->ptr())) + break; + } + lex->grant_tot_col|= lex->which_columns; + if (point) + point->rights |= lex->which_columns; + else + lex->columns.push_back(new LEX_COLUMN (*new_str,lex->which_columns)); + } + ; + + +require_clause: /* empty */ + | REQUIRE_SYM require_list + { + Lex->ssl_type=SSL_TYPE_SPECIFIED; + } + | REQUIRE_SYM SSL_SYM + { + Lex->ssl_type=SSL_TYPE_ANY; + } + | REQUIRE_SYM X509_SYM + { + Lex->ssl_type=SSL_TYPE_X509; + } + | REQUIRE_SYM NONE_SYM + { + Lex->ssl_type=SSL_TYPE_NONE; + } + ; + +grant_options: + /* empty */ {} + | WITH grant_option_list; + +grant_option_list: + grant_option_list grant_option {} + | grant_option {} + ; + +grant_option: + GRANT OPTION { Lex->grant |= GRANT_ACL;} + | MAX_QUERIES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.questions=$2; + lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR; + } + | MAX_UPDATES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.updates=$2; + lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR; + } + | MAX_CONNECTIONS_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.conn_per_hour= $2; + lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; + } + | MAX_USER_CONNECTIONS_SYM ulong_num + { + LEX *lex=Lex; + lex->mqh.user_conn= $2; + lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS; + } + ; + +begin: + BEGIN_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_BEGIN; + lex->start_transaction_opt= 0; + } + opt_work {} + ; + +opt_work: + /* empty */ {} + | WORK_SYM {} + ; + +opt_chain: + /* empty */ { $$= (YYTHD->variables.completion_type == 1); } + | AND_SYM NO_SYM CHAIN_SYM { $$=0; } + | AND_SYM CHAIN_SYM { $$=1; } + ; + +opt_release: + /* empty */ { $$= (YYTHD->variables.completion_type == 2); } + | RELEASE_SYM { $$=1; } + | NO_SYM RELEASE_SYM { $$=0; } + ; + +opt_savepoint: + /* empty */ {} + | SAVEPOINT_SYM {} + ; + +commit: + COMMIT_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_COMMIT; + lex->tx_chain= $3; + lex->tx_release= $4; + } + ; + +rollback: + ROLLBACK_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK; + lex->tx_chain= $3; + lex->tx_release= $4; + } + | ROLLBACK_SYM opt_work + TO_SYM opt_savepoint ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $5; + } + ; + +savepoint: + SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SAVEPOINT; + lex->ident= $2; + } + ; + +release: + RELEASE_SYM SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RELEASE_SAVEPOINT; + lex->ident= $3; + } + ; + +/* + UNIONS : glue selects together +*/ + + +union_clause: + /* empty */ {} + | union_list + ; + +union_list: + UNION_SYM union_option + { + LEX *lex=Lex; + if (lex->exchange) + { + /* Only the last SELECT can have INTO...... */ + my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); + YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + /* This counter shouldn't be incremented for UNION parts */ + Lex->nest_level--; + if (mysql_new_select(lex, 0)) + YYABORT; + mysql_init_select(lex); + lex->current_select->linkage=UNION_TYPE; + if ($2) /* UNION DISTINCT - remember position */ + lex->current_select->master_unit()->union_distinct= + lex->current_select; + } + select_init + { + /* + Remove from the name resolution context stack the context of the + last select in the union. + */ + Lex->pop_context(); + } + ; + +union_opt: + /* Empty */ { $$= 0; } + | union_list { $$= 1; } + | union_order_or_limit { $$= 1; } + ; + +union_order_or_limit: + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + SELECT_LEX *fake= unit->fake_select_lex; + if (fake) + { + unit->global_parameters= fake; + fake->no_table_names_allowed= 1; + lex->current_select= fake; + } + thd->where= "global ORDER clause"; + } + order_or_limit + { + THD *thd= YYTHD; + thd->lex->current_select->no_table_names_allowed= 0; + thd->where= ""; + } + ; + +order_or_limit: + order_clause opt_limit_clause_init + | limit_clause + ; + +union_option: + /* empty */ { $$=1; } + | DISTINCT { $$=1; } + | ALL { $$=0; } + ; + +subselect: + SELECT_SYM subselect_start subselect_init subselect_end + { + $$= $3; + } + | '(' subselect_start subselect ')' + { + LEX *lex= Lex; + THD *thd= YYTHD; + /* + note that a local variable can't be used for + $3 as it's used in local variable construction + and some compilers can't guarnatee the order + in which the local variables are initialized. + */ + List_iterator<Item> it($3->item_list); + Item *item; + /* + we must fill the items list for the "derived table". + */ + while ((item= it++)) + add_item_to_list(thd, item); + } + union_clause subselect_end { $$= $3; }; + +subselect_init: + select_init2 + { + $$= Lex->current_select->master_unit()->first_select(); + }; + +subselect_start: + { + LEX *lex=Lex; + if (!lex->expr_allows_subselect) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ + if (mysql_new_select(Lex, 1)) + YYABORT; + }; + +subselect_end: + { + LEX *lex=Lex; + lex->pop_context(); + SELECT_LEX *child= lex->current_select; + lex->current_select = lex->current_select->return_after_parsing(); + lex->nest_level--; + lex->current_select->n_child_sum_items += child->n_sum_items; + }; + +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp_or_event: + definer view_or_trigger_or_sp_or_event_tail + {} + | view_replace_or_algorithm definer view_tail + {} + ; + +view_or_trigger_or_sp_or_event_tail: + view_tail + {} + | trigger_tail + {} + | sp_tail + {} + | event_tail + {} + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + +definer: + /* empty */ + { + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + YYTHD->lex->definer= 0; + } + | DEFINER_SYM EQ user + { + YYTHD->lex->definer= get_current_user(YYTHD, $3); + } + ; + +/************************************************************************** + + CREATE VIEW statement parts. + +**************************************************************************/ + +view_replace_or_algorithm: + view_replace + {} + | view_replace view_algorithm + {} + | view_algorithm + {} + ; + +view_replace: + OR_SYM REPLACE + { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; } + ; + +view_algorithm: + ALGORITHM_SYM EQ UNDEFINED_SYM + { 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_TMPTABLE; } + ; + +view_algorithm_opt: + /* empty */ + { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } + | view_algorithm + {} + ; + +view_suid: + /* empty */ + { Lex->create_view_suid= VIEW_SUID_DEFAULT; } + | SQL_SYM SECURITY_SYM DEFINER_SYM + { Lex->create_view_suid= VIEW_SUID_DEFINER; } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { Lex->create_view_suid= VIEW_SUID_INVOKER; } + ; + +view_tail: + view_suid VIEW_SYM table_ident + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_CREATE_VIEW; + /* first table in list is target VIEW name */ + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) + YYABORT; + } + view_list_opt AS view_select view_check_option + {} + ; + +view_list_opt: + /* 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))); + } + ; + +view_select: + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= FALSE; + lex->parsing_options.allows_select_into= FALSE; + lex->parsing_options.allows_select_procedure= FALSE; + lex->parsing_options.allows_derived= FALSE; + } + view_select_aux + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= TRUE; + lex->parsing_options.allows_select_into= TRUE; + lex->parsing_options.allows_select_procedure= TRUE; + lex->parsing_options.allows_derived= TRUE; + } + ; + +view_select_aux: + SELECT_SYM remember_name select_init2 + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + char *stmt_beg= (lex->sphead ? + (char *)lex->sphead->m_tmp_query : + thd->query); + lex->create_view_select_start= $2 - stmt_beg; + } + | '(' remember_name select_paren ')' union_opt + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + char *stmt_beg= (lex->sphead ? + (char *)lex->sphead->m_tmp_query : + thd->query); + lex->create_view_select_start= $2 - stmt_beg; + } + ; + +view_check_option: + /* empty */ + { Lex->create_view_check= VIEW_CHECK_NONE; } + | WITH CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH CASCADED CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH LOCAL_SYM CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_LOCAL; } + ; + +/************************************************************************** + + CREATE TRIGGER statement parts. + +**************************************************************************/ + +trigger_tail: + TRIGGER_SYM remember_name sp_name trg_action_time trg_event + ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); + YYABORT; + } + + if (!(sp= new sp_head())) + YYABORT; + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + sp->init_sp_name(YYTHD, $3); + lex->stmt_definition_begin= $2; + lex->ident.str= $7; + lex->ident.length= $10 - $7; + + sp->m_type= TYPE_ENUM_TRIGGER; + lex->sphead= sp; + lex->spname= $3; + /* + We have to turn of CLIENT_MULTI_QUERIES while parsing a + stored procedure, otherwise yylex will chop it into pieces + at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->ptr; + while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0])) + ++lex->sphead->m_body_begin; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + lex->sql_command= SQLCOM_CREATE_TRIGGER; + sp->init_strings(YYTHD, lex); + /* Restore flag if it was cleared above */ + + YYTHD->client_capabilities |= $<ulong_num>13; + sp->restore_thd_mem_root(YYTHD); + + if (sp->is_not_allowed_in_function("trigger")) + YYABORT; + + /* + We have to do it after parsing trigger body, because some of + sp_proc_stmt alternatives are not saving/restoring LEX, so + lex->query_tables can be wiped out. + */ + if (!lex->select_lex.add_table_to_list(YYTHD, $8, + (LEX_STRING*) 0, + TL_OPTION_UPDATING, + TL_IGNORE)) + YYABORT; + } + ; + +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +sp_tail: + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); + YYABORT; + } + + lex->stmt_definition_begin= $2; + + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + sp->init_sp_name(YYTHD, $3); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + $<ulong_num>$= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + LEX *lex= Lex; + + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_pdparam_list + ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + sp->init_strings(YYTHD, lex); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* + Restore flag if it was cleared above + Be careful with counting. the block where we save the value + is $4. + */ + YYTHD->client_capabilities |= $<ulong_num>4; + sp->restore_thd_mem_root(YYTHD); + } + ; + +/*************************************************************************/ + +xa: XA_SYM begin_or_start xid opt_join_or_resume + { + Lex->sql_command = SQLCOM_XA_START; + } + | XA_SYM END xid opt_suspend + { + Lex->sql_command = SQLCOM_XA_END; + } + | XA_SYM PREPARE_SYM xid + { + Lex->sql_command = SQLCOM_XA_PREPARE; + } + | XA_SYM COMMIT_SYM xid opt_one_phase + { + Lex->sql_command = SQLCOM_XA_COMMIT; + } + | XA_SYM ROLLBACK_SYM xid + { + Lex->sql_command = SQLCOM_XA_ROLLBACK; + } + | XA_SYM RECOVER_SYM + { + Lex->sql_command = SQLCOM_XA_RECOVER; + } + ; + +xid: text_string + { + YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); + } + | text_string ',' text_string + { + YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + | text_string ',' text_string ',' ulong_num + { + YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) + YYABORT; + Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + ; + +begin_or_start: BEGIN_SYM {} + | START_SYM {} + ; + +opt_join_or_resume: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | JOIN_SYM { Lex->xa_opt=XA_JOIN; } + | RESUME_SYM { Lex->xa_opt=XA_RESUME; } + ; + +opt_one_phase: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; } + ; + +opt_suspend: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | SUSPEND_SYM { Lex->xa_opt=XA_SUSPEND; } + opt_migrate + ; + +opt_migrate: + /* nothing */ { } + | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; } + ; + +install: + INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= $3; + lex->ident= $5; + }; + +uninstall: + UNINSTALL_SYM PLUGIN_SYM ident + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= $3; + }; diff --git a/storage/federated/ha_federated.cc b/storage/federated/ha_federated.cc index 7330a19b791..397922e6e51 100644 --- a/storage/federated/ha_federated.cc +++ b/storage/federated/ha_federated.cc @@ -381,8 +381,8 @@ static handler *federated_create_handler(handlerton *hton, static byte *federated_get_key(FEDERATED_SHARE *share, uint *length, my_bool not_used __attribute__ ((unused))) { - *length= share->connect_string_length; - return (byte*) share->scheme; + *length= share->share_key_length; + return (byte*) share->share_key; } /* @@ -549,13 +549,14 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num) int buf_len; DBUG_ENTER("ha_federated parse_url_error"); - if (share->scheme) + if (share->connection_string) { DBUG_PRINT("info", - ("error: parse_url. Returning error code %d freeing share->scheme 0x%lx", - error_num, (long) share->scheme)); - my_free((gptr) share->scheme, MYF(0)); - share->scheme= 0; + ("error: parse_url. Returning error code %d \ + freeing share->connection_string %lx", + error_num, (long unsigned int) share->connection_string)); + my_free((gptr) share->connection_string, MYF(0)); + share->connection_string= 0; } buf_len= min(table->s->connect_string.length, FEDERATED_QUERY_BUFFER_SIZE-1); @@ -563,6 +564,77 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num) my_error(error_num, MYF(0), buf); DBUG_RETURN(error_num); } +/* + retrieve server object which contains server meta-data + from the system table given a server's name, set share + connection parameter members +*/ +int get_connection(FEDERATED_SHARE *share) +{ + int error_num= ER_FOREIGN_SERVER_DOESNT_EXIST; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + FOREIGN_SERVER *server; + MYSQL *mysql_conn; + MYSQL_RES *result= 0; + MYSQL_ROW row; + DBUG_ENTER("ha_federated::get_connection"); + + if (!(server= + get_server_by_name(share->connection_string))) + { + DBUG_PRINT("info", ("get_server_by_name returned > 0 error condition!")); + /* need to come up with error handling */ + error_num=1; + goto error; + } + DBUG_PRINT("info", ("get_server_by_name returned server at %lx", (long unsigned int) server)); + + /* + Most of these should never be empty strings, error handling will + need to be implemented. Also, is this the best way to set the share + members? Is there some allocation needed? In running this code, it works + except there are errors in the trace file of the share being overrun + at the address of the share. + */ + if (server->server_name) + share->server_name= server->server_name; + share->server_name_length= server->server_name_length ? + server->server_name_length : 0; + if (server->username) + share->username= server->username; + if (server->password) + share->password= server->password; + if (server->db) + share->database= server->db; + + share->port= server->port ? server->port : MYSQL_PORT; + + if (server->host) + share->hostname= server->host; + if (server->socket) + share->socket= server->socket; + else if (strcmp(share->hostname, my_localhost) == 0) + share->socket= my_strdup(MYSQL_UNIX_ADDR, MYF(0)); + if (server->scheme) + share->scheme= server->scheme; + else + share->scheme= NULL; + + DBUG_PRINT("info", ("share->username %s", share->username)); + DBUG_PRINT("info", ("share->password %s", share->password)); + DBUG_PRINT("info", ("share->hostname %s", share->hostname)); + DBUG_PRINT("info", ("share->database %s", share->database)); + DBUG_PRINT("info", ("share->port %d", share->port)); + DBUG_PRINT("info", ("share->socket %s", share->socket)); + DBUG_RETURN(0); + +error: + my_sprintf(error_buffer, + (error_buffer, "server name: '%s' doesn't exist!", + share->connection_string)); + my_error(error_num, MYF(0), error_buffer); + DBUG_RETURN(error_num); +} /* Parse connection info from table->s->connect_string @@ -576,22 +648,39 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num) DESCRIPTION Populates the share with information about the connection to the foreign database that will serve as the data source. - This string must be specified (currently) in the "comment" field, + This string must be specified (currently) in the "CONNECTION" field, listed in the CREATE TABLE statement. This string MUST be in the format of any of these: - scheme://username:password@hostname:port/database/table - scheme://username@hostname/database/table - scheme://username@hostname:port/database/table - scheme://username:password@hostname/database/table + CONNECTION="scheme://username:password@hostname:port/database/table" + CONNECTION="scheme://username@hostname/database/table" + CONNECTION="scheme://username@hostname:port/database/table" + CONNECTION="scheme://username:password@hostname/database/table" + + _OR_ + + CONNECTION="connection name" + + An Example: - mysql://joe:joespass@192.168.1.111:9308/federated/testtable + CREATE TABLE t1 (id int(32)) + ENGINE="FEDERATED" + CONNECTION="mysql://joe:joespass@192.168.1.111:9308/federated/testtable"; + + CREATE TABLE t2 ( + id int(4) NOT NULL auto_increment, + name varchar(32) NOT NULL, + PRIMARY KEY(id) + ) ENGINE="FEDERATED" CONNECTION="my_conn"; ***IMPORTANT*** - Currently, only "mysql://" is supported. + Currently, the Federated Storage Engine only supports connecting to another + MySQL Database ("scheme" of "mysql"). Connections using JDBC as well as + other connectors are in the planning stage. + 'password' and 'port' are both optional. @@ -611,87 +700,126 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, share->port= 0; share->socket= 0; + DBUG_PRINT("info", ("share at %lx", (long unsigned int) share)); DBUG_PRINT("info", ("Length: %d", table->s->connect_string.length)); - DBUG_PRINT("info", ("String: '%.*s'", table->s->connect_string.length, + DBUG_PRINT("info", ("String: '%.*s'", table->s->connect_string.length, table->s->connect_string.str)); - share->scheme= my_strndup(table->s->connect_string.str, - table->s->connect_string.length, - MYF(0)); - - share->connect_string_length= table->s->connect_string.length; - DBUG_PRINT("info",("parse_url alloced share->scheme 0x%lx", (long) share->scheme)); - - /* - remove addition of null terminator and store length - for each string in share - */ - if (!(share->username= strstr(share->scheme, "://"))) - goto error; - share->scheme[share->username - share->scheme]= '\0'; - - if (strcmp(share->scheme, "mysql") != 0) - goto error; - - share->username+= 3; + share->connection_string= my_strndup((const byte*)table->s-> + connect_string.str, + table->s->connect_string.length, + MYF(0)); + + // Add a null for later termination of table name + share->connection_string[table->s->connect_string.length]= 0; + DBUG_PRINT("info",("parse_url alloced share->connection_string %lx", + (long unsigned int) share->connection_string)); + + DBUG_PRINT("info",("share->connection_string %s",share->connection_string)); + /* No delimiters, must be a straight connection name */ + if ( (!strchr(share->connection_string, '/')) && + (!strchr(share->connection_string, '@')) && + (!strchr(share->connection_string, ';'))) + { - if (!(share->hostname= strchr(share->username, '@'))) - goto error; - - share->username[share->hostname - share->username]= '\0'; - share->hostname++; + DBUG_PRINT("info", + ("share->connection_string %s internal format \ + share->connection_string %lx", + share->connection_string, + (long unsigned int) share->connection_string)); - if ((share->password= strchr(share->username, ':'))) - { - share->username[share->password - share->username]= '\0'; - share->password++; - share->username= share->username; - /* make sure there isn't an extra / or @ */ - if ((strchr(share->password, '/') || strchr(share->hostname, '@'))) + share->parsed= FALSE; + if ((error_num= get_connection(share))) goto error; + /* - Found that if the string is: - user:@hostname:port/database/table - Then password is a null string, so set to NULL + connection specifies everything but, resort to + expecting remote and foreign table names to match */ - if ((share->password[0] == '\0')) - share->password= NULL; + share->table_name= table->s->table_name.str; + share->table_name_length= table->s->table_name.length; + share->table_name[share->table_name_length]= '\0'; } else - share->username= share->username; + { + share->parsed= TRUE; + // Add a null for later termination of table name + share->connection_string[table->s->connect_string.length]= 0; + share->scheme= share->connection_string; + DBUG_PRINT("info",("parse_url alloced share->scheme %lx", + (long unsigned int) share->scheme)); - /* make sure there isn't an extra / or @ */ - if ((strchr(share->username, '/')) || (strchr(share->hostname, '@'))) - goto error; + /* + remove addition of null terminator and store length + for each string in share + */ + if (!(share->username= strstr(share->scheme, "://"))) + goto error; + share->scheme[share->username - share->scheme]= '\0'; - if (!(share->database= strchr(share->hostname, '/'))) - goto error; - share->hostname[share->database - share->hostname]= '\0'; - share->database++; + if (strcmp(share->scheme, "mysql") != 0) + goto error; - if ((share->sport= strchr(share->hostname, ':'))) - { - share->hostname[share->sport - share->hostname]= '\0'; - share->sport++; - if (share->sport[0] == '\0') - share->sport= NULL; + share->username+= 3; + + if (!(share->hostname= strchr(share->username, '@'))) + goto error; + + share->username[share->hostname - share->username]= '\0'; + share->hostname++; + + if ((share->password= strchr(share->username, ':'))) + { + share->username[share->password - share->username]= '\0'; + share->password++; + share->username= share->username; + /* make sure there isn't an extra / or @ */ + if ((strchr(share->password, '/') || strchr(share->hostname, '@'))) + goto error; + /* + Found that if the string is: +user:@hostname:port/db/table +Then password is a null string, so set to NULL + */ + if ((share->password[0] == '\0')) + share->password= NULL; + } else - share->port= atoi(share->sport); - } + share->username= share->username; - if (!(share->table_name= strchr(share->database, '/'))) - goto error; - share->database[share->table_name - share->database]= '\0'; - share->table_name++; + /* make sure there isn't an extra / or @ */ + if ((strchr(share->username, '/')) || (strchr(share->hostname, '@'))) + goto error; - share->table_name_length= strlen(share->table_name); - - /* make sure there's not an extra / */ - if ((strchr(share->table_name, '/'))) - goto error; + if (!(share->database= strchr(share->hostname, '/'))) + goto error; + share->hostname[share->database - share->hostname]= '\0'; + share->database++; - if (share->hostname[0] == '\0') - share->hostname= NULL; + if ((share->sport= strchr(share->hostname, ':'))) + { + share->hostname[share->sport - share->hostname]= '\0'; + share->sport++; + if (share->sport[0] == '\0') + share->sport= NULL; + else + share->port= atoi(share->sport); + } + + if (!(share->table_name= strchr(share->database, '/'))) + goto error; + share->database[share->table_name - share->database]= '\0'; + share->table_name++; + share->table_name_length= strlen(share->table_name); + + /* make sure there's not an extra / */ + if ((strchr(share->table_name, '/'))) + goto error; + + if (share->hostname[0] == '\0') + share->hostname= NULL; + + } if (!share->port) { if (strcmp(share->hostname, my_localhost) == 0) @@ -702,7 +830,7 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, DBUG_PRINT("info", ("scheme: %s username: %s password: %s \ - hostname: %s port: %d database: %s tablename: %s", + hostname: %s port: %d db: %s tablename: %s", share->scheme, share->username, share->password, share->hostname, share->port, share->database, share->table_name)); @@ -713,7 +841,6 @@ error: DBUG_RETURN(parse_url_error(share, table, error_num)); } - /***************************************************************************** ** FEDERATED tables *****************************************************************************/ @@ -1313,14 +1440,16 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) pthread_mutex_lock(&federated_mutex); + tmp_share.share_key= table_name; + tmp_share.share_key_length= strlen(table_name); if (parse_url(&tmp_share, table, 0)) goto error; /* TODO: change tmp_share.scheme to LEX_STRING object */ if (!(share= (FEDERATED_SHARE *) hash_search(&federated_open_tables, - (byte*) tmp_share.scheme, + (byte*) tmp_share.share_key, tmp_share. - connect_string_length))) + share_key_length))) { query.set_charset(system_charset_info); query.append(STRING_WITH_LEN("SELECT ")); @@ -1367,7 +1496,8 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) error: pthread_mutex_unlock(&federated_mutex); - my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR)); + my_free((gptr) tmp_share.connection_string, MYF(MY_ALLOW_ZERO_PTR)); + tmp_share.connection_string= 0; my_free((gptr) share, MYF(MY_ALLOW_ZERO_PTR)); return NULL; } @@ -1387,8 +1517,14 @@ static int free_share(FEDERATED_SHARE *share) if (!--share->use_count) { hash_delete(&federated_open_tables, (byte*) share); - my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR)); - my_free((gptr) share->socket, MYF(MY_ALLOW_ZERO_PTR)); + if (share->parsed) + my_free((gptr) share->socket, MYF(MY_ALLOW_ZERO_PTR)); + /*if (share->connection_string) + { + */ + my_free((gptr) share->connection_string, MYF(MY_ALLOW_ZERO_PTR)); + share->connection_string= 0; + /*}*/ thr_lock_delete(&share->lock); VOID(pthread_mutex_destroy(&share->mutex)); my_free((gptr) share, MYF(0)); @@ -2697,7 +2833,9 @@ int ha_federated::create(const char *name, TABLE *table_arg, if (!(retval= parse_url(&tmp_share, table_arg, 1))) retval= check_foreign_data_source(&tmp_share, 1); - my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR)); + /* free this because strdup created it in parse_url */ + my_free((gptr) tmp_share.connection_string, MYF(MY_ALLOW_ZERO_PTR)); + tmp_share.connection_string= 0; DBUG_RETURN(retval); } diff --git a/storage/federated/ha_federated.h b/storage/federated/ha_federated.h index d1b485d63e2..63399e71bcd 100644 --- a/storage/federated/ha_federated.h +++ b/storage/federated/ha_federated.h @@ -43,6 +43,9 @@ The example implements the minimum of what you will probably need. */ typedef struct st_federated_share { + bool parsed; + /* this key is unique db/tablename */ + const char *share_key; /* the primary select query to be used in rnd_init */ @@ -50,6 +53,8 @@ typedef struct st_federated_share { /* remote host info, parse_url supplies */ + char *server_name; + char *connection_string; char *scheme; char *connect_string; char *hostname; @@ -60,8 +65,9 @@ typedef struct st_federated_share { char *table; char *socket; char *sport; + int share_key_length; ushort port; - uint table_name_length, connect_string_length, use_count; + uint table_name_length, server_name_length, connect_string_length, use_count; pthread_mutex_t mutex; THR_LOCK lock; } FEDERATED_SHARE; |