diff options
author | unknown <kostja@bodhi.local> | 2006-10-23 12:06:59 +0400 |
---|---|---|
committer | unknown <kostja@bodhi.local> | 2006-10-23 12:06:59 +0400 |
commit | efcd6b42b44ce178a779924c1e960cde3d9a4246 (patch) | |
tree | 48398b537ee608f80f6d1155dea687ca9d0e8239 | |
parent | 9852dfbcf5d3bfb5eec4316977b3d0d37761520e (diff) | |
parent | d76cf9973a9d8fdff1d815597a611e3cae2cf646 (diff) | |
download | mariadb-git-efcd6b42b44ce178a779924c1e960cde3d9a4246.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into bodhi.local:/opt/local/work/mysql-5.1-runtime-merge
mysql-test/r/ps.result:
Auto merged
mysql-test/t/func_gconcat.test:
Auto merged
mysql-test/t/ps.test:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_func.h:
Auto merged
sql/log_event.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/set_var.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql/sql_view.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
tests/mysql_client_test.c:
Auto merged
mysql-test/r/view.result:
Manual merge.
mysql-test/t/view.test:
Manual merge.
-rw-r--r-- | include/my_time.h | 2 | ||||
-rw-r--r-- | mysql-test/r/ps.result | 99 | ||||
-rw-r--r-- | mysql-test/r/sp-error.result | 32 | ||||
-rw-r--r-- | mysql-test/r/view.result | 22 | ||||
-rw-r--r-- | mysql-test/t/func_gconcat.test | 2 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 95 | ||||
-rw-r--r-- | mysql-test/t/sp-error.test | 57 | ||||
-rw-r--r-- | mysql-test/t/view.test | 52 | ||||
-rw-r--r-- | sql/event_data_objects.cc | 27 | ||||
-rw-r--r-- | sql/event_db_repository.cc | 10 | ||||
-rw-r--r-- | sql/item_sum.cc | 1 | ||||
-rw-r--r-- | sql/item_sum.h | 48 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/parse_file.h | 16 | ||||
-rw-r--r-- | sql/sql_base.cc | 11 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 21 | ||||
-rw-r--r-- | sql/sql_parse.cc | 9 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 13 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 8 | ||||
-rw-r--r-- | sql/sql_view.cc | 46 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 139 | ||||
-rw-r--r-- | sql/strfunc.cc | 30 |
23 files changed, 622 insertions, 121 deletions
diff --git a/include/my_time.h b/include/my_time.h index d0f2fc323d8..6f053e71000 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -100,6 +100,8 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to); /* The following must be sorted so that simple intervals comes first. (get_interval_value() depends on this) + When updating this enum please update + LEX_STRING interval_type_to_name[] in sql/time.cc */ enum interval_type diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index b7dae03bf47..e9a8dbf7f7d 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -546,7 +546,103 @@ GROUP_CONCAT(Track SEPARATOR ', ') CAD DEALLOCATE PREPARE STMT; DROP TABLE t1; -End of 4.1 tests +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT, INDEX(i)); +INSERT INTO t1 VALUES (1); +PREPARE stmt FROM "SELECT (COUNT(i) = 1), COUNT(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(COUNT(i) = 1) COUNT(i) +0 0 +PREPARE stmt FROM "SELECT (AVG(i) = 1), AVG(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +1 1.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(AVG(i) = 1) AVG(i) +NULL NULL +PREPARE stmt FROM "SELECT (VARIANCE(i) = 1), VARIANCE(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +0 0.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(VARIANCE(i) = 1) VARIANCE(i) +NULL NULL +PREPARE stmt FROM "SELECT (STDDEV(i) = 1), STDDEV(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +NULL NULL +SET @a = 1; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +0 0.0000 +SET @a = 0; +EXECUTE stmt USING @a; +(STDDEV(i) = 1) STDDEV(i) +NULL NULL +PREPARE stmt FROM "SELECT (BIT_OR(i) = 1), BIT_OR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_OR(i) = 1) BIT_OR(i) +0 0 +PREPARE stmt FROM "SELECT (BIT_AND(i) = 1), BIT_AND(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +0 18446744073709551615 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_AND(i) = 1) BIT_AND(i) +0 18446744073709551615 +PREPARE stmt FROM "SELECT (BIT_XOR(i) = 1), BIT_XOR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +0 0 +SET @a = 1; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +1 1 +SET @a = 0; +EXECUTE stmt USING @a; +(BIT_XOR(i) = 1) BIT_XOR(i) +0 0 +DEALLOCATE PREPARE stmt; +DROP TABLE t1; +End of 4.1 tests. create table t1 (a varchar(20)); insert into t1 values ('foo'); prepare stmt FROM 'SELECT char_length (a) FROM t1'; @@ -1360,6 +1456,7 @@ i 1 DEALLOCATE PREPARE stmt; DROP TABLE t1, t2; +DROP PROCEDURE IF EXISTS p1; End of 5.0 tests. create procedure proc_1() reset query cache; call proc_1(); diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index a2d783dbcbe..d955b69bde2 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1226,6 +1226,30 @@ END; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF NOT EXISTS bug14702() BEGIN END' at line 1 +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT); +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO @a; +ERROR HY000: View's SELECT contains a 'INTO' clause +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO DUMPFILE "file"; +ERROR HY000: View's SELECT contains a 'INTO' clause +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO OUTFILE "file"; +ERROR HY000: View's SELECT contains a 'INTO' clause +CREATE PROCEDURE bug20953() +CREATE VIEW v AS SELECT i FROM t1 PROCEDURE ANALYSE(); +ERROR HY000: View's SELECT contains a 'PROCEDURE' clause +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 FROM (SELECT 1) AS d1; +ERROR HY000: View's SELECT contains a subquery in the FROM clause +CREATE PROCEDURE bug20953(i INT) CREATE VIEW v AS SELECT i; +ERROR HY000: View's SELECT contains a variable or parameter +CREATE PROCEDURE bug20953() +BEGIN +DECLARE i INT; +CREATE VIEW v AS SELECT i; +END | +ERROR HY000: View's SELECT contains a variable or parameter +PREPARE stmt FROM "CREATE VIEW v AS SELECT ?"; +ERROR HY000: View's SELECT contains a variable or parameter +DROP TABLE t1; End of 5.0 tests drop function if exists bug16164; create function bug16164() returns int @@ -1234,9 +1258,9 @@ show authors; return 42; end| ERROR 0A000: Not allowed to return a result set from a function -drop function if exists bug20701| -create function bug20701() returns varchar(25) binary return "test"| +drop function if exists bug20701; +create function bug20701() returns varchar(25) binary return "test"; ERROR 42000: This version of MySQL doesn't yet support 'return value collation' -create function bug20701() returns varchar(25) return "test"| -drop function bug20701| +create function bug20701() returns varchar(25) return "test"; +drop function bug20701; End of 5.1 tests diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index ed31ade9d33..d58acfeafa5 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -12,6 +12,9 @@ create table t1 (a int, b int); insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); create view v1 (c,d) as select a,b+@@global.max_user_connections from t1; ERROR HY000: View's SELECT contains a variable or parameter +create view v1 (c,d) as select a,b from t1 +where a = @@global.max_user_connections; +ERROR HY000: View's SELECT contains a variable or parameter create view v1 (c) as select b+1 from t1; select c from v1; c @@ -596,11 +599,6 @@ ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function drop view v1; create view v1 (a,a) as select 'a','a'; ERROR 42S21: Duplicate column name 'a' -drop procedure if exists p1; -create procedure p1 () begin declare v int; create view v1 as select v; end;// -call p1(); -ERROR HY000: View's SELECT contains a variable or parameter -drop procedure p1; create table t1 (col1 int,col2 char(22)); insert into t1 values(5,'Hello, world of views'); create view v1 as select * from t1; @@ -886,6 +884,8 @@ ERROR HY000: View's SELECT contains a 'INTO' clause create table t1 (a int); create view v1 as select a from t1 procedure analyse(); ERROR HY000: View's SELECT contains a 'PROCEDURE' clause +create view v1 as select 1 from (select 1) as d1; +ERROR HY000: View's SELECT contains a subquery in the FROM clause drop table t1; create table t1 (s1 int, primary key (s1)); create view v1 as select * from t1; @@ -2956,6 +2956,18 @@ View Create View v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`pk` AS `pk` from (`t1` join `t2` on(((`t2`.`fk` = `t1`.`pk`) and (`t2`.`ver` = (select max(`t`.`ver`) AS `MAX(t.ver)` from `t2` `t` where (`t`.`org` = `t2`.`org`)))))) DROP VIEW v1; DROP TABLE t1, t2; +DROP FUNCTION IF EXISTS f1; +DROP VIEW IF EXISTS v1; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1); +CREATE VIEW v1 AS SELECT MAX(i) FROM t1; +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +SET NEW.i = (SELECT * FROM v1) + 1; +INSERT INTO t1 VALUES (1); +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT * FROM v1); +UPDATE t1 SET i= f1(); +DROP FUNCTION f1; CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, val INT UNSIGNED NOT NULL); CREATE VIEW v1 AS SELECT id, val FROM t1 WHERE val >= 1 AND val <= 5 WITH CHECK OPTION; INSERT INTO v1 (val) VALUES (2); diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index c2035132d06..db3536c6d36 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -98,7 +98,7 @@ select ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'withou select distinct ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'with distinct: cutoff at length of shortname' from t1; drop table t1; -# check zero rows +# check zero rows (bug#836) create table t1(id int); create table t2(id int); insert into t1 values(0),(1); diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index ac79dbc3434..0239be9b6d9 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1040,7 +1040,80 @@ EXECUTE STMT USING @id,@id; DEALLOCATE PREPARE STMT; DROP TABLE t1; ---echo End of 4.1 tests +# +# BUG#21354: (COUNT(*) = 1) not working in SELECT inside prepared +# statement +# +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT, INDEX(i)); +INSERT INTO t1 VALUES (1); + +PREPARE stmt FROM "SELECT (COUNT(i) = 1), COUNT(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (AVG(i) = 1), AVG(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (VARIANCE(i) = 1), VARIANCE(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (STDDEV(i) = 1), STDDEV(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_OR(i) = 1), BIT_OR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_AND(i) = 1), BIT_AND(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +PREPARE stmt FROM "SELECT (BIT_XOR(i) = 1), BIT_XOR(i) FROM t1 WHERE i = ?"; +SET @a = 0; +EXECUTE stmt USING @a; +SET @a = 1; +EXECUTE stmt USING @a; +SET @a = 0; +EXECUTE stmt USING @a; + +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + + +--echo End of 4.1 tests. + + ############################# 5.0 tests start ################################ # # @@ -1437,6 +1510,26 @@ DEALLOCATE PREPARE stmt; DROP TABLE t1, t2; +# +# BUG#21856: Prepared Statments: crash if bad create +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +let $iterations= 100; +--disable_query_log +--disable_result_log +while ($iterations > 0) +{ + --error ER_PARSE_ERROR + PREPARE stmt FROM "CREATE PROCEDURE p1()"; + dec $iterations; +} +--enable_query_log +--enable_result_log + + --echo End of 5.0 tests. # diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e829a71c45a..8754d9ca82d 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1770,6 +1770,47 @@ END; # +# BUG#20953: create proc with a create view that uses local +# vars/params should fail to create +# +# See test case for what syntax is forbidden in a view. +# +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# We do not have to drop this procedure and view because they won't be +# created. +--error ER_VIEW_SELECT_CLAUSE +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO @a; +--error ER_VIEW_SELECT_CLAUSE +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO DUMPFILE "file"; +--error ER_VIEW_SELECT_CLAUSE +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 INTO OUTFILE "file"; +--error ER_VIEW_SELECT_CLAUSE +CREATE PROCEDURE bug20953() + CREATE VIEW v AS SELECT i FROM t1 PROCEDURE ANALYSE(); +--error ER_VIEW_SELECT_DERIVED +CREATE PROCEDURE bug20953() CREATE VIEW v AS SELECT 1 FROM (SELECT 1) AS d1; +--error ER_VIEW_SELECT_VARIABLE +CREATE PROCEDURE bug20953(i INT) CREATE VIEW v AS SELECT i; +delimiter |; +--error ER_VIEW_SELECT_VARIABLE +CREATE PROCEDURE bug20953() +BEGIN + DECLARE i INT; + CREATE VIEW v AS SELECT i; +END | +delimiter ;| +--error ER_VIEW_SELECT_VARIABLE +PREPARE stmt FROM "CREATE VIEW v AS SELECT ?"; + +DROP TABLE t1; + + +# # End of 5.0 tests # --echo End of 5.0 tests @@ -1788,12 +1829,14 @@ begin show authors; return 42; end| +delimiter ;| + # # BUG#20701: BINARY keyword should be forbidden in stored routines # --disable_warnings -drop function if exists bug20701| +drop function if exists bug20701; --enable_warnings # # This was disabled in 5.1.12. See bug #20701 @@ -1801,17 +1844,19 @@ drop function if exists bug20701| # be removed. # --error ER_NOT_SUPPORTED_YET -create function bug20701() returns varchar(25) binary return "test"| -create function bug20701() returns varchar(25) return "test"| -drop function bug20701| +create function bug20701() returns varchar(25) binary return "test"; +create function bug20701() returns varchar(25) return "test"; +drop function bug20701; + + --echo End of 5.1 tests # # BUG#NNNN: New bug synopsis # #--disable_warnings -#drop procedure if exists bugNNNN| -#drop function if exists bugNNNN| +#drop procedure if exists bugNNNN; +#drop function if exists bugNNNN; #--enable_warnings #create procedure bugNNNN... #create function bugNNNN... diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index f86e32e6a82..2446bedfc14 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -23,8 +23,11 @@ create table t1 (a int, b int); insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10); # view with variable --- error 1351 +-- error ER_VIEW_SELECT_VARIABLE create view v1 (c,d) as select a,b+@@global.max_user_connections from t1; +-- error ER_VIEW_SELECT_VARIABLE +create view v1 (c,d) as select a,b from t1 + where a = @@global.max_user_connections; # simple view create view v1 (c) as select b+1 from t1; @@ -487,19 +490,6 @@ drop view v1; create view v1 (a,a) as select 'a','a'; # -# SP variables inside view test -# ---disable_warnings -drop procedure if exists p1; ---enable_warnings -delimiter //; -create procedure p1 () begin declare v int; create view v1 as select v; end;// -delimiter ;// --- error 1351 -call p1(); -drop procedure p1; - -# # updatablity should be transitive # create table t1 (col1 int,col2 char(22)); @@ -820,6 +810,8 @@ create view v1 as select 5 into outfile 'ttt'; create table t1 (a int); -- error 1350 create view v1 as select a from t1 procedure analyse(); +-- error ER_VIEW_SELECT_DERIVED +create view v1 as select 1 from (select 1) as d1; drop table t1; # @@ -2886,6 +2878,38 @@ DROP VIEW v1; DROP TABLE t1, t2; # +# Bug#19111: TRIGGERs selecting from a VIEW on the firing base table +# fail +# +# Allow to select from a view on a table being modified in a trigger +# and stored function, since plain select is allowed there. +# +--disable_warnings +DROP FUNCTION IF EXISTS f1; +DROP VIEW IF EXISTS v1; +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1); + +CREATE VIEW v1 AS SELECT MAX(i) FROM t1; + +# Plain 'SET NEW.i = (SELECT MAX(i) FROM t1) + 1' works, so select +# from a view should work too. +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + SET NEW.i = (SELECT * FROM v1) + 1; +INSERT INTO t1 VALUES (1); + +# Plain 'RETURN (SELECT MAX(i) FROM t1)' works in INSERT, so select +# from a view should work too. +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT * FROM v1); +UPDATE t1 SET i= f1(); + +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE t1; + # Bug #16813 (WITH CHECK OPTION doesn't work with UPDATE) # CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, val INT UNSIGNED NOT NULL); diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 4b9aa43b14b..afd10350bb5 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -886,14 +886,29 @@ Event_queue_element::load_from_row(TABLE *table) goto error; /* - In DB the values start from 1 but enum interval_type starts - from 0 + We load the interval type from disk as string and then map it to + an integer. This decouples the values of enum interval_type + and values actually stored on disk. Therefore the type can be + reordered without risking incompatibilities of data between versions. */ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null()) - interval= (interval_type) ((ulonglong) - table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_int() - 1); - else - interval= (interval_type) 0; + { + int i; + char buff[MAX_FIELD_WIDTH]; + String str(buff, sizeof(buff), &my_charset_bin); + LEX_STRING tmp; + + table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str); + if (!(tmp.length= str.length())) + goto error; + + tmp.str= str.c_ptr_safe(); + + i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info); + if (i < 0) + goto error; + interval= (interval_type) i; + } table->field[ET_FIELD_LAST_EXECUTED]->get_date(&last_executed, TIME_NO_ZERO_DATE); diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 464c26044c7..3d30aff669b 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -188,11 +188,11 @@ mysql_event_fill_row(THD *thd, TABLE *table, Event_parse_data *et, fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE); fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull(); - /* - In the enum (C) intervals start from 0 but in mysql enum valid values - start from 1. Thus +1 offset is needed! - */ - fields[ET_FIELD_TRANSIENT_INTERVAL]->store((longlong)et->interval+1, TRUE); + + fields[ET_FIELD_TRANSIENT_INTERVAL]-> + store(interval_type_to_name[et->interval].str, + interval_type_to_name[et->interval].length, + scs); fields[ET_FIELD_EXECUTE_AT]->set_null(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 73e2c5e6935..1c574cc872f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1065,6 +1065,7 @@ longlong Item_sum_count::val_int() void Item_sum_count::cleanup() { DBUG_ENTER("Item_sum_count::cleanup"); + clear(); Item_sum_int::cleanup(); used_table_cache= ~(table_map) 0; DBUG_VOID_RETURN; diff --git a/sql/item_sum.h b/sql/item_sum.h index 3679780db60..0204e88a9e1 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -260,9 +260,30 @@ public: Item_sum(THD *thd, Item_sum *item); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; + + /* + This method is similar to add(), but it is called when the current + aggregation group changes. Thus it performs a combination of + clear() and add(). + */ inline bool reset() { clear(); return add(); }; + + /* + Prepare this item for evaluation of an aggregate value. This is + called by reset() when a group changes, or, for correlated + subqueries, between subquery executions. E.g. for COUNT(), this + method should set count= 0; + */ virtual void clear()= 0; + + /* + This method is called for the next row in the same group. Its + purpose is to aggregate the new value to the previous values in + the group (i.e. since clear() was called last time). For example, + for COUNT(), do count++. + */ virtual bool add()=0; + /* Called when new group is started and results are being saved in a temporary table. Similar to reset(), but must also store value in @@ -306,7 +327,17 @@ public: void make_field(Send_field *field); void print(String *str); void fix_num_length_and_dec(); - void no_rows_in_result() { reset(); } + + /* + This function is called by the execution engine to assign 'NO ROWS + FOUND' value to an aggregate item, when the underlying result set + has no rows. Such value, in a general case, may be different from + the default value of the item after 'clear()': e.g. a numeric item + may be initialized to 0 by clear() and to NULL by + no_rows_in_result(). + */ + void no_rows_in_result() { clear(); } + virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); @@ -610,6 +641,11 @@ public: const char *func_name() const { return "avg("; } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); + void cleanup() + { + clear(); + Item_sum_num::cleanup(); + } }; class Item_sum_variance; @@ -689,6 +725,11 @@ public: Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); enum Item_result result_type () const { return REAL_RESULT; } + void cleanup() + { + clear(); + Item_sum_num::cleanup(); + } }; class Item_sum_std; @@ -819,6 +860,11 @@ public: void update_field(); void fix_length_and_dec() { decimals= 0; max_length=21; unsigned_flag= 1; maybe_null= null_value= 0; } + void cleanup() + { + clear(); + Item_sum_int::cleanup(); + } }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b7ef2cd97df..fab9bcaca1b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1485,6 +1485,8 @@ uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs); void unhex_type2(TYPELIB *lib); uint check_word(TYPELIB *lib, const char *val, const char *end, const char **end_of_word); +int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, + CHARSET_INFO * const cs); bool is_keyword(const char *name, uint len); diff --git a/sql/parse_file.h b/sql/parse_file.h index 33871588e11..5fb65b4c7ec 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -107,4 +107,20 @@ public: bool bad_format_errors); }; + +/* + Custom version of standard offsetof() macro which can be used to get + offsets of members in class for non-POD types (according to the current + version of C++ standard offsetof() macro can't be used in such cases and + attempt to do so causes warnings to be emitted, OTOH in many cases it is + still OK to assume that all instances of the class has the same offsets + for the same members). + + This is temporary solution which should be removed once File_parser class + and related routines are refactored. +*/ + +#define my_offsetof(TYPE, MEMBER) \ + ((size_t)((char *)&(((TYPE *)0x10)->MEMBER) - (char*)0x10)) + #endif /* _PARSE_FILE_H_ */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 3e7bcf7798a..c2cf170dbe3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1365,6 +1365,10 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, Also SELECT::exclude_from_table_unique_test used to exclude from check tables of main SELECT of multi-delete and multi-update + We also skip tables with TABLE_LIST::prelocking_placeholder set, + because we want to allow SELECTs from them, and their modification + will rise the error anyway. + TODO: when we will have table/view change detection we can do this check only once for PS/SP @@ -1411,12 +1415,13 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list) if (((! (res= find_table_in_global_list(table_list, d_name, t_name))) && (! (res= mysql_lock_have_duplicate(thd, table, table_list)))) || ((!res->table || res->table != table->table) && - res->select_lex && !res->select_lex->exclude_from_table_unique_test)) + res->select_lex && !res->select_lex->exclude_from_table_unique_test && + !res->prelocking_placeholder)) break; /* - If we found entry of this table or or table of SELECT which already + If we found entry of this table or table of SELECT which already processed in derived table or top select of multi-update/multi-delete - (exclude_from_table_unique_test). + (exclude_from_table_unique_test) or prelocking placeholder. */ table_list= res->next_global; DBUG_PRINT("info", diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index dd358492d0d..2aa7e1c11c4 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -151,7 +151,6 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->safe_to_cache_query= 1; lex->time_zone_tables_used= 0; lex->leaf_tables_insert= 0; - lex->variables_used= 0; lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; lex->next_state=MY_LEX_START; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1815774526f..c8acc9aeca4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -867,6 +867,25 @@ public: }; +/* + st_parsing_options contains the flags for constructions that are + allowed in the current statement. +*/ + +struct st_parsing_options +{ + bool allows_variable; + bool allows_select_into; + bool allows_select_procedure; + bool allows_derived; + + st_parsing_options() + : allows_variable(TRUE), allows_select_into(TRUE), + allows_select_procedure(TRUE), allows_derived(TRUE) + {} +}; + + /* The state of the lex parsing. This is saved in the THD struct */ typedef struct st_lex : public Query_tables_list @@ -1023,7 +1042,7 @@ typedef struct st_lex : public Query_tables_list bool stmt_prepare_mode; bool safe_to_cache_query; bool subqueries, ignore; - bool variables_used; + st_parsing_options parsing_options; ALTER_INFO alter_info; /* Prepared statements SQL syntax:*/ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fc7ed7ff673..09bf1737e39 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6088,14 +6088,19 @@ void mysql_parse(THD *thd, char *inBuf, uint length) DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); - lex->unit.cleanup(); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ if (lex->sphead) { /* Clean up after failed stored procedure/function */ delete lex->sphead; lex->sphead= NULL; } + query_cache_abort(&thd->net); + lex->unit.cleanup(); } thd->proc_info="freeing items"; thd->end_statement(); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 63a129918d0..314d4ed5631 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2811,7 +2811,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ + if (error && lex->sphead) + { + delete lex->sphead; + lex->sphead= NULL; + } + lex->safe_to_cache_query= FALSE; + /* While doing context analysis of the query (in check_prepared_statement) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -2837,6 +2849,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (error == 0) error= check_prepared_statement(this, name.str != 0); + /* Free sp_head if check_prepared_statement() failed. */ if (error && lex->sphead) { delete lex->sphead; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index acb7d5b61df..ca95b39f215 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -36,17 +36,17 @@ static File_option triggers_file_parameters[]= { { { C_STRING_WITH_LEN("triggers") }, - offsetof(class Table_triggers_list, definitions_list), + my_offsetof(class Table_triggers_list, definitions_list), FILE_OPTIONS_STRLIST }, { { C_STRING_WITH_LEN("sql_modes") }, - offsetof(class Table_triggers_list, definition_modes_list), + my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }, { { C_STRING_WITH_LEN("definers") }, - offsetof(class Table_triggers_list, definers_list), + my_offsetof(class Table_triggers_list, definers_list), FILE_OPTIONS_STRLIST }, { { 0, 0 }, 0, FILE_OPTIONS_STRING } @@ -55,7 +55,7 @@ static File_option triggers_file_parameters[]= File_option sql_modes_parameters= { { C_STRING_WITH_LEN("sql_modes") }, - offsetof(class Table_triggers_list, definition_modes_list), + my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 7f6d935ff5e..718b776400c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -236,25 +236,9 @@ bool mysql_create_view(THD *thd, bool res= FALSE; DBUG_ENTER("mysql_create_view"); - if (lex->proc_list.first || - lex->result) - { - my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), (lex->result ? - "INTO" : - "PROCEDURE")); - res= TRUE; - goto err; - } - if (lex->derived_tables || - lex->variables_used || lex->param_list.elements) - { - int err= (lex->derived_tables ? - ER_VIEW_SELECT_DERIVED : - ER_VIEW_SELECT_VARIABLE); - my_message(err, ER(err), MYF(0)); - res= TRUE; - goto err; - } + /* This is ensured in the parser. */ + DBUG_ASSERT(!lex->proc_list.first && !lex->result && + !lex->param_list.elements && !lex->derived_tables); if (mode != VIEW_CREATE_NEW) { @@ -582,40 +566,40 @@ static const int num_view_backups= 3; */ static File_option view_parameters[]= {{{ C_STRING_WITH_LEN("query")}, - offsetof(TABLE_LIST, query), + my_offsetof(TABLE_LIST, query), FILE_OPTIONS_ESTRING}, {{ C_STRING_WITH_LEN("md5")}, - offsetof(TABLE_LIST, md5), + my_offsetof(TABLE_LIST, md5), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("updatable")}, - offsetof(TABLE_LIST, updatable_view), + my_offsetof(TABLE_LIST, updatable_view), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("algorithm")}, - offsetof(TABLE_LIST, algorithm), + my_offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("definer_user")}, - offsetof(TABLE_LIST, definer.user), + my_offsetof(TABLE_LIST, definer.user), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("definer_host")}, - offsetof(TABLE_LIST, definer.host), + my_offsetof(TABLE_LIST, definer.host), FILE_OPTIONS_STRING}, {{ C_STRING_WITH_LEN("suid")}, - offsetof(TABLE_LIST, view_suid), + my_offsetof(TABLE_LIST, view_suid), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("with_check_option")}, - offsetof(TABLE_LIST, with_check), + my_offsetof(TABLE_LIST, with_check), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("revision")}, - offsetof(TABLE_LIST, revision), + my_offsetof(TABLE_LIST, revision), FILE_OPTIONS_REV}, {{ C_STRING_WITH_LEN("timestamp")}, - offsetof(TABLE_LIST, timestamp), + my_offsetof(TABLE_LIST, timestamp), FILE_OPTIONS_TIMESTAMP}, {{ C_STRING_WITH_LEN("create-version")}, - offsetof(TABLE_LIST, file_version), + my_offsetof(TABLE_LIST, file_version), FILE_OPTIONS_ULONGLONG}, {{ C_STRING_WITH_LEN("source")}, - offsetof(TABLE_LIST, source), + my_offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, {{NullS, 0}, 0, FILE_OPTIONS_STRING} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 26dea00029a..05c57479c42 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -793,7 +793,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr - bool_term bool_factor bool_test bool_pri + 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 @@ -5953,32 +5953,7 @@ simple_expr: } | literal | param_marker - | '@' ident_or_text SET_VAR expr - { - $$= new Item_func_set_user_var($2,$4); - LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); - lex->variables_used= 1; - } - | '@' ident_or_text - { - $$= new Item_func_get_user_var($2); - LEX *lex= Lex; - lex->uncacheable(UNCACHEABLE_RAND); - lex->variables_used= 1; - } - | '@' '@' opt_var_ident_type ident_or_text opt_component - { - - if ($4.str && $5.str && check_reserved_words(&$4)) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } - if (!($$= get_system_var(YYTHD, $3, $4, $5))) - YYABORT; - Lex->variables_used= 1; - } + | variable | sum_expr | simple_expr OR_OR_SYM simple_expr { $$= new Item_func_concat($1, $3); } @@ -6701,6 +6676,46 @@ sum_expr: $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; }; @@ -7130,6 +7145,13 @@ 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)) @@ -7514,6 +7536,13 @@ procedure_clause: | 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"); @@ -7613,28 +7642,40 @@ select_var_ident: ; into: - INTO OUTFILE TEXT_STRING_filesystem + 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($3.str, 0)) || + if (!(lex->exchange= new sql_exchange($2.str, 0)) || !(lex->result= new select_export(lex->exchange))) YYABORT; } opt_field_term opt_line_term - | INTO DUMPFILE TEXT_STRING_filesystem + | DUMPFILE TEXT_STRING_filesystem { LEX *lex=Lex; if (!lex->describe) { lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($3.str,1))) + if (!(lex->exchange= new sql_exchange($2.str,1))) YYABORT; if (!(lex->result= new select_dump(lex->exchange))) YYABORT; } } - | INTO select_var_list_init + | select_var_list_init { Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); } @@ -8843,8 +8884,13 @@ param_marker: { THD *thd=YYTHD; LEX *lex= thd->lex; - Item_param *item= new Item_param((uint) (lex->tok_start - - (uchar *) thd->query)); + 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)); @@ -8964,6 +9010,12 @@ simple_ident: 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 - @@ -8973,7 +9025,6 @@ simple_ident: splocal->m_sp= lex->sphead; #endif $$ = (Item*) splocal; - lex->variables_used= 1; lex->safe_to_cache_query=0; } else @@ -10872,6 +10923,24 @@ view_list: ; 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; diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 2525703172f..ef769a5b16e 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -312,3 +312,33 @@ outp: return (uint32) (to - to_start); } + + +/* + Searches for a LEX_STRING in an LEX_STRING array. + + SYNOPSIS + find_string_in_array() + heap The array + needle The string to search for + + NOTE + The last LEX_STRING in the array should have str member set to NULL + + RETURN VALUES + -1 Not found + >=0 Ordinal position +*/ + +int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, + CHARSET_INFO * const cs) +{ + const LEX_STRING *pos; + for (pos= haystack; pos->str; pos++) + if (!cs->coll->strnncollsp(cs, (uchar *) pos->str, pos->length, + (uchar *) needle->str, needle->length, 0)) + { + return (pos - haystack); + } + return -1; +} |