diff options
93 files changed, 8009 insertions, 500 deletions
diff --git a/include/my_base.h b/include/my_base.h index 89f5e826fd5..ea8fd623b28 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -48,6 +48,8 @@ #define HA_OPEN_INTERNAL_TABLE 512U #define HA_OPEN_NO_PSI_CALL 1024U /* Don't call/connect PSI */ #define HA_OPEN_MERGE_TABLE 2048U +#define HA_OPEN_FOR_CREATE 4096U + /* Allow opening even if table is incompatible as this is for ALTER TABLE which will fix the table structure. @@ -351,6 +353,7 @@ enum ha_base_keytype { #define HA_CREATE_RELIES_ON_SQL_LAYER 128U #define HA_CREATE_INTERNAL_TABLE 256U #define HA_PRESERVE_INSERT_ORDER 512U +#define HA_CREATE_NO_ROLLBACK 1024U /* Flags used by start_bulk_insert */ @@ -500,7 +503,9 @@ enum ha_base_keytype { #define HA_ERR_DECRYPTION_FAILED 192 /* Table encrypted but decypt failed */ #define HA_ERR_FK_DEPTH_EXCEEDED 193 /* FK cascade depth exceeded */ #define HA_ERR_TABLESPACE_MISSING 194 /* Missing Tablespace */ -#define HA_ERR_LAST 194 /* Copy of last error nr * */ +#define HA_ERR_SEQUENCE_INVALID_DATA 195 +#define HA_ERR_SEQUENCE_RUN_OUT 196 +#define HA_ERR_LAST 196 /* Copy of last error nr * */ /* Number of different errors */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/include/my_handler_errors.h b/include/my_handler_errors.h index bdea4f71eaf..4b85832a6de 100644 --- a/include/my_handler_errors.h +++ b/include/my_handler_errors.h @@ -105,7 +105,9 @@ static const char *handler_error_messages[]= "Too many words in a FTS phrase or proximity search", "Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.", "Foreign key cascade delete/update exceeds max depth", - "Tablespace is missing for table" + "Tablespace is missing for table", + "Sequence has been run out", + "Sequence values are conflicting" }; #endif /* MYSYS_MY_HANDLER_ERRORS_INCLUDED */ diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 07e7d433dde..6dabc5e0192 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -113,6 +113,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/wsrep_dummy.cc ../sql/encryption.cc ../sql/item_windowfunc.cc ../sql/sql_window.cc ../sql/sql_cte.cc + ../sql/sql_sequence.cc ../sql/sql_sequence.h + ../sql/ha_sequence.cc ../sql/ha_sequence.h ../sql/temporary_tables.cc ../sql/session_tracker.cc ${GEN_SOURCES} diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5d81ee3265b..17a2d7db632 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -194,6 +194,7 @@ my @DEFAULT_SUITES= qw( roles- rpl- sys_vars- + sql_sequence- unit- vcol- wsrep- diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index cfd2ede6ef9..778bfb13bd3 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1396,7 +1396,7 @@ performance-schema-max-rwlock-instances -1 performance-schema-max-socket-classes 10 performance-schema-max-socket-instances -1 performance-schema-max-stage-classes 150 -performance-schema-max-statement-classes 187 +performance-schema-max-statement-classes 189 performance-schema-max-table-handles -1 performance-schema-max-table-instances -1 performance-schema-max-thread-classes 50 diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 98aa2b50fc6..6af6b167511 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -4,7 +4,7 @@ CREATE FUNCTION myfunc_double RETURNS REAL SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION myfunc_nonexist RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; ERROR HY000: Can't find symbol 'myfunc_nonexist' in library CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; -CREATE FUNCTION sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION lookup RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; @@ -226,7 +226,7 @@ DROP FUNCTION myfunc_double; DROP FUNCTION myfunc_nonexist; ERROR 42000: FUNCTION test.myfunc_nonexist does not exist DROP FUNCTION myfunc_int; -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP FUNCTION lookup; DROP FUNCTION reverse_lookup; DROP FUNCTION avgcost; @@ -340,32 +340,32 @@ DROP FUNCTION check_const_len; DROP PROCEDURE check_const_len_sp; DROP TRIGGER check_const_len_trigger; DROP TABLE const_len_bug; -CREATE FUNCTION sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; CREATE TABLE t1 (a INT); CREATE TABLE t2 (a INT PRIMARY KEY); INSERT INTO t1 VALUES (4),(3),(2),(1); INSERT INTO t2 SELECT * FROM t1; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq ASC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq ASC; seq a 1 4 2 3 3 2 4 1 -SELECT sequence() AS seq, a FROM t1 ORDER BY seq DESC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq DESC; seq a 4 1 3 2 2 3 1 4 -SELECT * FROM t1 WHERE a = sequence(); +SELECT * FROM t1 WHERE a = udf_sequence(); a -SELECT * FROM t2 WHERE a = sequence(); +SELECT * FROM t2 WHERE a = udf_sequence(); a 1 2 3 4 -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP TABLE t1,t2; drop function if exists test.metaphon; drop function if exists metaphon; diff --git a/mysql-test/r/udf_notembedded.result b/mysql-test/r/udf_notembedded.result index 3fdcdbbe9d3..377af563d3e 100644 --- a/mysql-test/r/udf_notembedded.result +++ b/mysql-test/r/udf_notembedded.result @@ -1,6 +1,6 @@ -create function sequence returns integer soname "UDF_EXAMPLE_LIB"; -create table t1 (n int key not null auto_increment, msg int as (sequence()) virtual); +create function udf_sequence returns integer soname "UDF_EXAMPLE_LIB"; +create table t1 (n int key not null auto_increment, msg int as (udf_sequence()) virtual); select * from t1; n msg drop table t1; -drop function sequence; +drop function udf_sequence; diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 1cde61620a6..a2a5f434f18 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -85,8 +85,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe UUID() function. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -149,8 +147,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe UUID() function. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -411,8 +407,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe @@hostname variable. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -475,8 +469,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe @@hostname variable. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -657,8 +649,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe SELECT...LIMIT statement. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1049,8 +1039,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe update of two autoinc columns. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1245,8 +1233,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe UDF. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -1309,8 +1295,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe UDF. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1551,8 +1535,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe use of mysql.general_log. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1823,14 +1805,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking statement that is unsafe in many ways. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave * binlog_format = STATEMENT: expect 7 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -2099,9 +2073,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -2235,9 +2206,6 @@ DROP TRIGGER trig_1; Invoking view view_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_1 AS SELECT multi_unsafe_func(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t1 SELECT * FROM view_sidef_1; Warnings: @@ -2281,9 +2249,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking view view_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_2 AS SELECT * FROM view_sidef_1; -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: diff --git a/mysql-test/suite/compat/oracle/r/sequence.result b/mysql-test/suite/compat/oracle/r/sequence.result new file mode 100644 index 00000000000..e3bf9d4daee --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sequence.result @@ -0,0 +1,60 @@ +SET sql_mode=ORACLE; +CREATE SEQUENCE s1; +SHOW CREATE SEQUENCE s1; +Table Create Table +s1 CREATE SEQUENCE "s1" start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle +SELECT s1.currval; +s1.currval +NULL +SELECT s1.nextval; +s1.nextval +1 +SELECT s1.nextval; +s1.nextval +2 +SELECT s1.nextval; +s1.nextval +3 +EXPLAIN EXTENDED SELECT s1.nextval; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select nextval("test"."s1") AS "s1.nextval" +SELECT nextval(s1); +nextval(s1) +4 +EXPLAIN EXTENDED SELECT s1.currval; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select lastval("test"."s1") AS "s1.currval" +SELECT lastval(s1); +lastval(s1) +4 +DROP SEQUENCE s1; +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.nextval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +VIEW_DEFINITION +select nextval(`test`.`s1`) AS `a` +SELECT * FROM v1; +a +1 +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE VIEW "v1" AS select nextval("test"."s1") AS "a" latin1 latin1_swedish_ci +DROP VIEW v1; +DROP SEQUENCE s1; +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.currval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +VIEW_DEFINITION +select lastval(`test`.`s1`) AS `a` +SELECT * FROM v1; +a +NULL +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE VIEW "v1" AS select lastval("test"."s1") AS "a" latin1 latin1_swedish_ci +DROP VIEW v1; +DROP SEQUENCE s1; diff --git a/mysql-test/suite/compat/oracle/t/sequence.test b/mysql-test/suite/compat/oracle/t/sequence.test new file mode 100644 index 00000000000..bc861c03014 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sequence.test @@ -0,0 +1,33 @@ +--source include/have_binlog_format_row.inc + +SET sql_mode=ORACLE; + +CREATE SEQUENCE s1; +SHOW CREATE SEQUENCE s1; +SELECT s1.currval; +SELECT s1.nextval; +SELECT s1.nextval; +SELECT s1.nextval; +EXPLAIN EXTENDED SELECT s1.nextval; +SELECT nextval(s1); +EXPLAIN EXTENDED SELECT s1.currval; +SELECT lastval(s1); +DROP SEQUENCE s1; + + +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.nextval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +SELECT * FROM v1; +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP SEQUENCE s1; + + +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.currval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +SELECT * FROM v1; +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP SEQUENCE s1; diff --git a/mysql-test/suite/sql_sequence/binlog.result b/mysql-test/suite/sql_sequence/binlog.result new file mode 100644 index 00000000000..80746dcdfcf --- /dev/null +++ b/mysql-test/suite/sql_sequence/binlog.result @@ -0,0 +1,31 @@ +create or replace sequence s1 cache 3; +select next value for s1, min_value from s1 where max_value> 1; +next value for s1 min_value +1 1 +select next value for s1, min_value from s1 where max_value> 2; +next value for s1 min_value +2 1 +select next value for s1, min_value from s1 where max_value> 3; +next value for s1 min_value +3 1 +select next value for s1, min_value from s1 where max_value> 4; +next value for s1 min_value +4 1 +drop sequence s1; +"Runnig SHOW BINLOG EVENTS" +Log_name Pos Event_type Server_id End_log_pos Info +# # Gtid 1 # GTID #-#-# +# # Query 1 # use `test`; create or replace sequence s1 cache 3 +# # Gtid 1 # BEGIN GTID #-#-# +# # Annotate_rows 1 # select next value for s1, min_value from s1 where max_value> 1 +# # Table_map 1 # table_id: 30 (test.s1) +# # Write_rows_v1 1 # table_id: 30 flags: STMT_END_F +# # Query 1 # COMMIT +# # Gtid 1 # BEGIN GTID #-#-# +# # Annotate_rows 1 # select next value for s1, min_value from s1 where max_value> 4 +# # Table_map 1 # table_id: 30 (test.s1) +# # Write_rows_v1 1 # table_id: 30 flags: STMT_END_F +# # Query 1 # COMMIT +# # Gtid 1 # GTID #-#-# +# # Query 1 # use `test`; DROP TABLE `s1` /* generated by server */ +RESET MASTER; diff --git a/mysql-test/suite/sql_sequence/binlog.test b/mysql-test/suite/sql_sequence/binlog.test new file mode 100644 index 00000000000..4ad74e04083 --- /dev/null +++ b/mysql-test/suite/sql_sequence/binlog.test @@ -0,0 +1,26 @@ +--source include/have_udf.inc +--source include/have_log_bin.inc +--source include/binlog_start_pos.inc + +# +# Testing binary logging of sequences +# + +--let $pos=`select $binlog_start_pos + 73` +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_start=query_get_value(SHOW MASTER STATUS, Position, 1) + +create or replace sequence s1 cache 3; +select next value for s1, min_value from s1 where max_value> 1; +select next value for s1, min_value from s1 where max_value> 2; +select next value for s1, min_value from s1 where max_value> 3; +select next value for s1, min_value from s1 where max_value> 4; +drop sequence s1; + +--echo "Runnig SHOW BINLOG EVENTS" +--replace_column 1 # 2 # 5 # +--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Server.ver.*/VERSIONS/ +--disable_query_log +--eval SHOW BINLOG EVENTS FROM $binlog_start; +--enable_query_log +RESET MASTER; diff --git a/mysql-test/suite/sql_sequence/create.result b/mysql-test/suite/sql_sequence/create.result new file mode 100644 index 00000000000..70f9745d409 --- /dev/null +++ b/mysql-test/suite/sql_sequence/create.result @@ -0,0 +1,497 @@ +drop table if exists t1; +Warnings: +Note 1051 Unknown table 'test.t1' +create or replace sequence t1 engine=myisam; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=innodb; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=InnoDB SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=maria; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=Aria +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=Aria SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=archive; +ERROR HY000: Table storage engine 'ARCHIVE' does not support the create option 'SEQUENCE' +show create table t1; +ERROR 42S02: Table 'test.t1' doesn't exist +create or replace sequence t1 start with 10; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 1 9223372036854775806 10 1 1000 0 0 +create or replace sequence t1 minvalue=11; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 11 minvalue 11 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +11 11 9223372036854775806 11 1 1000 0 0 +create or replace sequence t1 maxvalue=13 increment by -1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 13 minvalue -9223372036854775807 maxvalue 13 increment by -1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +13 -9223372036854775807 13 13 -1 1000 0 0 +create or replace sequence t1 increment by -1 cache 100; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with -1 minvalue -9223372036854775807 maxvalue -1 increment by -1 cache 100 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +-1 -9223372036854775807 -1 -1 -1 100 0 0 +create or replace sequence t1 cycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 1 0 +create or replace sequence t1 nocycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +create or replace sequence t1 cycle minvalue= 14; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 14 minvalue 14 maxvalue 9223372036854775806 increment by 1 cache 1000 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +14 14 9223372036854775806 14 1 1000 1 0 +create or replace sequence t1 cycle increment by -1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with -1 minvalue -9223372036854775807 maxvalue -1 increment by -1 cache 1000 cycle ENGINE=MyISAM +drop sequence t1; +create sequence if not exists t1; +create sequence if not exists t1 start with 10; +Warnings: +Note 1050 Table 't1' already exists +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +create or replace sequence t1 start with 10 minvalue=10 maxvalue=11 nocache cycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue 10 maxvalue 11 increment by 1 nocache cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 10 11 10 1 0 1 0 +create or replace sequence t1 start with 10 minvalue=-10 maxvalue=11 cache=10 cycle increment by 10; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue -10 maxvalue 11 increment by 10 cache 10 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 -10 11 10 10 10 1 0 +create or replace sequence t1 start with 10 NO MAXVALUE NO MINVALUE; +create or replace sequence t1 start with 10 maxvalue 10; +create or replace sequence t1 start with 10 minvalue 10; +create or replace sequence t1 start with 10 minvalue 10 maxvalue 11 cycle; +create or replace sequence t1 start with 10 maxvalue=9223372036854775806; +create or replace sequence t1 start with 10 minvalue=-9223372036854775807; +drop sequence if exists t1; +create sequence t1 increment by 0; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 0 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 0 1000 0 0 +drop sequence t1; +create table t1 (a int); +show create sequence t1; +ERROR HY000: 'test.t1' is not SEQUENCE +drop sequence t1; +ERROR 42S02: 'test.t1' is not a SEQUENCE +drop sequence if exists t1; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +create sequence t1 start with 10 maxvalue=9; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 minvalue= 100 maxvalue=10; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 start with 9 minvalue=10; +ERROR HY000: Sequence 'test.t1' values are conflicting +create or replace sequence t1 maxvalue=13, increment by -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ' increment by -1' at line 1 +create or replace sequence t1 start with= 10 maxvalue=13; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '= 10 maxvalue=13' at line 1 +create or replace sequence t1 maxvalue=13, increment= -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ' increment= -1' at line 1 +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NO MINVALUE' at line 1 +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NO MINVALUE' at line 1 +create sequence t1 start with 10 maxvalue=9223372036854775807; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 start with 10 minvalue=-9223372036854775808; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '9223372036854775808' at line 1 +create or replace sequence t1 start with 10 NO MINVALUE minvalue=1; +drop sequence t1; +create sequence t1; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +flush tables; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +create or replace sequence t1 engine=aria; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +flush tables; +create or replace sequence t1 comment= "test 1"; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM COMMENT='test 1' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 COMMENT='test 1' +create or replace sequence t1 comment= "test 2" min_rows=1 max_rows=2; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM COMMENT='test 2' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM MIN_ROWS=1 MAX_ROWS=2 SEQUENCE=1 COMMENT='test 2' +create or replace sequence t1 start=1 increment= 2; +create or replace sequence t1 start 1 increment 2; +drop sequence t1; +CREATE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) ENGINE=MyISAM SEQUENCE=1 +drop sequence t1; +CREATE OR REPLACE TABLE t1 ( +`next_val` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_val) +CREATE OR REPLACE TABLE t1 ( +`next_value` int(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_value) +CREATE OR REPLACE TABLE t1 ( +`next_val` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` bigint(21) unsigned NOT NULL, /* error */ +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_val) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21), +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_value) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL, +extra_field bigint(21) +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (Wrong number of columns) +CREATE OR REPLACE TABLE t1 ( +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`next_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (min_value) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL, +key key1 (next_value) +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (Sequence tables cannot have any keys) +drop sequence if exists t1; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +create sequence t1; +create sequence t2; +create table t3 (a int) engine=myisam; +select table_catalog, table_schema, table_name, table_type from information_schema.tables where table_catalog="test"; +table_catalog table_schema table_name table_type +CREATE SEQUENCE s1; +drop sequence s1; +drop sequence if exists t1,t2,t3,t4; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t3' +Note 4066 Unknown SEQUENCE: 'test.t4' +drop table if exists t1,t2,t3; +Warnings: +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table t1,t2,s1; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table if exists t1,t2,s1,s2; +Warnings: +Note 1051 Unknown table 'test.s2' +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence t1,t2,s1,s2; +ERROR 42S02: Unknown SEQUENCE: 'test.t1,test.t2,test.s2' +drop table if exists t1,t2; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence if exists t1,t2,s1,s2; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +Note 4066 Unknown SEQUENCE: 'test.t2' +Note 4066 Unknown SEQUENCE: 'test.s2' +drop table if exists t1,t2; +CREATE TEMPORARY SEQUENCE s1; +DROP SEQUENCE s1; +DROP TEMPORARY SEQUENCE s1; +ERROR 42S02: Unknown SEQUENCE: 'test.s1' +CREATE TEMPORARY SEQUENCE s1; +CREATE SEQUENCE s2; +CREATE TEMPORARY TABLE t1 (a int); +CREATE TABLE t2 (a int); +DROP TEMPORARY SEQUENCE t1,t2,s1,s2; +ERROR 42S02: Unknown SEQUENCE: 'test.t1,test.t2,test.s2' +DROP TEMPORARY SEQUENCE s1; +ERROR 42S02: Unknown SEQUENCE: 'test.s1' +DROP TEMPORARY TABLE t1; +DROP TABLE t1,t2,s1,s2; +ERROR 42S02: Unknown table 'test.t1,test.s1' +create view v1 as (select 1); +CREATE SEQUENCE s1; +DROP SEQUENCE s1,v1; +ERROR 42S02: 'test.v1' is a view +drop view v1; +CREATE TEMPORARY SEQUENCE t1; +select next value for t1; +next value for t1 +1 +drop temporary table t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +100 +select previous value for t1; +previous value for t1 +100 +drop temporary sequence t1; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +CREATE TEMPORARY SEQUENCE t1 engine=innodb; +select next value for t1; +next value for t1 +1 +drop temporary table t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10 engine=innodb; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10 engine=innodb; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +100 +select previous value for t1; +previous value for t1 +100 +drop temporary sequence t1; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +create table t1 (a int) engine=sql_sequence; +ERROR 42000: Unknown storage engine 'sql_sequence' diff --git a/mysql-test/suite/sql_sequence/create.test b/mysql-test/suite/sql_sequence/create.test new file mode 100644 index 00000000000..6cb6dedd91b --- /dev/null +++ b/mysql-test/suite/sql_sequence/create.test @@ -0,0 +1,374 @@ +# +# Test create options with sequences +# +--source include/have_innodb.inc +--source include/have_archive.inc + +drop table if exists t1; + +# +# Check some sample engines +# + +create or replace sequence t1 engine=myisam; +show create sequence t1; +show create table t1; +select * from t1; +create or replace sequence t1 engine=innodb; +show create sequence t1; +show create table t1; +select * from t1; +create or replace sequence t1 engine=maria; +show create sequence t1; +show create table t1; +select * from t1; +--error ER_ILLEGAL_HA_CREATE_OPTION +create or replace sequence t1 engine=archive; +# +# The following error should be fixed. We shouldn't delete old table on errors +# +--error ER_NO_SUCH_TABLE +show create table t1; + + +# Check start values +create or replace sequence t1 start with 10; +show create sequence t1; +select * from t1; +create or replace sequence t1 minvalue=11; +show create sequence t1; +select * from t1; +create or replace sequence t1 maxvalue=13 increment by -1; +show create sequence t1; +select * from t1; + +create or replace sequence t1 increment by -1 cache 100; +show create sequence t1; +select * from t1; +create or replace sequence t1 cycle; +show create sequence t1; +select * from t1; +create or replace sequence t1 nocycle; +show create sequence t1; +select * from t1; +show create sequence t1; +create or replace sequence t1 cycle minvalue= 14; +show create sequence t1; +select * from t1; +create or replace sequence t1 cycle increment by -1; +show create sequence t1; + +drop sequence t1; +create sequence if not exists t1; +create sequence if not exists t1 start with 10; +select * from t1; +show create sequence t1; + +create or replace sequence t1 start with 10 minvalue=10 maxvalue=11 nocache cycle; +show create sequence t1; +select * from t1; +create or replace sequence t1 start with 10 minvalue=-10 maxvalue=11 cache=10 cycle increment by 10; +show create sequence t1; +select * from t1; + +# NO MINVALUE, NO MAXVALUE +create or replace sequence t1 start with 10 NO MAXVALUE NO MINVALUE; + +# Some edge cases +create or replace sequence t1 start with 10 maxvalue 10; +create or replace sequence t1 start with 10 minvalue 10; +create or replace sequence t1 start with 10 minvalue 10 maxvalue 11 cycle; +create or replace sequence t1 start with 10 maxvalue=9223372036854775806; +create or replace sequence t1 start with 10 minvalue=-9223372036854775807; +drop sequence if exists t1; + +create sequence t1 increment by 0; +show create sequence t1; +select * from t1; +drop sequence t1; + +# +# Wrong usage and arguments to create sequence +# + +create table t1 (a int); +--error ER_WRONG_OBJECT +show create sequence t1; +--error ER_NOT_SEQUENCE2 +drop sequence t1; +drop sequence if exists t1; + +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 10 maxvalue=9; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 minvalue= 100 maxvalue=10; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 9 minvalue=10; +--error ER_PARSE_ERROR +create or replace sequence t1 maxvalue=13, increment by -1; +--error ER_PARSE_ERROR +create or replace sequence t1 start with= 10 maxvalue=13; +--error ER_PARSE_ERROR +create or replace sequence t1 maxvalue=13, increment= -1; +--error ER_PARSE_ERROR +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +--error ER_PARSE_ERROR +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 10 maxvalue=9223372036854775807; +--error ER_PARSE_ERROR +create sequence t1 start with 10 minvalue=-9223372036854775808; + +# This should probably give an error +create or replace sequence t1 start with 10 NO MINVALUE minvalue=1; +drop sequence t1; + +# +# Test with LIST COLUMNS as first command +# +create sequence t1; +show fields from t1; +flush tables; +show fields from t1; +create or replace sequence t1 engine=aria; +show fields from t1; +show fields from t1; +flush tables; + +# +# Test with other create options +# + +create or replace sequence t1 comment= "test 1"; +show create sequence t1; +show create table t1; +create or replace sequence t1 comment= "test 2" min_rows=1 max_rows=2; +show create sequence t1; +show create table t1; +create or replace sequence t1 start=1 increment= 2; +create or replace sequence t1 start 1 increment 2; +drop sequence t1; + +# +# test with create table +# + +CREATE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +show create sequence t1; +show create table t1; +drop sequence t1; + +# Wrong column name + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_val` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# Wrong type + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` int(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_val` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` bigint(21) unsigned NOT NULL, /* error */ + `round` bigint(21) NOT NULL +) sequence=1; + + +# Missing NOT NULL + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21), + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# Extra field + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL, + extra_field bigint(21) +) sequence=1; + +# Wrong field order + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `next_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# key + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL, + key key1 (next_value) +) sequence=1; + +drop sequence if exists t1; + +# +# DROP SEQUENCE +# + +create sequence t1; +create sequence t2; +create table t3 (a int) engine=myisam; +select table_catalog, table_schema, table_name, table_type from information_schema.tables where table_catalog="test"; + +CREATE SEQUENCE s1; +drop sequence s1; + +drop sequence if exists t1,t2,t3,t4; +drop table if exists t1,t2,t3; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table t1,t2,s1; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table if exists t1,t2,s1,s2; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +--error ER_UNKNOWN_SEQUENCES +drop sequence t1,t2,s1,s2; +drop table if exists t1,t2; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence if exists t1,t2,s1,s2; +drop table if exists t1,t2; + +CREATE TEMPORARY SEQUENCE s1; +DROP SEQUENCE s1; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE s1; + +CREATE TEMPORARY SEQUENCE s1; +CREATE SEQUENCE s2; +CREATE TEMPORARY TABLE t1 (a int); +CREATE TABLE t2 (a int); +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE t1,t2,s1,s2; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE s1; +DROP TEMPORARY TABLE t1; +--error ER_BAD_TABLE_ERROR +DROP TABLE t1,t2,s1,s2; + +create view v1 as (select 1); +CREATE SEQUENCE s1; +--error ER_IT_IS_A_VIEW +DROP SEQUENCE s1,v1; +drop view v1; + +# +# CREATE TEMPORARY SEQUENCE +# + +CREATE TEMPORARY SEQUENCE t1; +select next value for t1; +drop temporary table t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10; +select next value for t1; +select previous value for t1; +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop temporary sequence t1; +select previous value for t1; +drop sequence t1; + +CREATE TEMPORARY SEQUENCE t1 engine=innodb; +select next value for t1; +drop temporary table t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10 engine=innodb; +select next value for t1; +select previous value for t1; +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10 engine=innodb; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop temporary sequence t1; +select previous value for t1; +drop sequence t1; + +# +# Check that we can't create anything with the sequence engine +# + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t1 (a int) engine=sql_sequence; diff --git a/mysql-test/suite/sql_sequence/disabled.def b/mysql-test/suite/sql_sequence/disabled.def new file mode 100644 index 00000000000..507617dd75d --- /dev/null +++ b/mysql-test/suite/sql_sequence/disabled.def @@ -0,0 +1,2 @@ +gtid : Disabled until Monty has time to check the result +replication : Disabled until Monty has time to check the result diff --git a/mysql-test/suite/sql_sequence/gtid-master.opt b/mysql-test/suite/sql_sequence/gtid-master.opt new file mode 100644 index 00000000000..dd4fb8c5f9a --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid-master.opt @@ -0,0 +1,3 @@ +--binlog_format=row +--query_cache_type=1 +--log-slave-updates diff --git a/mysql-test/suite/sql_sequence/gtid-slave.opt b/mysql-test/suite/sql_sequence/gtid-slave.opt new file mode 100644 index 00000000000..dc0ff1864e0 --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid-slave.opt @@ -0,0 +1,4 @@ +--binlog_format=row +--query_cache_type=1 +--read_only=true +--log-slave-updates diff --git a/mysql-test/suite/sql_sequence/gtid.result b/mysql-test/suite/sql_sequence/gtid.result new file mode 100644 index 00000000000..f9a2d8ab60a --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid.result @@ -0,0 +1,722 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; +set global read_only=on; +########################################### +master and slave sync sequence. +########################################### +use s_db; +create sequence s1; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +drop sequence s1; +########################################### +not support create table engine=sequence. +########################################### +create table t(id int)engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +create table t(id int)engine=innodb; +alter table t engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop table t; +########################################### +not support alter sequence table. +########################################### +create sequence s2; +alter table s2 add id int; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +alter table s2 add index ind_x(start); +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop sequence s2; +########################################### +not support create temproary sequence. +########################################### +create temporary sequence s2; +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 'sequence s2' at line 1 +########################################### +all invalid sequence value +########################################### +use s_db; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +cycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +nocache +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 5 +maxvalue 100000 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 5 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 0 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +########################################### +global read lock prevent query sequence +########################################### +use s_db; +create sequence s_db.s1; +flush table with read lock; +select * for s1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop sequence s_db.s1; +########################################### +session setting +########################################### +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +set session sequence_read_skip_cache=false; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +4 +drop sequence s1; +########################################### +query cache test +########################################### +use s_db; +show global variables like 'query_cache_type'; +Variable_name Value +query_cache_type ON +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +show status like 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 0 +########################################### +priv test +########################################### +create sequence s_db.s1; +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +create sequence s_db.s2; +drop sequence s_db.s2; +select * for s_db.s1; +ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' +create sequence s_db.s2; +ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' +drop sequence s_db.s1; +########################################### +run out sequence value +########################################### +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +commit; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +drop sequence s_t; +drop table t; +########################################### +read_only prevent query sequence +########################################### +create sequence s_db.s1; +show global variables like 'read_only'; +Variable_name Value +read_only OFF +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +show global variables like 'read_only'; +Variable_name Value +read_only ON +select * for s_db.s1; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +drop sequence s_db.s1; +########################################### +update based table +########################################### +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; +use s_db; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 20 1 1 5 1 0 +select nextval for s_t; +nextval +1 +select nextval from s_t; +nextval +7 +------------------------------------------ +master update nextval; +------------------------------------------ +select nextval for s_t; +nextval +2 +update s_t set nextval= 11; +commit; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +------------------------------------------ +show slave nextval; +------------------------------------------ +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +set session sequence_read_skip_cache=off; +select * for s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 17 1 20 1 1 5 1 0 +------------------------------------------ +update into invalid sequence +------------------------------------------ +select nextval for s_t; +nextval +12 +update s_t set nextval= 11,start=10, minvalue=11; +commit; +create table t_1(id int); +insert into t_1 value(1111); +select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +insert into t_1 select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +commit; +select * from t_1; +id +1111 +------------------------------------------ +delete sequence row +------------------------------------------ +delete from s_t; +commit; +select nextval for s_t; +nextval +drop sequence s_t; +drop table t_1; +########################################### +test transaction context (innodb) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +select * from t_1; +id +1111 +1 +2 +2222 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +test transaction context (myisam) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +close binlog +########################################### +use s_db; +create sequence s1 cache 2; +select nextval for s1; +nextval +1 +select nextval for s1; +nextval +2 +select nextval for s1; +nextval +3 +select nextval for s1; +nextval +4 +commit; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +------------------------------------------ +close session binlog. +------------------------------------------ +set session sql_log_bin=off; +select nextval for s1; +nextval +5 +select nextval for s1; +nextval +6 +select nextval for s1; +nextval +7 +select nextval for s1; +nextval +8 +set session sql_log_bin=on; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 10 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +drop sequence s1; +########################################### +statement binlog +########################################### +------------------------------------------ +set binlog_format=statement +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format STATEMENT +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +########################################### +test savepoint +########################################### +set session sequence_read_skip_cache=off; +set session binlog_format=row; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 values(2222); +select * from t1; +id +1111 +1 +2 +3 +2222 +rollback to sp1; +select * from t1; +id +1111 +select nextval for s1; +nextval +4 +commit; +drop sequence s1; +drop table t1; +########################################### +test proc +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create table t(id int)engine=innodb; +create procedure p1() +begin +create sequence s1 cache 2; +end// +create procedure p2() +begin +insert into t select nextval for s1; +commit; +end// +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); +select * from t; +id +1 +2 +3 +4 +use s_db; +select * from t; +id +1 +2 +3 +4 +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; +########################################### +test trigger +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; +CREATE TRIGGER tri_1 +before INSERT ON t2 FOR EACH ROW +BEGIN +INSERT INTO t1 select nextval for s1; +END// +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +select * from t2; +id +1111 +1111 +1111 +1111 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t2; +id +select * from t1; +id +select nextval for s1; +nextval +5 +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; +########################################### +test function +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +CREATE function f1() returns int +BEGIN +INSERT INTO t1 select nextval for s1; +return (1); +END// +begin; +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t1; +id +select nextval for s1; +nextval +5 +drop function f1; +drop table t1; +drop sequence s1; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; +include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/gtid.test b/mysql-test/suite/sql_sequence/gtid.test new file mode 100644 index 00000000000..30717a4e53c --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid.test @@ -0,0 +1,660 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/have_innodb.inc + +connection master; +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; + +--sync_slave_with_master + +connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT); +connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT); + +connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT); +connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT); + +connection slave; +set global read_only=on; + +--echo ########################################### +--echo master and slave sync sequence. +--echo ########################################### +connection master; +use s_db; + +create sequence s1; +show create table s1; + +--sync_slave_with_master +connection slave; +use s_db; +show create table s1; + +connection master; +use s_db; + +drop sequence s1; + +--echo ########################################### +--echo not support create table engine=sequence. +--echo ########################################### +connection master; + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t(id int)engine=sequence; + +create table t(id int)engine=innodb; + +--error ER_UNKNOWN_STORAGE_ENGINE +alter table t engine=sequence; + +drop table t; +--echo ########################################### +--echo not support alter sequence table. +--echo ########################################### +connection master; + +create sequence s2; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add id int; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add index ind_x(start); +drop sequence s2; + +--echo ########################################### +--echo Support create temporary sequence. +--echo ########################################### +connection master; + +create temporary sequence s2; +drop temporary sequence s2; + +--echo ########################################### +--echo all invalid sequence value +--echo ########################################### + +connection master; +use s_db; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + cycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + nocycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + nocache + nocycle; +drop sequence s2; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 0 + nocache + nocycle; + +--echo ########################################### +--echo global read lock prevent query sequence +--echo ########################################### +connection master; +use s_db; +create sequence s_db.s1; +flush table with read lock; +--error ER_CANT_UPDATE_WITH_READLOCK +select next value for s1; + +unlock tables; + +drop sequence s_db.s1; + +--echo ########################################### +--echo query cache test +--echo ########################################### +connection master; +use s_db; +show global variables like 'query_cache_type'; + +show status like 'Qcache_hits'; +show status like 'Qcache_inserts'; + +--echo ########################################### +--echo priv test +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +select next value for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select next value for s_db.s1; +--error ER_TABLEACCESS_DENIED_ERROR +create sequence s_db.s2; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo run out sequence value +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +commit; +select * from t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t; + +connection m_normal_1; +use s_db; +drop sequence s_t; +drop table t; + +--echo ########################################### +--echo read_only prevent query sequence +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +show global variables like 'read_only'; +select next value for s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select next value for s_db.s1; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo update based table +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from s_t; + + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +update s_t set next_value= 11; +commit; + +select * from s_t; + +connection master; +--sync_slave_with_master + +--echo ------------------------------------------ +--echo show slave nextval; +--echo ------------------------------------------ +connection s_normal_3; +select * from s_t; + +connection m_normal_1; +select next value for s_t; +select * from s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + + +--echo ------------------------------------------ +--echo update into invalid sequence +--echo ------------------------------------------ +connection m_normal_1; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set next_value= 11,start=10, min_value=11; +commit; + +create table t_1(id int); +insert into t_1 value(1111); +select next value for s_t; +insert into t_1 select next value for s_t; +commit; + +select * from t_1; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +--error ER_ILLEGAL_HA +delete from s_t; +commit; + +select next value for s_t; + +connection m_normal_1; +drop sequence s_t; +drop table t_1; + +--echo ########################################### +--echo test transaction context (innodb) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo test transaction context (myisam) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo close binlog +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +commit; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +--echo ------------------------------------------ +--echo close session binlog. +--echo ------------------------------------------ +connection master; +set session sql_log_bin=off; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +set session sql_log_bin=on; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo statement binlog +--echo ########################################### +--echo ------------------------------------------ +--echo set binlog_format=statement +--echo ------------------------------------------ +connection master; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_BINLOG_STMT_MODE_AND_ROW_ENGINE +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +set session binlog_format=row; + +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; + +insert into t1 values(2222); + +select * from t1; +rollback to sp1; +select * from t1; +select next value for s1; + +commit; + +drop sequence s1; +drop table t1; + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +use s_db; +create table t(id int)engine=innodb; + +delimiter //; + +create procedure p1() +begin + create sequence s1 cache 2; +end// + +create procedure p2() +begin + insert into t select next value for s1; + commit; +end// + +delimiter ;// + +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); + +select * from t; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from t; + +connection m_normal_1; +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; + +--echo ########################################### +--echo test trigger +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select next value for s1; +END// +delimiter ;// + +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); + +select * from t2; +select * from t1; +rollback; +select * from t2; +select * from t1; + +select next value for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +delimiter //; +CREATE function f1() returns int +BEGIN + INSERT INTO t1 select next value for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select next value for s1; + +drop function f1; +drop table t1; +drop sequence s1; + +connection master; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; + + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/next.result b/mysql-test/suite/sql_sequence/next.result new file mode 100644 index 00000000000..fc28152a2b7 --- /dev/null +++ b/mysql-test/suite/sql_sequence/next.result @@ -0,0 +1,404 @@ +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 2 cycle; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +3 0 +select next value for t1; +next value for t1 +2 +select next_value,round from t1; +next_value round +3 0 +select next value for t1; +next value for t1 +3 +select next_value,round from t1; +next_value round +5 0 +select next value for t1; +next value for t1 +4 +select next_value,round from t1; +next_value round +5 0 +select next value for t1; +next value for t1 +5 +select next_value,round from t1; +next_value round +7 0 +select next value for t1; +next value for t1 +6 +select next_value,round from t1; +next_value round +7 0 +select next value for t1; +next value for t1 +7 +select next_value,round from t1; +next_value round +9 0 +select next value for t1; +next value for t1 +8 +select next_value,round from t1; +next_value round +9 0 +select next value for t1; +next value for t1 +9 +select next_value,round from t1; +next_value round +11 0 +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +11 0 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +3 1 +select NEXT VALUE for t1,seq from seq_1_to_20; +NEXT VALUE for t1 seq +2 1 +3 2 +4 3 +5 4 +6 5 +7 6 +8 7 +9 8 +10 9 +1 10 +2 11 +3 12 +4 13 +5 14 +6 15 +7 16 +8 17 +9 18 +10 19 +1 20 +drop sequence t1; +CREATE SEQUENCE t1 minvalue 1 maxvalue 10 increment by -1 cache 2 cycle engine=aria; +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +8 0 +select next value for t1; +next value for t1 +9 +select next_value,round from t1; +next_value round +8 0 +select next value for t1; +next value for t1 +8 +select next_value,round from t1; +next_value round +6 0 +select next value for t1; +next value for t1 +7 +select next_value,round from t1; +next_value round +6 0 +select next value for t1; +next value for t1 +6 +select next_value,round from t1; +next_value round +4 0 +select next value for t1; +next value for t1 +5 +select next_value,round from t1; +next_value round +4 0 +select next value for t1; +next value for t1 +4 +select next_value,round from t1; +next_value round +2 0 +select next value for t1; +next value for t1 +3 +select next_value,round from t1; +next_value round +2 0 +select next value for t1; +next value for t1 +2 +select next_value,round from t1; +next_value round +0 0 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +0 0 +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +8 1 +select NEXT VALUE for t1,seq from seq_1_to_20; +NEXT VALUE for t1 seq +9 1 +8 2 +7 3 +6 4 +5 5 +4 6 +3 7 +2 8 +1 9 +10 10 +9 11 +8 12 +7 13 +6 14 +5 15 +4 16 +3 17 +2 18 +1 19 +10 20 +drop sequence t1; +CREATE SEQUENCE t1 start with 8 minvalue 1 maxvalue 10 increment by 1 cache 2 nocycle; +select next value for t1; +next value for t1 +8 +select next value for t1; +next value for t1 +9 +select next value for t1; +next value for t1 +10 +select previous value for t1; +previous value for t1 +10 +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +drop sequence t1; +create sequence s1 start with 1 cache 2 maxvalue 5; +select next value for s1; +next value for s1 +1 +select next value for s1; +next value for s1 +2 +select next value for s1; +next value for s1 +3 +select next value for s1; +next value for s1 +4 +select next value for s1; +next value for s1 +5 +select next value for s1; +ERROR HY000: Sequence 'test.s1' has run out +drop sequence s1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 100 increment by 1 cache 10; +select next value for t1; +next value for t1 +1 +select * from t1; +next_value min_value max_value start increment cache cycle round +11 1 100 1 1 10 0 0 +flush tables; +select next value for t1; +next value for t1 +11 +select nextval(t1); +nextval(t1) +12 +drop sequence t1; +CREATE SEQUENCE t9 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t9; +previous value for t9 +NULL +select next value for t9; +next value for t9 +1 +select previous value for t9, lastval(t9); +previous value for t9 lastval(t9) +1 1 +select next value for t9; +next value for t9 +2 +select previous value for t9, lastval(t9); +previous value for t9 lastval(t9) +2 2 +select seq, previous value for t9, NEXT VALUE for t9, previous value for t9 from seq_1_to_20; +seq previous value for t9 NEXT VALUE for t9 previous value for t9 +1 2 3 3 +2 3 4 4 +3 4 5 5 +4 5 6 6 +5 6 7 7 +6 7 8 8 +7 8 9 9 +8 9 10 10 +9 10 1 1 +10 1 2 2 +11 2 3 3 +12 3 4 4 +13 4 5 5 +14 5 6 6 +15 6 7 7 +16 7 8 8 +17 8 9 9 +18 9 10 10 +19 10 1 1 +20 1 2 2 +select * from t9; +next_value min_value max_value start increment cache cycle round +6 1 10 1 1 5 1 2 +drop sequence t9; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +flush tables; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +5 +select previous value for t1; +previous value for t1 +5 +drop sequence t1; +CREATE or replace SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 +INCREMENT BY 1 START WITH 3984356 CACHE 20 CYCLE engine=innodb; +show create table s1; +Table Create Table +s1 CREATE TABLE `s1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=InnoDB SEQUENCE=1 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984356 1 9999999999 3984356 1 20 1 0 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984356 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984357 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984358 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984376 1 9999999999 3984356 1 20 1 0 +FLUSH TABLES; +select * from s1; +next_value min_value max_value start increment cache cycle round +3984376 1 9999999999 3984356 1 20 1 0 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984376 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984396 1 9999999999 3984356 1 20 1 0 +drop sequence s1; +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +explain select next value for t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used +explain select next value for t1, min_value from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 +drop table t1; +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int); +insert into t1 values (next value for s1); +insert into t1 values (next value for s1); +select * from t1; +a +1 +2 +drop table t1,s1; +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int primary key auto_increment, b int default 0) engine=myisam; +insert into t1 values (),(),(),(),(),(),(); +update t1 set b= next value for s1 where a <= 3; +select * from t1; +a b +1 1 +2 2 +3 3 +4 0 +5 0 +6 0 +7 0 +drop table t1,s1; +CREATE OR REPLACE SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 3984356 nocache CYCLE engine='innodb'; +select * from s1; +next_value min_value max_value start increment cache cycle round +3984356 1 9999999999 3984356 1 0 1 0 +select next value for s1; +next value for s1 +3984356 +drop sequence s1; +create table t1 (a int); +select next value for t1; +ERROR 42S02: 'test.t1' is not a SEQUENCE +drop table t1; +create sequence t1; +select next value for t1; +next value for t1 +1 +select next value for t1, min_value; +ERROR 42S22: Unknown column 'min_value' in 'field list' +drop sequence t1; diff --git a/mysql-test/suite/sql_sequence/next.test b/mysql-test/suite/sql_sequence/next.test new file mode 100644 index 00000000000..426ee5709a1 --- /dev/null +++ b/mysql-test/suite/sql_sequence/next.test @@ -0,0 +1,201 @@ +--source include/have_sequence.inc +--source include/have_innodb.inc + +# +# Test sequence generation +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 2 cycle; +show create table t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; + +select NEXT VALUE for t1,seq from seq_1_to_20; + +drop sequence t1; + +CREATE SEQUENCE t1 minvalue 1 maxvalue 10 increment by -1 cache 2 cycle engine=aria; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; + +select NEXT VALUE for t1,seq from seq_1_to_20; + +drop sequence t1; + +CREATE SEQUENCE t1 start with 8 minvalue 1 maxvalue 10 increment by 1 cache 2 nocycle; +select next value for t1; +select next value for t1; +select next value for t1; +select previous value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +select previous value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +drop sequence t1; + +create sequence s1 start with 1 cache 2 maxvalue 5; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +drop sequence s1; + +# +# Test that flush tables jumps to next next_value +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 100 increment by 1 cache 10; +select next value for t1; +select * from t1; +flush tables; +select next value for t1; +select nextval(t1); +drop sequence t1; + +# +# Test currval/previous +# + +CREATE SEQUENCE t9 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t9; +select next value for t9; +select previous value for t9, lastval(t9); +select next value for t9; +select previous value for t9, lastval(t9); +select seq, previous value for t9, NEXT VALUE for t9, previous value for t9 from seq_1_to_20; +select * from t9; +drop sequence t9; + +# +# Check what happens when one refers to a sequence that has been closed/deleted +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select next value for t1; +select previous value for t1; +flush tables; +select previous value for t1; +drop sequence t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop sequence t1; + +# This failed in an early build + +CREATE or replace SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 +INCREMENT BY 1 START WITH 3984356 CACHE 20 CYCLE engine=innodb; +show create table s1; +select * from s1; +select NEXT VALUE FOR s1; +select NEXT VALUE FOR s1; +select NEXT VALUE FOR s1; +select * from s1; +FLUSH TABLES; +select * from s1; +select NEXT VALUE FOR s1; +select * from s1; +drop sequence s1; + +# +# Explain +# + +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +explain select next value for t1; +explain select next value for t1, min_value from t1; +drop table t1; + +# +# Using insert with NEXT VALUE +# + +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int); +insert into t1 values (next value for s1); +insert into t1 values (next value for s1); +select * from t1; +drop table t1,s1; + +# +# Using update with NEXT VALUE +# + +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int primary key auto_increment, b int default 0) engine=myisam; +insert into t1 values (),(),(),(),(),(),(); +update t1 set b= next value for s1 where a <= 3; +select * from t1; +drop table t1,s1; + +# +# NO CACHE and InnoDB +# + +CREATE OR REPLACE SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 3984356 nocache CYCLE engine='innodb'; +select * from s1; +select next value for s1; +drop sequence s1; + + +# +# Some error testing +# + +create table t1 (a int); +--error ER_NOT_SEQUENCE +select next value for t1; +drop table t1; + +create sequence t1; +select next value for t1; +--error ER_BAD_FIELD_ERROR +select next value for t1, min_value; +drop sequence t1; diff --git a/mysql-test/suite/sql_sequence/read_only.result b/mysql-test/suite/sql_sequence/read_only.result new file mode 100644 index 00000000000..3f6a95610dd --- /dev/null +++ b/mysql-test/suite/sql_sequence/read_only.result @@ -0,0 +1,25 @@ +create sequence s1 cache 2 engine=innodb; +connection default; +show global variables like 'innodb_read_only'; +Variable_name Value +innodb_read_only ON +use test; +set session binlog_format= row; +########################################### +read_only create error. +########################################### +show global variables like 'innodb_read_only'; +Variable_name Value +innodb_read_only ON +use test; +create sequence s2 cache 5 engine=innodb; +ERROR HY000: Can't create table `test`.`s2` (errno: 165 "Table is read only") +########################################### +read_only query error. +########################################### +select next value for s1; +ERROR HY000: Table 's1' is read only +select next value for s1; +ERROR HY000: Table 's1' is read only +select next value for s1; +ERROR HY000: Table 's1' is read only diff --git a/mysql-test/suite/sql_sequence/read_only.test b/mysql-test/suite/sql_sequence/read_only.test new file mode 100644 index 00000000000..d8743617ad2 --- /dev/null +++ b/mysql-test/suite/sql_sequence/read_only.test @@ -0,0 +1,39 @@ +--source include/have_innodb.inc + +# +# Test innodb read only +# + +create sequence s1 cache 2 engine=innodb; + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server 10 +--source include/wait_until_disconnected.inc +--enable_reconnect +--exec echo "restart":--innodb_read_only=1 > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +connection default; +show global variables like 'innodb_read_only'; +use test; +set session binlog_format= row; + +--echo ########################################### +--echo read_only create error. +--echo ########################################### + +show global variables like 'innodb_read_only'; +use test; + +--error ER_CANT_CREATE_TABLE +create sequence s2 cache 5 engine=innodb; + +--echo ########################################### +--echo read_only query error. +--echo ########################################### +--error ER_OPEN_AS_READONLY +select next value for s1; +--error ER_OPEN_AS_READONLY +select next value for s1; +--error ER_OPEN_AS_READONLY +select next value for s1; diff --git a/mysql-test/suite/sql_sequence/replication-master.opt b/mysql-test/suite/sql_sequence/replication-master.opt new file mode 100644 index 00000000000..bbea8eabc91 --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication-master.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 diff --git a/mysql-test/suite/sql_sequence/replication-slave.opt b/mysql-test/suite/sql_sequence/replication-slave.opt new file mode 100644 index 00000000000..a4e068e4b43 --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication-slave.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 --read_only=true diff --git a/mysql-test/suite/sql_sequence/replication.result b/mysql-test/suite/sql_sequence/replication.result new file mode 100644 index 00000000000..eed4c130a9b --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication.result @@ -0,0 +1,1030 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; +set global read_only=on; +########################################### +master and slave sync sequence. +########################################### +use s_db; +create sequence s1; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +drop sequence s1; +########################################### +not support create table engine=sequence. +########################################### +create table t(id int)engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +create table t(id int)engine=innodb; +alter table t engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop table t; +########################################### +not support alter sequence table. +########################################### +create sequence s2; +alter table s2 add id int; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +alter table s2 add index ind_x(start); +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop sequence s2; +########################################### +support create sequence +########################################### +create table t_1(id int); +show create sequence t_1; +ERROR HY000: 's_db.t_1' is not SEQUENCE +drop table t_1; +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +insert into s2 values(0, 0, 1, 10, 1, 2, 1, 1, 0); +commit; +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 9 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 10 1 2 1 1 1 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 10 1 2 1 1 1 +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 1 +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 1 +drop sequence s2; +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`rrval` bigint(21) NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=myisam DEFAULT CHARSET=latin1; +show create sequence s2; +Table Create Table +s2 CREATE SEQUENCE `s2` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +drop sequence s2; +########################################### +select sequence syntax test +########################################### +create sequence s2; +create table t2 (id int); +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select * from t2; +id +insert into t2 select nextval for s2; +commit; +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 9223372036854775807 1 1 10000 0 0 +select * for t2; +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 +select * from s2, t2; +currval nextval minvalue maxvalue start increment cache cycle round id +0 10002 1 9223372036854775807 1 1 10000 0 0 1 +select * for s2, t2; +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 ' t2' at line 1 +drop sequence s2; +drop table t2; +########################################### +support rename, not support truncate +########################################### +create sequence s2; +alter table s2 rename to s2_1; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +rename table s2 to s2_1; +select * for s2_1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +truncate table s2_1; +ERROR HY000: Table storage engine for 's2_1' doesn't have this option +rename table s2_1 to s2; +drop sequence s2; +########################################### +not support create temproary sequence. +########################################### +create temporary sequence s2; +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 'sequence s2' at line 1 +########################################### +all invalid sequence value +########################################### +use s_db; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +cycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +nocache +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 5 +maxvalue 100000 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 5 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 0 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +########################################### +global read lock prevent query sequence +########################################### +use s_db; +create sequence s_db.s1; +flush table with read lock; +select * for s1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop sequence s_db.s1; +########################################### +session setting +########################################### +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +set session sequence_read_skip_cache=false; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +4 +drop sequence s1; +########################################### +query cache test +########################################### +use s_db; +show global variables like 'query_cache_type'; +Variable_name Value +query_cache_type ON +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +show status like 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 1 +########################################### +priv test +########################################### +create sequence s_db.s1; +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +create sequence s_db.s2; +drop sequence s_db.s2; +select * for s_db.s1; +ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' +create sequence s_db.s2; +ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' +drop sequence s_db.s1; +########################################### +run out sequence value +########################################### +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +commit; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +drop sequence s_t; +drop table t; +########################################### +read_only prevent query sequence +########################################### +create sequence s_db.s1; +show global variables like 'read_only'; +Variable_name Value +read_only OFF +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +show global variables like 'read_only'; +Variable_name Value +read_only ON +select * for s_db.s1; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +drop sequence s_db.s1; +########################################### +update based table +########################################### +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; +use s_db; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 20 1 1 5 1 0 +select nextval for s_t; +nextval +1 +select nextval from s_t; +nextval +7 +------------------------------------------ +master update nextval; +------------------------------------------ +select nextval for s_t; +nextval +2 +update s_t set nextval= 11; +commit; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +------------------------------------------ +show slave nextval; +------------------------------------------ +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +set session sequence_read_skip_cache=off; +select * for s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 17 1 20 1 1 5 1 0 +------------------------------------------ +update into invalid sequence +------------------------------------------ +select nextval for s_t; +nextval +12 +update s_t set nextval= 11,start=10, minvalue=11; +commit; +create table t_1(id int); +insert into t_1 value(1111); +select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +insert into t_1 select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +commit; +select * from t_1; +id +1111 +------------------------------------------ +delete sequence row +------------------------------------------ +delete from s_t; +commit; +select nextval for s_t; +nextval +drop sequence s_t; +drop table t_1; +########################################### +test transaction context (innodb) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +select * from t_1; +id +1111 +1 +2 +2222 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +test transaction context (myisam) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +close binlog +########################################### +use s_db; +create sequence s1 cache 2; +select nextval for s1; +nextval +1 +select nextval for s1; +nextval +2 +select nextval for s1; +nextval +3 +select nextval for s1; +nextval +4 +commit; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +------------------------------------------ +close session binlog. +------------------------------------------ +set session sql_log_bin=off; +select nextval for s1; +nextval +5 +select nextval for s1; +nextval +6 +select nextval for s1; +nextval +7 +select nextval for s1; +nextval +8 +set session sql_log_bin=on; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 10 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +drop sequence s1; +########################################### +statement binlog +########################################### +------------------------------------------ +set binlog_format=statement +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format STATEMENT +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +------------------------------------------ +set binlog_format=mixed +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=mixed; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format MIXED +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +########################################### +test savepoint +########################################### +set session sequence_read_skip_cache=off; +set session binlog_format=row; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 values(2222); +select * from t1; +id +1111 +1 +2 +3 +2222 +rollback to sp1; +select * from t1; +id +1111 +select nextval for s1; +nextval +4 +commit; +drop sequence s1; +drop table t1; +########################################### +create as +########################################### +set session sequence_read_skip_cache=off; +create sequence s1 cache 2; +create table t as select * for s1; +select * from t; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +drop sequence s1; +drop table t; +########################################### +test proc +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create table t(id int)engine=innodb; +create procedure p1() +begin +create sequence s1 cache 2; +end// +create procedure p2() +begin +insert into t select nextval for s1; +commit; +end// +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); +select * from t; +id +1 +2 +3 +4 +use s_db; +select * from t; +id +1 +2 +3 +4 +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; +########################################### +test trigger +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; +CREATE TRIGGER tri_1 +before INSERT ON t2 FOR EACH ROW +BEGIN +INSERT INTO t1 select nextval for s1; +END// +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +select * from t2; +id +1111 +1111 +1111 +1111 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t2; +id +select * from t1; +id +select nextval for s1; +nextval +5 +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; +########################################### +test function +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +CREATE function f1() returns int +BEGIN +INSERT INTO t1 select nextval for s1; +return (1); +END// +begin; +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t1; +id +select nextval for s1; +nextval +5 +drop function f1; +drop table t1; +drop sequence s1; +########################################### +test value boundary +########################################### +use s_db; +------------------------------------------ +round increment by round +------------------------------------------ +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 1 cycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 6 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 2 7 5 1 1 1 1 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 2 7 5 1 1 1 1 +drop sequence s1; +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 10 nocycle; +select nextval, round for s1; +nextval round +5 0 +select nextval, round for s1; +nextval round +6 0 +select nextval, round for s1; +nextval round +7 0 +select nextval, round for s1; +ERROR HY000: Sequence 's_db.s1' has been run out. +drop sequence s1; +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 nocache cycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 3 2 3 0 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 1 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 3 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 4 +drop sequence s1; +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 cache 2 nocycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 3 2 3 2 0 0 +select * for s1; +ERROR HY000: Sequence 's_db.s1' has been run out. +drop sequence s1; +------------------------------------------ +beyond ulonglong maxvalue +------------------------------------------ +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 1 cycle; +select nextval, round for s1; +nextval round +9223372036854775805 0 +select nextval, round for s1; +nextval round +9223372036854775806 0 +select nextval, round for s1; +nextval round +9223372036854775807 0 +select nextval, round for s1; +nextval round +9223372036854775804 1 +select nextval, round for s1; +nextval round +9223372036854775805 1 +select nextval, round for s1; +nextval round +9223372036854775806 1 +select nextval, round for s1; +nextval round +9223372036854775807 1 +select nextval, round for s1; +nextval round +9223372036854775804 2 +select nextval, round for s1; +nextval round +9223372036854775805 2 +select nextval, round for s1; +nextval round +9223372036854775806 2 +drop sequence s1; +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 10 cycle; +select nextval, round for s1; +nextval round +9223372036854775805 0 +select nextval, round for s1; +nextval round +9223372036854775806 0 +select nextval, round for s1; +nextval round +9223372036854775807 0 +select nextval, round for s1; +nextval round +9223372036854775804 1 +select nextval, round for s1; +nextval round +9223372036854775805 1 +select nextval, round for s1; +nextval round +9223372036854775806 1 +select nextval, round for s1; +nextval round +9223372036854775807 1 +select nextval, round for s1; +nextval round +9223372036854775804 2 +select nextval, round for s1; +nextval round +9223372036854775805 2 +select nextval, round for s1; +nextval round +9223372036854775806 2 +drop sequence s1; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; +include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/replication.test b/mysql-test/suite/sql_sequence/replication.test new file mode 100644 index 00000000000..bf7b6bbdbf7 --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication.test @@ -0,0 +1,881 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/have_innodb.inc + +# +# This test is originally sequence.test from Jianwei modified for MariaDB +# To test basic sequence functionallity together with replication +# + +connection master; +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; + +--sync_slave_with_master + +connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT); +connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT); + +connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT); +connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT); + +connection slave; +set global read_only=on; + +--echo ########################################### +--echo master and slave sync sequence. +--echo ########################################### +connection master; +use s_db; + +create sequence s1; +show create table s1; + +--sync_slave_with_master +connection slave; +use s_db; +show create table s1; + +connection master; +use s_db; + +drop sequence s1; + +--echo ########################################### +--echo not support create table engine=sequence. +--echo ########################################### +connection master; + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t(id int)engine=sequence; + +create table t(id int)engine=innodb; + +--error ER_UNKNOWN_STORAGE_ENGINE +alter table t engine=sequence; + +drop table t; +--echo ########################################### +--echo not support alter sequence table. +--echo ########################################### +connection master; + +create sequence s2; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add id int; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add index ind_x(start); +drop sequence s2; + +--echo ########################################### +--echo support create sequence +--echo ########################################### +connection master; + +create table t_1(id int); +--error ER_WRONG_OBJECT +show create sequence t_1; + +drop table t_1; + +--error ER_PARSE_ERROR +CREATE SEQUENCE `s2` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB sequence=1; + +CREATE TABLE `s2` ( + `next_value` bigint(21) NOT NULL COMMENT 'next value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB sequence=1; + +insert into s2 values(0, 1, 10, 1, 2, 1, 1, 0); +commit; +--error ER_PARSE_ERROR +select * for s2; +select NEXT VALUE for s2; +select NEXT VALUE for s2; +select NEXT VALUE for s2; +select * from s2; +commit; + +connection master; +--sync_slave_with_master +select * from s2; + +connection slave; +select * from s2; + + +connection master; +drop sequence s2; + +CREATE TABLE `s2` ( + `next_value` bigint(21) NOT NULL COMMENT 'next value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=myisam DEFAULT CHARSET=latin1 sequence=1; + +show create sequence s2; +drop sequence s2; + +--echo ########################################### +--echo select sequence syntax test +--echo ########################################### +connection master; +create sequence s2; +create table t2 (id int); + +select * from s2; +select * from t2; +insert into t2 select next value for s2; +commit; + +select NEXT VALUE for s2; +--error ER_NOT_SEQUENCE +select NEXT VALUE for t2; + +select * from s2, t2; + +--error ER_PARSE_ERROR +select * for s2; +--error ER_PARSE_ERROR +select * for s2, t2; + +connection master; +drop sequence s2; +drop table t2; + +--echo ########################################### +--echo support rename, not support truncate +--echo ########################################### +connection master; + +create sequence s2; + +alter table s2 rename to s2_1; +rename table s2_1 to s2_2; +show create sequence s2_2; +select * from s2_2; + +--error ER_ILLEGAL_HA +truncate table s2_2; +rename table s2_2 to s2; +drop sequence s2; + +--echo ########################################### +--echo all invalid sequence value +--echo ########################################### + +connection master; +use s_db; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + cycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + nocycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + nocache + nocycle; +drop sequence s2; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 0 + nocache + nocycle; + +--echo ########################################### +--echo global read lock prevent query sequence +--echo ########################################### +connection master; +use s_db; +create sequence s_db.s1; +flush table with read lock; +--error ER_CANT_UPDATE_WITH_READLOCK +select NEXT VALUE for s1; +unlock tables; +drop sequence s_db.s1; + +--echo ########################################### +--echo query cache test +--echo ########################################### +connection master; +use s_db; +flush status; +show global variables like 'query_cache_type'; + +show status like 'Qcache_hits'; +show status like 'Qcache_inserts'; + +--echo ########################################### +--echo priv test +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +select NEXT VALUE for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select NEXT VALUE for s_db.s1; +--error ER_TABLEACCESS_DENIED_ERROR +create sequence s_db.s2; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo run out sequence value +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +commit; +select * from t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t; + +connection m_normal_1; +use s_db; +drop sequence s_t; +drop table t; + +--echo ########################################### +--echo read_only prevent query sequence +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +show global variables like 'read_only'; +select * from s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select next value for s_db.s1; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo update based table +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle engine=innodb; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from s_t; + + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select next_value from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +update s_t set next_value= 11; +commit; + +select * from s_t; +SELECT NEXT VALUE for s_t; + +connection master; +--sync_slave_with_master + +--echo ------------------------------------------ +--echo show slave nextval; +--echo ------------------------------------------ +connection s_normal_3; +select * from s_t; + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + + +--echo ------------------------------------------ +--echo update into invalid sequence +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set min_value=11, max_value=9; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set next_value= 12, start=10, min_value=11, max_value=20; +select * from s_t; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +--error ER_ILLEGAL_HA +delete from s_t; +commit; + +select next value for s_t; + +connection m_normal_1; +drop sequence s_t; + +--echo ########################################### +--echo test transaction context (innodb) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5 engine=innodb; + +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; +select * from s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select * from s_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo test transaction context (myisam) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo close binlog +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +commit; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +--echo ------------------------------------------ +--echo close session binlog. +--echo ------------------------------------------ +connection master; +set session sql_log_bin=off; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +set session sql_log_bin=on; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo statement binlog +--echo ########################################### +--echo ------------------------------------------ +--echo set binlog_format=statement +--echo ------------------------------------------ +connection master; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_BINLOG_STMT_MODE_AND_ROW_ENGINE +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ------------------------------------------ +--echo set binlog_format=mixed +--echo ------------------------------------------ +connection master; +set session binlog_format=mixed; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +--sync_slave_with_master +set session binlog_format=row; + +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; + +insert into t1 values(2222); + +select * from t1; +rollback to sp1; +select * from t1; +select next value for s1; + +commit; + +drop sequence s1; +drop table t1; + +connection master; +--sync_slave_with_master + +--echo ########################################### +--echo create as +--echo ########################################### +connection m_normal_1; + +create sequence s1 cache 2; +create table t as select next value for s1; +select * from t; + +drop sequence s1; +drop table t; + +connection master; +--sync_slave_with_master + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +use s_db; +create table t(id int)engine=innodb; + +delimiter //; + +create procedure p1() +begin + create sequence s1 cache 2; +end// + +create procedure p2() +begin + insert into t select next value for s1; + commit; +end// + +delimiter ;// + +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); + +select * from t; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from t; + +connection m_normal_1; +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; + +--echo ########################################### +--echo test trigger +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select next value for s1; +END// +delimiter ;// + +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); + +select * from t2; +select * from t1; +rollback; +select * from t2; +select * from t1; + +select next value for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +delimiter //; +CREATE function f1() returns int +BEGIN + INSERT INTO t1 select next value for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select next value for s1; + +drop function f1; +drop table t1; +drop sequence s1; + +--echo ########################################### +--echo test value boundary +--echo ########################################### +connection m_normal_1; +use s_db; + +--echo ------------------------------------------ +--echo round increment by round +--echo ------------------------------------------ +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 1 cycle; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +drop sequence s1; + +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 10 nocycle; +select next value for s1; +select next value for s1; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +select * from s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 nocache cycle; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 cache 2 nocycle; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +drop sequence s1; + +--echo ------------------------------------------ +--echo beyond ulonglong maxvalue +--echo ------------------------------------------ +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775806 cache 1 cycle; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +drop sequence s1; + +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775806 cache 10 cycle; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +drop sequence s1; + +connection master; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 052d30563b2..f93f340b4fc 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3063,9 +3063,9 @@ READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME PERFORMANCE_SCHEMA_MAX_STATEMENT_CLASSES SESSION_VALUE NULL -GLOBAL_VALUE 187 +GLOBAL_VALUE 189 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 187 +DEFAULT_VALUE 189 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Maximum number of statement instruments. diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 42a813b0782..c3a25c6bcce 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -25,7 +25,7 @@ eval CREATE FUNCTION myfunc_nonexist RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval CREATE FUNCTION sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; +eval CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB eval CREATE FUNCTION lookup RETURNS STRING SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB @@ -238,7 +238,7 @@ DROP FUNCTION myfunc_double; --error ER_SP_DOES_NOT_EXIST DROP FUNCTION myfunc_nonexist; DROP FUNCTION myfunc_int; -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP FUNCTION lookup; DROP FUNCTION reverse_lookup; DROP FUNCTION avgcost; @@ -401,19 +401,19 @@ DROP TABLE const_len_bug; # --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval CREATE FUNCTION sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; +eval CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; CREATE TABLE t1 (a INT); CREATE TABLE t2 (a INT PRIMARY KEY); INSERT INTO t1 VALUES (4),(3),(2),(1); INSERT INTO t2 SELECT * FROM t1; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq ASC; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq DESC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq ASC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq DESC; -SELECT * FROM t1 WHERE a = sequence(); -SELECT * FROM t2 WHERE a = sequence(); +SELECT * FROM t1 WHERE a = udf_sequence(); +SELECT * FROM t2 WHERE a = udf_sequence(); -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP TABLE t1,t2; # diff --git a/mysql-test/t/udf_notembedded.test b/mysql-test/t/udf_notembedded.test index bf54af7256c..d6658604005 100644 --- a/mysql-test/t/udf_notembedded.test +++ b/mysql-test/t/udf_notembedded.test @@ -5,10 +5,10 @@ # MDEV-8644 Using a UDF in a virtual column causes a crash when stopping the server # --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval create function sequence returns integer soname "$UDF_EXAMPLE_SO"; -create table t1 (n int key not null auto_increment, msg int as (sequence()) virtual); +eval create function udf_sequence returns integer soname "$UDF_EXAMPLE_SO"; +create table t1 (n int key not null auto_increment, msg int as (udf_sequence()) virtual); select * from t1; source include/restart_mysqld.inc; drop table t1; -drop function sequence; +drop function udf_sequence; diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c index 9916650308a..b8d9442fbc9 100644 --- a/mysys/safemalloc.c +++ b/mysys/safemalloc.c @@ -293,7 +293,7 @@ static void warn(const char *format,...) { void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; int frames= backtrace(frame, array_elements(frame)); - fprintf(stderr, " "); + fprintf(stderr, " at "); if (frames < SF_REMEMBER_FRAMES + SF_FRAMES_SKIP) frame[frames]= 0; print_stack(frame + SF_FRAMES_SKIP); @@ -318,7 +318,8 @@ static int bad_ptr(const char *where, void *ptr) } if (irem->marker != MAGICSTART) { - warn("Error: %s unallocated data or underrun buffer", where); + DBUG_PRINT("error",("Unallocated data or underrun buffer %p", ptr)); + warn("Error: %s unallocated data or underrun buffer %p", ptr, where); return 1; } @@ -328,7 +329,8 @@ static int bad_ptr(const char *where, void *ptr) magicend[2] != MAGICEND2 || magicend[3] != MAGICEND3) { - warn("Error: %s overrun buffer ", where); + DBUG_PRINT("error",("Overrun buffer %p", ptr)); + warn("Error: %s overrun buffer %p", where); fprintf(stderr, "Allocated at "); print_stack(irem->frame); return 1; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 964cbd6894c..1dfa313a70c 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -144,6 +144,7 @@ SET (SQL_SOURCE sql_type.cc sql_type.h item_windowfunc.cc sql_window.cc sql_cte.cc sql_cte.h + sql_sequence.cc sql_sequence.h ha_sequence.h ${WSREP_SOURCES} table_cache.cc encryption.cc temporary_tables.cc ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc @@ -166,6 +167,8 @@ ENDIF() MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY RECOMPILE_FOR_EMBEDDED) +MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONLY +RECOMPILE_FOR_EMBEDDED) ADD_LIBRARY(sql STATIC ${SQL_SOURCE}) ADD_DEPENDENCIES(sql GenServerSource) diff --git a/sql/create_options.cc b/sql/create_options.cc index 99562faa077..e6d86860afd 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -320,8 +320,8 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, *current* value of the underlying sysvar. 2. But only if the underlying sysvar value is different from the sysvar's default. - 3. If it's ALTER TABLE and the sysvar option was not explicitly - mentioned - do nothing, do not add it to the list. + 3. If it's ALTER TABLE or CREATE_SEQUENCE and the sysvar option was + not explicitly mentioned - do nothing, do not add it to the list. 4. But if it was ALTER TABLE with sysvar option = DEFAULT, we add it to the list (under the same condition #2). 5. If we're here parsing the option list from the .frm file @@ -329,7 +329,6 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, do not add it to the list (makes no sense anyway) and use the *default* value of the underlying sysvar. Because sysvar value can change, but it should not affect existing tables. - This is how it's implemented: the current sysvar value is added to the list if suppress_warning is FALSE (meaning a table is created, that is CREATE TABLE or ALTER TABLE) and it's actually a CREATE TABLE diff --git a/sql/datadict.cc b/sql/datadict.cc index 103a33214ae..1093f2cdd09 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -18,6 +18,7 @@ #include "sql_priv.h" #include "sql_class.h" #include "sql_table.h" +#include "ha_sequence.h" static int read_string(File file, uchar**to, size_t length) { @@ -46,33 +47,40 @@ static int read_string(File file, uchar**to, size_t length) engine_name is a LEX_STRING, where engine_name->str must point to a buffer of at least NAME_CHAR_LEN+1 bytes. - @retval FRMTYPE_ERROR error - @retval FRMTYPE_TABLE table - @retval FRMTYPE_VIEW view + @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise + + @retval TABLE_TYPE_UNKNOWN error + @retval TABLE_TYPE_TABLE table + @retval TABLE_TYPE_SEQUENCE sequence table + @retval TABLE_TYPE_VIEW view */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) +Table_type dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name, + bool *is_sequence) { File file; - uchar header[10]; //"TYPE=VIEW\n" it is 10 characters + uchar header[40]; //"TYPE=VIEW\n" it is 10 characters size_t error; - frm_type_enum type= FRMTYPE_ERROR; + Table_type type= TABLE_TYPE_UNKNOWN; uchar dbt; DBUG_ENTER("dd_frm_type"); - if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0) - DBUG_RETURN(FRMTYPE_ERROR); + *is_sequence= 0; + + if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) + < 0) + DBUG_RETURN(TABLE_TYPE_UNKNOWN); error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)); if (error) goto err; - if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header))) + if (!strncmp((char*) header, "TYPE=VIEW\n", 10)) { - type= FRMTYPE_VIEW; + type= TABLE_TYPE_VIEW; goto err; } - type= FRMTYPE_TABLE; + type= TABLE_TYPE_NORMAL; if (!is_binary_frm_header(header) || !engine_name) goto err; @@ -91,6 +99,9 @@ frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) } } + if (((header[39] >> 4) & 3) == HA_CHOICE_YES) + *is_sequence= 1; + /* read the true engine name */ { MY_STAT state; diff --git a/sql/datadict.h b/sql/datadict.h index 9b180a882f9..46dac394f07 100644 --- a/sql/datadict.h +++ b/sql/datadict.h @@ -1,6 +1,7 @@ #ifndef DATADICT_INCLUDED #define DATADICT_INCLUDED -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, Oracle and/or its affiliates. + Copyright (c) 2017 MariaDB corporation. 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 @@ -21,11 +22,12 @@ Data dictionary API. */ -enum frm_type_enum +enum Table_type { - FRMTYPE_ERROR= 0, - FRMTYPE_TABLE, - FRMTYPE_VIEW + TABLE_TYPE_UNKNOWN, + TABLE_TYPE_NORMAL, /* Normal table */ + TABLE_TYPE_SEQUENCE, + TABLE_TYPE_VIEW }; /* @@ -35,11 +37,14 @@ enum frm_type_enum Prefer to use ha_table_exists() instead. To check whether it's an frm of a view, use dd_frm_is_view(). */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name); + +enum Table_type dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name, + bool *is_sequence); static inline bool dd_frm_is_view(THD *thd, char *path) { - return dd_frm_type(thd, path, NULL) == FRMTYPE_VIEW; + bool not_used2; + return dd_frm_type(thd, path, NULL, ¬_used2) == TABLE_TYPE_VIEW; } bool dd_recreate_table(THD *thd, const char *db, const char *table_name, diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7f156c80205..b27de9dad4d 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -77,7 +77,9 @@ HA_DUPLICATE_POS | \ HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED | \ - HA_READ_BEFORE_WRITE_REMOVAL) + HA_READ_BEFORE_WRITE_REMOVAL |\ + HA_CAN_TABLES_WITHOUT_ROLLBACK) + static const char *ha_par_ext= ".par"; /**************************************************************************** diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 70cd3760783..70ec4ae8edb 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -429,6 +429,7 @@ public: virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to, enum thr_lock_type lock_type); virtual int external_lock(THD * thd, int lock_type); + LEX_STRING *engine_name() { return hton_name(table->part_info->default_engine_type); } /* When table is locked a statement is started by calling start_stmt instead of external_lock diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc new file mode 100644 index 00000000000..f3c4d8961a8 --- /dev/null +++ b/sql/ha_sequence.cc @@ -0,0 +1,417 @@ +/* + Copyright (c) 2017, Aliyun and/or its affiliates. + Copyright (c) 2017, MariaDB corporation + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include <my_global.h> +#include "sql_list.h" +#include "table.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_plugin.h" +#include "mysql/plugin.h" +#include "sql_priv.h" +#include "sql_parse.h" +#include "sql_table.h" +#include "sql_update.h" +#include "sql_base.h" +#include "log_event.h" + +/* + Table flags we should inherit and disable from the original engine. + We add HA_STATS_RECORDS_IS_EXACT as ha_sequence::info() will ensure + that records is always 1 +*/ + +#define SEQUENCE_ENABLED_TABLE_FLAGS (HA_STATS_RECORDS_IS_EXACT | \ + HA_PERSISTENT_TABLE) +#define SEQUENCE_DISABLED_TABLE_FLAGS (HA_CAN_SQL_HANDLER | \ + HA_CAN_INSERT_DELAYED | \ + HA_BINLOG_STMT_CAPABLE) +handlerton *sql_sequence_hton; + +/* + Create a sequence handler +*/ + +ha_sequence::ha_sequence(handlerton *hton, TABLE_SHARE *share) + :handler(hton, share), sequence_locked(0) +{ + sequence= share->sequence; + DBUG_ASSERT(share->sequence); +} + +/** + Destructor method must remove the underlying handler +*/ +ha_sequence::~ha_sequence() +{ + delete file; +} + +/** + Sequence table open method + + @param name Path to file (dbname and tablename) + @param mode mode + @param flags Flags how to open file + + RETURN VALUES + @retval 0 Success + @retval != 0 Failure +*/ + +int ha_sequence::open(const char *name, int mode, uint flags) +{ + int error; + DBUG_ENTER("ha_sequence::open"); + DBUG_ASSERT(table->s == table_share && file); + + file->table= table; + if (!(error= file->open(name, mode, flags))) + { + /* + Copy values set by handler::open() in the underlying handler + Reuse original storage engine data for duplicate key reference + It would be easier to do this if we would have another handler + call: fixup_after_open()... + */ + ref= file->ref; + ref_length= file->ref_length; + dup_ref= file->dup_ref; + + /* + ha_open() sets the following for us. We have to set this for the + underlying handler + */ + file->cached_table_flags= file->table_flags(); + + file->reset_statistics(); + internal_tmp_table= file->internal_tmp_table= + MY_TEST(flags & HA_OPEN_INTERNAL_TABLE); + reset_statistics(); + + /* Don't try to read the inital row the call is part of create code */ + if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR))) + { + if ((error= table->s->sequence->read_initial_values(table))) + file->ha_close(); + } + } + DBUG_RETURN(error); +} + +/* + Clone the sequence. Needed if table is used by range optimization + (Very, very unlikely) +*/ + +handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root) +{ + ha_sequence *new_handler; + DBUG_ENTER("ha_sequence::clone"); + if (!(new_handler= new (mem_root) ha_sequence(ht, table_share))) + DBUG_RETURN(NULL); + + /* + Allocate new_handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + goto err; + + if (new_handler->ha_open(table, name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL)) + goto err; + + /* Reuse original storage engine data for duplicate key reference */ + new_handler->ref= file->ref; + new_handler->ref_length= file->ref_length; + new_handler->dup_ref= file->dup_ref; + + DBUG_RETURN((handler*) new_handler); + +err: + delete new_handler; + DBUG_RETURN(NULL); +} + + +/* + Map the create table to the original storage engine +*/ + +int ha_sequence::create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) +{ + DBUG_ASSERT(create_info->sequence); + /* Sequence tables has one and only one row */ + create_info->max_rows= create_info->min_rows= 1; + return (file->create(name, form, create_info)); +} + +/** + Sequence write row method. + + A sequence table has only one row. Any inserts in the table + will update this row. + + @retval 0 Success + @retval != 0 Failure + + NOTES: + sequence_locked is set if we are called from SEQUENCE::next_value + In this case the mutex is already locked and we should not update + the sequence with 'buf' as the sequence object is already up to date. +*/ + +int ha_sequence::write_row(uchar *buf) +{ + int error; + DBUG_ENTER("ha_sequence::write_row"); + DBUG_ASSERT(table->record[0] == buf); + + row_already_logged= 0; + if (!sequence->initialized) + { + /* This calls is from ha_open() as part of create table */ + DBUG_RETURN(file->write_row(buf)); + } + + /* + User tries to write a row + - Check that row is an accurate object + - Update the first row in the table + */ + + sequence_definition tmp_seq; + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust()) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + + /* + Lock sequence to ensure that no one can come in between + while sequence, table and binary log are updated. + */ + if (!sequence_locked) // If not from next_value() + sequence->lock(); + + if (!(error= file->update_first_row(buf))) + { + Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; + if (!sequence_locked) + sequence->copy(&tmp_seq); + rows_changed++; + /* We have to do the logging while we hold the sequence mutex */ + error= binlog_log_row(table, 0, buf, log_func); + row_already_logged= 1; + } + + sequence->all_values_used= 0; + if (!sequence_locked) + sequence->unlock(); + DBUG_RETURN(error); +} + + +int ha_sequence::update_row(const uchar *old_data, uchar *new_data) +{ + int error; + sequence_definition tmp_seq; + DBUG_ENTER("ha_sequence::update_row"); + DBUG_ASSERT(new_data == table->record[0]); + + row_already_logged= 0; + + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust()) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + + /* + Lock sequence to ensure that no one can come in between + while sequence, table and binary log is updated. + */ + sequence->lock(); + if (!(error= file->update_row(old_data, new_data))) + { + sequence->copy(&tmp_seq); + rows_changed++; + /* We have to do the logging while we hold the sequence mutex */ + error= binlog_log_row(table, old_data, new_data, + Update_rows_log_event::binlog_row_logging_function); + row_already_logged= 1; + } + sequence->all_values_used= 0; + sequence->unlock(); + DBUG_RETURN(error); +} + + +/* + Inherit the sequence base table flags. +*/ + +handler::Table_flags ha_sequence::table_flags() const +{ + DBUG_ENTER("ha_sequence::table_flags"); + DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) | + SEQUENCE_ENABLED_TABLE_FLAGS); +} + + +int ha_sequence::info(uint flag) +{ + DBUG_ENTER("ha_sequence::info"); + file->info(flag); + /* Inform optimizer that we have always only one record */ + stats= file->stats; + stats.records= 1; + DBUG_RETURN(false); +} + +int ha_sequence::external_lock(THD *thd, int lock_type) +{ + int error= file->external_lock(thd, lock_type); + + /* + Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in + handler.cc when we later call it with file->ha_..() + */ + file->m_lock_type= lock_type; + return error; +} + +/* + Squence engine error deal method +*/ + +void ha_sequence::print_error(int error, myf errflag) +{ + char *sequence_db= table_share->db.str; + char *sequence_name= table_share->table_name.str; + DBUG_ENTER("ha_sequence::print_error"); + + switch (error) { + case HA_ERR_SEQUENCE_INVALID_DATA: + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db, + sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_SEQUENCE_RUN_OUT: + { + my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_WRONG_COMMAND: + my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", table_share->db.str, + table_share->table_name.str); + DBUG_VOID_RETURN; + } + file->print_error(error, errflag); + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Sequence plugin interface +*****************************************************************************/ + +/* + Create an new handler +*/ + +static handler *sequence_create_handler(handlerton *hton, + TABLE_SHARE *share, + MEM_ROOT *mem_root) +{ + DBUG_ENTER("sequence_create_handler"); + DBUG_RETURN(new (mem_root) ha_sequence(hton, share)); +} + + +/* + Sequence engine end. + + SYNOPSIS + sequence_end() + p handlerton. + type panic type. + RETURN VALUES + 0 Success + !=0 Failure +*/ +static int sequence_end(handlerton* hton, + ha_panic_function type __attribute__((unused))) +{ + DBUG_ENTER("sequence_end"); + DBUG_RETURN(0); +} + + +/* + Sequence engine init. + + SYNOPSIS + sequence_initialize() + + @param p handlerton. + + retval 0 Success + retval !=0 Failure +*/ + +static int sequence_initialize(void *p) +{ + handlerton *local_sequence_hton= (handlerton *)p; + DBUG_ENTER("sequence_initialize"); + + local_sequence_hton->state= SHOW_OPTION_YES; + local_sequence_hton->db_type= DB_TYPE_SEQUENCE; + local_sequence_hton->create= sequence_create_handler; + local_sequence_hton->panic= sequence_end; + local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE | + HTON_HIDDEN | + HTON_TEMPORARY_NOT_SUPPORTED | + HTON_ALTER_NOT_SUPPORTED | + HTON_NO_PARTITION); + DBUG_RETURN(0); +} + + +static struct st_mysql_storage_engine sequence_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +maria_declare_plugin(sql_sequence) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &sequence_storage_engine, + "SQL_SEQUENCE", + "jianwei.zhao @ Aliyun & Monty @ MariaDB corp", + "Sequence Storage Engine for CREATE SEQUENCE", + PLUGIN_LICENSE_GPL, + sequence_initialize, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100, /* 1.0 */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */ +} +maria_declare_plugin_end; diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h new file mode 100644 index 00000000000..aeb7ff3fd58 --- /dev/null +++ b/sql/ha_sequence.h @@ -0,0 +1,138 @@ +#ifndef HA_SEQUENCE_INCLUDED +#define HA_SEQUENCE_INCLUDED +/* + Copyright (c) 2017 Aliyun and/or its affiliates. + Copyright (c) 2017 MariaDB corporation + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_sequence.h" +#include "table.h" +#include "handler.h" + +extern handlerton *sql_sequence_hton; + +/* + Sequence engine handler. + + The sequence engine is a logic engine. It doesn't store any data. + All the sequence data stored into the base table which must support + non rollback writes (HA_CAN_TABLES_WITHOUT_ROLLBACK) + + The sequence data (SEQUENCE class) is stored in TABLE_SHARE->sequence + + TABLE RULES: + 1. When table is created, one row is automaticlly inserted into + the table. The table will always have one and only one row. + 2. Any inserts or updates to the table will be validated. + 3. Inserts will overwrite the original row. + 4. DELETE and TRUNCATE will not affect the table. + Instead a warning will be given. + 5. Cache will be reset for any updates. + + CACHE RULES: + SEQUENCE class is used to cache values that sequence defined. + 1. If hit cache, we can query back the sequence nextval directly + instead of reading the underlying table. + + 2. When run out of values, the sequence engine will reserve new values + in update the base table. + + 3. The cache is invalidated if any update on based table. +*/ + +class ha_sequence :public handler +{ +private: + handler *file; + SEQUENCE *sequence; /* From table_share->sequence */ + +public: + ha_sequence(handlerton *hton, TABLE_SHARE *share); + ~ha_sequence(); + + /* virtual function that are re-implemented for sequence */ + int open(const char *name, int mode, uint test_if_locked); + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); + handler *clone(const char *name, MEM_ROOT *mem_root); + int write_row(uchar *buf); + int update_row(const uchar *old_data, uchar *new_data); + Table_flags table_flags() const; + /* One can't delete from sequence engine */ + int delete_row(const uchar *buf) + { return HA_ERR_WRONG_COMMAND; } + /* One can't delete from sequence engine */ + int truncate() + { return HA_ERR_WRONG_COMMAND; } + /* Can't use query cache */ + uint8 table_cache_type() + { return HA_CACHE_TBL_NOCACHE; } + void print_error(int error, myf errflag); + int info(uint); + LEX_STRING *engine_name() { return hton_name(file->ht); } + int external_lock(THD *thd, int lock_type); + + /* Functions that are directly mapped to the underlying handler */ + int rnd_init(bool scan) + { return file->rnd_init(scan); } + int rnd_next(uchar *buf) + { return file->rnd_next(buf); } + int rnd_end() + { return file->rnd_end(); } + int rnd_pos(uchar *buf, uchar *pos) + { return file->rnd_pos(buf, pos); } + void position(const uchar *record) + { return file->position(record); } + const char *table_type() const + { return file->table_type(); } + ulong index_flags(uint inx, uint part, bool all_parts) const + { return file->index_flags(inx, part, all_parts); } + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type) + { return file->store_lock(thd, to, lock_type); } + int close(void) + { return file->close(); } + const char **bas_ext() const + { return file->bas_ext(); } + int delete_table(const char*name) + { return file->delete_table(name); } + int rename_table(const char *from, const char *to) + { return file->rename_table(from, to); } + void unbind_psi() + { return file->unbind_psi(); } + void rebind_psi() + { return file->rebind_psi(); } + + bool auto_repair(int error) const + { return file->auto_repair(error); } + int repair(THD* thd, HA_CHECK_OPT* check_opt) + { return file->repair(thd, check_opt); } + bool check_and_repair(THD *thd) + { return file->check_and_repair(thd); } + bool is_crashed() const + { return file->is_crashed(); } + + /* New methods */ + void register_original_handler(handler *file_arg) + { + file= file_arg; + init(); /* Update cached_table_flags */ + } + + /* To inform handler that sequence is already locked by called */ + bool sequence_locked; +}; +#endif diff --git a/sql/handler.cc b/sql/handler.cc index a6016646d3c..5d0ec99e978 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -41,6 +41,7 @@ #include <mysql/psi/mysql_table.h> #include "debug_sync.h" // DEBUG_SYNC #include "sql_audit.h" +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -291,7 +292,6 @@ handler *get_ha_partition(partition_info *part_info) } #endif - static const char **handler_errmsgs; C_MODE_START @@ -618,7 +618,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin) /* This is entirely for legacy. We will create a new "disk based" hton and a "memory" hton which will be configurable longterm. We should be able to - remove partition and myisammrg. + remove partition. */ switch (hton->db_type) { case DB_TYPE_HEAP: @@ -630,6 +630,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin) case DB_TYPE_PARTITION_DB: partition_hton= hton; break; + case DB_TYPE_SEQUENCE: + sql_sequence_hton= hton; + break; default: break; }; @@ -2426,6 +2429,11 @@ err: return NULL; } +LEX_STRING *handler::engine_name() +{ + return hton_name(ht); +} + double handler::keyread_time(uint index, uint ranges, ha_rows rows) { @@ -2549,6 +2557,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, } reset_statistics(); internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE); + DBUG_RETURN(error); } @@ -2797,10 +2806,11 @@ int handler::ha_rnd_init_with_error(bool scan) /** - Read first row (only) from a table. + Read first row (only) from a table. Used for reading tables with + only one row, either based on table statistics or if table is a SEQUENCE. - This is never called for InnoDB tables, as these table types - has the HA_STATS_RECORDS_IS_EXACT set. + This is never called for normal InnoDB tables, as these table types + does not have HA_STATS_RECORDS_IS_EXACT set. */ int handler::read_first_row(uchar * buf, uint primary_key) { @@ -3992,7 +4002,7 @@ void handler::mark_trx_read_write_internal() */ if (ha_info->is_started()) { - DBUG_ASSERT(has_transactions()); + DBUG_ASSERT(has_transaction_manager()); /* table_share can be NULL in ha_delete_table(). See implementation of standalone function ha_delete_table() in sql_base.cc. @@ -5033,22 +5043,28 @@ private: loaded, frm is invalid), the return value will be true, but *hton will be NULL. */ + bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton) + handlerton **hton, bool *is_sequence) { handlerton *dummy; + bool dummy2; DBUG_ENTER("ha_table_exists"); if (hton) *hton= 0; else if (engines_with_discover) hton= &dummy; + if (!is_sequence) + is_sequence= &dummy2; + *is_sequence= 0; TDC_element *element= tdc_lock_share(thd, db, table_name); if (element && element != MY_ERRPTR) { if (hton) *hton= element->share->db_type(); + *is_sequence= element->share->table_type == TABLE_TYPE_SEQUENCE; tdc_unlock_share(element); DBUG_RETURN(TRUE); } @@ -5066,7 +5082,7 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, char engine_buf[NAME_CHAR_LEN + 1]; LEX_STRING engine= { engine_buf, 0 }; - if (dd_frm_type(thd, path, &engine) != FRMTYPE_VIEW) + if (dd_frm_type(thd, path, &engine, is_sequence) != TABLE_TYPE_VIEW) { plugin_ref p= plugin_lock_by_name(thd, &engine, MYSQL_STORAGE_ENGINE_PLUGIN); *hton= p ? plugin_hton(p) : NULL; @@ -5089,7 +5105,6 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, DBUG_RETURN(TRUE); } - if (need_full_discover_for_existence) { TABLE_LIST table; @@ -5778,8 +5793,6 @@ static int write_locked_table_maps(THD *thd) } -typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); - static int check_wsrep_max_ws_rows(); static int binlog_log_row_internal(TABLE* table, @@ -5820,10 +5833,10 @@ static int binlog_log_row_internal(TABLE* table, return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } -static inline int binlog_log_row(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func) { if (!table->file->check_table_binlog_row_based(1)) return 0; @@ -5975,7 +5988,7 @@ int handler::ha_write_row(uchar *buf) { error= write_row(buf); }) MYSQL_INSERT_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, 0, buf, log_func); @@ -6007,7 +6020,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) { error= update_row(old_data, new_data);}) MYSQL_UPDATE_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, old_data, new_data, log_func); @@ -6015,6 +6028,28 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) return error; } +/* + Update first row. Only used by sequence tables +*/ + +int handler::update_first_row(uchar *new_data) +{ + int error; + if (!(error= ha_rnd_init(1))) + { + int end_error; + if (!(error= ha_rnd_next(table->record[1]))) + error= update_row(table->record[1], new_data); + end_error= ha_rnd_end(); + if (!error) + error= end_error; + /* Logging would be wrong if update_row works but ha_rnd_end fails */ + DBUG_ASSERT(!end_error || error != 0); + } + return error; +} + + int handler::ha_delete_row(const uchar *buf) { int error; diff --git a/sql/handler.h b/sql/handler.h index 3b8829a4b83..236e39f5dde 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,9 +42,11 @@ #include <ft_global.h> #include <keycache.h> #include <mysql/psi/mysql_table.h> +#include "sql_sequence.h" class Alter_info; class Virtual_column_info; +class sequence_definition; // the following is for checking tables @@ -262,6 +264,24 @@ enum enum_alter_inplace_result { #define HA_CONCURRENT_OPTIMIZE (1ULL << 46) /* + If the storage engine support tables that will not roll back on commit + In addition the table should not lock rows and support READ and WRITE + UNCOMMITTED. + This is useful for implementing things like SEQUENCE but can also in + the future be useful to do logging that should never roll back. +*/ +#define HA_CAN_TABLES_WITHOUT_ROLLBACK (1ULL << 47) + +/* + Mainly for usage by SEQUENCE engine. Setting this flag means + that the table will never roll back and that all operations + for this table should stored in the non transactional log + space that will always be written, even on rollback. +*/ + +#define HA_PERSISTENT_TABLE (1ULL << 48) + +/* Set of all binlog flags. Currently only contain the capabilities flags. */ @@ -380,6 +400,7 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_TMP_TABLE 1U #define HA_CREATE_TMP_ALTER 8U +#define HA_LEX_CREATE_SEQUENCE 16U #define HA_MAX_REC_LENGTH 65535 @@ -434,7 +455,8 @@ enum legacy_db_type DB_TYPE_PERFORMANCE_SCHEMA=28, DB_TYPE_ARIA=42, DB_TYPE_TOKUDB=43, - DB_TYPE_FIRST_DYNAMIC=44, + DB_TYPE_SEQUENCE=44, + DB_TYPE_FIRST_DYNAMIC=45, DB_TYPE_DEFAULT=127 // Must be last }; /* @@ -522,6 +544,8 @@ given at all. */ */ #define HA_CREATE_USED_STATS_SAMPLE_PAGES (1UL << 24) +/* Create a sequence */ +#define HA_CREATE_USED_SEQUENCE (1UL << 25) /* This is master database for most of system tables. However there @@ -1684,6 +1708,7 @@ struct Table_scope_and_contents_source_st engine_option_value *option_list; ///< list of table create options enum_stats_auto_recalc stats_auto_recalc; bool varchar; ///< 1 if table has a VARCHAR + bool sequence; // If SEQUENCE=1 was used List<Virtual_column_info> *check_constraint_list; @@ -1697,6 +1722,7 @@ struct Table_scope_and_contents_source_st TABLE_LIST *pos_in_locked_tables; MDL_ticket *mdl_ticket; bool table_was_deleted; + sequence_definition *seq_create_info; void init() { @@ -2646,6 +2672,8 @@ public: bool mark_trx_read_write_done; /* mark_trx_read_write was called */ bool check_table_binlog_row_based_done; /* check_table_binlog.. was called */ bool check_table_binlog_row_based_result; /* cached check_table_binlog... */ + /* Set to 1 if handler logged last insert/update/delete operation */ + bool row_already_logged; /* TRUE <=> the engine guarantees that returned records are within the range being scanned. @@ -2748,6 +2776,7 @@ public: mark_trx_read_write_done(0), check_table_binlog_row_based_done(0), check_table_binlog_row_based_result(0), + row_already_logged(0), in_range_check_pushed_down(FALSE), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), keyread(MAX_KEY), @@ -2985,8 +3014,24 @@ public: virtual double keyread_time(uint index, uint ranges, ha_rows rows); virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } + + /* + True if changes to the table is persistent (no rollback) + This is manly used to decide how to log changes to the table in + the binary log. + */ bool has_transactions() - { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; } + { + return ((ha_table_flags() & (HA_NO_TRANSACTIONS | HA_PERSISTENT_TABLE)) + == 0); + } + /* + True if the underlaying table doesn't support transactions + */ + bool has_transaction_manager() + { + return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0); + } /** This method is used to analyse the error to see whether the error @@ -3905,7 +3950,7 @@ public: return 0; } - LEX_STRING *engine_name() { return hton_name(ht); } + virtual LEX_STRING *engine_name(); TABLE* get_table() { return table; } TABLE_SHARE* get_table_share() { return table_share; } @@ -3997,6 +4042,12 @@ private: return HA_ERR_WRONG_COMMAND; } + /* + Optimized function for updating the first row. Only used by sequence + tables + */ + virtual int update_first_row(uchar *new_data); + virtual int delete_row(const uchar *buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; @@ -4059,6 +4110,7 @@ protected: enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } friend class ha_partition; + friend class ha_sequence; public: /** This method is similar to update_row, however the handler doesn't need @@ -4289,7 +4341,7 @@ int ha_discover_table(THD *thd, TABLE_SHARE *share); int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, Discovered_table_list *result, bool reusable); bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton= 0); + handlerton **hton= 0, bool *is_sequence= 0); #endif /* key cache */ @@ -4347,6 +4399,11 @@ inline const char *table_case_name(HA_CREATE_INFO *info, const char *name) return ((lower_case_table_names == 2 && info->alias) ? info->alias : name); } +typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func); #define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \ { \ diff --git a/sql/item_func.cc b/sql/item_func.cc index 7eeb9547c89..8b52ed3c613 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -53,6 +53,7 @@ #include "sp.h" #include "set_var.h" #include "debug_sync.h" +#include "sql_base.h" #ifdef NO_EMBEDDED_ACCESS_CHECKS #define sp_restore_security_context(A,B) while (0) {} @@ -6939,3 +6940,147 @@ longlong Item_func_cursor_rowcount::val_int() sp_cursor *c= get_open_cursor_or_error(); return !(null_value= !c) ? c->row_count() : 0; } + +/***************************************************************************** + SEQUENCE functions +*****************************************************************************/ + +longlong Item_func_nextval::val_int() +{ + longlong value; + int error; + const char *key; + TABLE *table= table_list->table; + uint length= get_table_def_key(table_list, &key); + THD *thd= table->in_use; + SEQUENCE_LAST_VALUE *entry; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ASSERT(table && table->s->sequence); + DBUG_ENTER("Item_func_nextval::val_int"); + + if (table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + if (!(key= (char*) my_memdup(key, length, MYF(MY_WME))) || + !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length))) + { + /* EOM, error given */ + my_free((char*) key); + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + if (my_hash_insert(&thd->sequences, (uchar*) entry)) + { + /* EOM, error given */ + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + } + entry->null_value= null_value= 0; + value= table->s->sequence->next_value(table,0, &error); + entry->value= value; + entry->set_version(table); + + if (error) // Warning already printed + entry->null_value= null_value= 1; // For not strict mode + DBUG_RETURN(value); +} + + +/* Print for nextval and lastval */ + +void Item_func_nextval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= table_list->db, *t_name= table_list->table_name; + bool use_db_name= d_name && d_name[0]; + THD *thd= current_thd; + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, d_name, (uint)strlen(d_name)); + str->append('.'); + } + append_identifier(thd, str, t_name, (uint) strlen(t_name)); + str->append(')'); +} + + +/* Return last used value for sequence or NULL if sequence hasn't been used */ + +longlong Item_func_lastval::val_int() +{ + const char *key; + SEQUENCE_LAST_VALUE *entry; + uint length= get_table_def_key(table_list, &key); + THD *thd= table_list->table->in_use; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ENTER("Item_func_lastval::val_int"); + + if (table_list->table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + /* Sequence not used */ + null_value= 1; + DBUG_RETURN(0); + } + if (entry->check_version(table_list->table)) + { + /* Table droped and re-created, remove current version */ + my_hash_delete(&thd->sequences, (uchar*) entry); + null_value= 1; + DBUG_RETURN(0); + } + + null_value= entry->null_value; + DBUG_RETURN(entry->value); +} diff --git a/sql/item_func.h b/sql/item_func.h index 9d948a03343..0b398adb937 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2769,6 +2769,48 @@ public: }; +/* Implementation for sequences: NEXT VALUE FOR sequence and NEXTVAL() */ + +class Item_func_nextval :public Item_int_func +{ +protected: + TABLE_LIST *table_list; +public: + Item_func_nextval(THD *thd, TABLE_LIST *table): + Item_int_func(thd), table_list(table) {} + longlong val_int(); + const char *func_name() const { return "nextval"; } + void fix_length_and_dec() + { + unsigned_flag= 0; + max_length= MAX_BIGINT_WIDTH; + maybe_null= 1; /* In case of errors */ + } + bool const_item() const { return 0; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_nextval>(thd, mem_root, this); } + void print(String *str, enum_query_type query_type); + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), "()", arg, + VCOL_NON_DETERMINISTIC); + } +}; + +/* Implementation for sequences: LASTVAL(sequence), PostgreSQL style */ + +class Item_func_lastval :public Item_func_nextval +{ +public: + Item_func_lastval(THD *thd, TABLE_LIST *table): + Item_func_nextval(thd, table) {} + longlong val_int(); + const char *func_name() const { return "lastval"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_lastval>(thd, mem_root, this); } +}; + + Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component); extern bool check_reserved_words(LEX_STRING *name); diff --git a/sql/lex.h b/sql/lex.h index 9918683e60b..e67b207a75d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -160,6 +160,7 @@ static SYMBOL symbols[] = { { "CURRENT_USER", SYM(CURRENT_USER)}, { "CURSOR", SYM(CURSOR_SYM)}, { "CURSOR_NAME", SYM(CURSOR_NAME_SYM)}, + { "CYCLE", SYM(CYCLE_SYM)}, { "DATA", SYM(DATA_SYM)}, { "DATABASE", SYM(DATABASE)}, { "DATABASES", SYM(DATABASES)}, @@ -287,6 +288,7 @@ static SYMBOL symbols[] = { { "IMPORT", SYM(IMPORT)}, { "INTERSECT", SYM(INTERSECT_SYM)}, { "IN", SYM(IN_SYM)}, + { "INCREMENT", SYM(INCREMENT_SYM)}, { "INDEX", SYM(INDEX_SYM)}, { "INDEXES", SYM(INDEXES)}, { "INFILE", SYM(INFILE)}, @@ -324,6 +326,7 @@ static SYMBOL symbols[] = { { "LANGUAGE", SYM(LANGUAGE_SYM)}, { "LAST", SYM(LAST_SYM)}, { "LAST_VALUE", SYM(LAST_VALUE)}, + { "LASTVAL", SYM(LASTVAL_SYM)}, { "LEADING", SYM(LEADING)}, { "LEAVE", SYM(LEAVE_SYM)}, { "LEAVES", SYM(LEAVES)}, @@ -379,7 +382,7 @@ static SYMBOL symbols[] = { { "MAX_STATEMENT_TIME", SYM(MAX_STATEMENT_TIME_SYM)}, { "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)}, { "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)}, - { "MAXVALUE", SYM(MAX_VALUE_SYM)}, + { "MAXVALUE", SYM(MAXVALUE_SYM)}, { "MEDIUM", SYM(MEDIUM_SYM)}, { "MEDIUMBLOB", SYM(MEDIUMBLOB)}, { "MEDIUMINT", SYM(MEDIUMINT)}, @@ -393,6 +396,7 @@ static SYMBOL symbols[] = { { "MINUTE", SYM(MINUTE_SYM)}, { "MINUTE_MICROSECOND", SYM(MINUTE_MICROSECOND_SYM)}, { "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM)}, + { "MINVALUE", SYM(MINVALUE_SYM)}, { "MIN_ROWS", SYM(MIN_ROWS)}, { "MOD", SYM(MOD_SYM)}, { "MODE", SYM(MODE_SYM)}, @@ -412,7 +416,12 @@ static SYMBOL symbols[] = { { "NCHAR", SYM(NCHAR_SYM)}, { "NEW", SYM(NEW_SYM)}, { "NEXT", SYM(NEXT_SYM)}, + { "NEXTVAL", SYM(NEXTVAL_SYM)}, { "NO", SYM(NO_SYM)}, + { "NOMAXVALUE", SYM(NOMAXVALUE_SYM)}, + { "NOMINVALUE", SYM(NOMINVALUE_SYM)}, + { "NOCACHE", SYM(NOCACHE_SYM)}, + { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, @@ -466,6 +475,7 @@ static SYMBOL symbols[] = { { "PREPARE", SYM(PREPARE_SYM)}, { "PRESERVE", SYM(PRESERVE_SYM)}, { "PREV", SYM(PREV_SYM)}, + { "PREVIOUS", SYM(PREVIOUS_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, { "PROCEDURE", SYM(PROCEDURE_SYM)}, @@ -546,6 +556,7 @@ static SYMBOL symbols[] = { { "SELECT", SYM(SELECT_SYM)}, { "SENSITIVE", SYM(SENSITIVE_SYM)}, { "SEPARATOR", SYM(SEPARATOR_SYM)}, + { "SEQUENCE", SYM(SEQUENCE_SYM)}, { "SERIAL", SYM(SERIAL_SYM)}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM)}, { "SESSION", SYM(SESSION_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index a51c34365fa..12de6ae0616 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -261,7 +261,7 @@ static void track_table_access(THD *thd, TABLE **tables, size_t count) if (t) tst->add_trx_state(thd, t->reginfo.lock_type, - t->file->has_transactions()); + t->file->has_transaction_manager()); } } } @@ -439,10 +439,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) This will work even if get_lock_data fails (next unlock will free all) */ -void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count) +void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag) { MYSQL_LOCK *sql_lock= - get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD); + get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag); if (sql_lock) mysql_unlock_tables(thd, sql_lock, 0); } @@ -539,7 +539,7 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) DBUG_ASSERT(table->lock_position == i); /* Unlock the table. */ - mysql_unlock_some_tables(thd, &table, /* table count */ 1); + mysql_unlock_some_tables(thd, &table, /* table count */ 1, 0); /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; @@ -733,6 +733,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) @param flags One of: - GET_LOCK_UNLOCK : If we should send TL_IGNORE to store lock - GET_LOCK_STORE_LOCKS : Store lock info in TABLE + - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock) */ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) @@ -750,7 +751,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) TABLE *t= table_ptr[i]; if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE && - t->s->tmp_table != INTERNAL_TMP_TABLE) + t->s->tmp_table != INTERNAL_TMP_TABLE && + (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0)) { lock_count+= t->file->lock_count(); table_count++; @@ -781,7 +783,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) THR_LOCK_DATA **locks_start; table= table_ptr[i]; if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE || - table->s->tmp_table == INTERNAL_TMP_TABLE) + table->s->tmp_table == INTERNAL_TMP_TABLE || + ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence)) continue; lock_type= table->reginfo.lock_type; DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); diff --git a/sql/lock.h b/sql/lock.h index 341d7a20f9f..35cb3043d57 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -31,7 +31,7 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); -void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); +void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); @@ -47,6 +47,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type, #define GET_LOCK_STORE_LOCKS 1 #define GET_LOCK_ACTION_MASK 1 #define GET_LOCK_ON_THD (1 << 1) +#define GET_LOCK_SKIP_SEQUENCES (1 << 2) MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags); void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock); diff --git a/sql/log_event.cc b/sql/log_event.cc index a6f2de2c6c5..c7cbff9c615 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4075,10 +4075,12 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If we are using CREATE ... SELECT or if we are a slave executing BEGIN...COMMIT (generated by CREATE...SELECT) we @@ -5318,7 +5320,8 @@ compare_errors: has already been dropped. To ignore such irrelevant "table does not exist errors", we silently clear the error if TEMPORARY was used. */ - if (thd->lex->sql_command == SQLCOM_DROP_TABLE && + if ((thd->lex->sql_command == SQLCOM_DROP_TABLE || + thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) && thd->lex->tmp_table() && thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR && !expected_error) @@ -11322,7 +11325,8 @@ Annotate_rows_log_event::Annotate_rows_log_event(THD *thd, bool direct) : Log_event(thd, 0, using_trans), m_save_thd_query_txt(0), - m_save_thd_query_len(0) + m_save_thd_query_len(0), + m_used_query_txt(0) { m_query_txt= thd->query(); m_query_len= thd->query_length(); @@ -11336,7 +11340,8 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, const Format_description_log_event *desc) : Log_event(buf, desc), m_save_thd_query_txt(0), - m_save_thd_query_len(0) + m_save_thd_query_len(0), + m_used_query_txt(0) { m_query_len= event_len - desc->common_header_len; m_query_txt= (char*) buf + desc->common_header_len; @@ -11344,10 +11349,14 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, Annotate_rows_log_event::~Annotate_rows_log_event() { + DBUG_ENTER("Annotate_rows_log_event::~Annotate_rows_log_event"); #ifndef MYSQL_CLIENT if (m_save_thd_query_txt) thd->set_query(m_save_thd_query_txt, m_save_thd_query_len); + else if (m_used_query_txt) + thd->reset_query(); #endif + DBUG_VOID_RETURN; } int Annotate_rows_log_event::get_data_size() @@ -11431,6 +11440,7 @@ int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi) { m_save_thd_query_txt= thd->query(); m_save_thd_query_len= thd->query_length(); + m_used_query_txt= 1; thd->set_query(m_query_txt, m_query_len); return 0; } @@ -11982,7 +11992,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id); table_list->updating= 1; - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id)); diff --git a/sql/log_event.h b/sql/log_event.h index 2bc0a858542..e45151c8564 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3908,6 +3908,7 @@ private: uint m_query_len; char *m_save_thd_query_txt; uint m_save_thd_query_len; + bool m_used_query_txt; }; /** diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c655ffc72d1..c497a41673e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -919,7 +919,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_PARTITION_LOCK_auto_inc; PSI_mutex_key key_RELAYLOG_LOCK_index; PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, - key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; + key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry, + key_LOCK_SEQUENCE; PSI_mutex_key key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, @@ -1004,6 +1005,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_slave_state, "LOCK_slave_state", 0}, { &key_LOCK_start_thread, "LOCK_start_thread", PSI_FLAG_GLOBAL}, { &key_LOCK_binlog_state, "LOCK_binlog_state", 0}, + { &key_LOCK_SEQUENCE, "SQUENCE::LOCK_SEQUENCE", 0}, { &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0}, { &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0}, { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0} @@ -3784,6 +3786,7 @@ SHOW_VAR com_status_vars[]= { {"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)}, {"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)}, {"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)}, + {"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)}, {"create_server", STMT_STATUS(SQLCOM_CREATE_SERVER)}, {"create_table", STMT_STATUS(SQLCOM_CREATE_TABLE)}, {"create_temporary_table", COM_STATUS(com_create_tmp_table)}, @@ -3802,6 +3805,7 @@ SHOW_VAR com_status_vars[]= { {"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)}, {"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)}, {"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)}, + {"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)}, {"drop_table", STMT_STATUS(SQLCOM_DROP_TABLE)}, {"drop_temporary_table", COM_STATUS(com_drop_tmp_table)}, {"drop_trigger", STMT_STATUS(SQLCOM_DROP_TRIGGER)}, diff --git a/sql/mysqld.h b/sql/mysqld.h index 8b88c2d4ee9..9f9e9a3ae66 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -300,7 +300,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, key_rpl_group_info_sleep_lock, key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data, - key_LOCK_start_thread, + key_LOCK_start_thread, key_LOCK_SEQUENCE, key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc; extern PSI_mutex_key key_RELAYLOG_LOCK_index; extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 385a69adee3..562d0acdd19 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7466,3 +7466,21 @@ ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD eng "Row variable '%-.192s' does not have a field '%-.192s'" ER_END_IDENTIFIER_DOES_NOT_MATCH eng "END identifier '%-.192s' does not match '%-.192s'" +ER_SEQUENCE_RUN_OUT + eng "Sequence '%-.64s.%-.64s' has run out" +ER_SEQUENCE_INVALID_DATA + eng "Sequence '%-.64s.%-.64s' values are conflicting" +ER_SEQUENCE_INVALID_TABLE_STRUCTURE + eng "Sequence '%-.64s.%-.64s' table structure is invalid (%s)" +ER_SEQUENCE_ACCESS_ERROR + eng "Sequence '%-.64s.%-.64s' access error" +ER_SEQUENCE_BINLOG_FORMAT + eng "Sequences requires binlog_format mixed or row" +ER_NOT_SEQUENCE 42S02 + eng "'%-.64s.%-.64s' is not a SEQUENCE" +ER_NOT_SEQUENCE2 42S02 + eng "'%-.192s' is not a SEQUENCE" +ER_UNKNOWN_SEQUENCES 42S02 + eng "Unknown SEQUENCE: '%-.300s'" +ER_UNKNOWN_VIEW 42S02 + eng "Unknown VIEW: '%-.300s'" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a9c89aef0a8..aeaca82649f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -234,12 +234,14 @@ sp_get_flags_for_command(LEX *lex) flags= sp_head::CONTAINS_DYNAMIC_SQL; break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: if (lex->tmp_table()) flags= 0; else flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: if (lex->tmp_table()) flags= 0; else @@ -4407,7 +4409,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { SP_TABLE *tab; - if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE && + if ((lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE || + lex_for_tmp_check->sql_command == SQLCOM_DROP_SEQUENCE) && lex_for_tmp_check->tmp_table()) return TRUE; @@ -4471,7 +4474,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE)))) return FALSE; - if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE && + if ((lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE || + lex_for_tmp_check->sql_command == SQLCOM_CREATE_SEQUENCE) && lex_for_tmp_check->query_tables == table && lex_for_tmp_check->tmp_table()) { diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5e3311d502a..09781f4bbe3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6438,7 +6438,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { if (!(rights & CREATE_ACL)) { - if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0)) + if (!ha_table_exists(thd, table_list->db, table_list->table_name)) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(TRUE); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 95c2163f043..f9452077d36 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -341,16 +341,17 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table, if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION || !is_view_operator_func) { - table->required_type=FRMTYPE_TABLE; - DBUG_ASSERT(!lex->only_view); + table->required_type= TABLE_TYPE_NORMAL; + DBUG_ASSERT(lex->table_type != TABLE_TYPE_VIEW); } - else if (lex->only_view) + else if (lex->table_type == TABLE_TYPE_VIEW) { - table->required_type= FRMTYPE_VIEW; + table->required_type= lex->table_type; } - else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) + else if ((lex->table_type != TABLE_TYPE_VIEW) && + lex->sql_command == SQLCOM_REPAIR) { - table->required_type= FRMTYPE_TABLE; + table->required_type= TABLE_TYPE_NORMAL; } if (lex->sql_command == SQLCOM_CHECK || diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5a51e2b34af..5f9c6ceabc7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1934,6 +1934,11 @@ retry_share: DBUG_RETURN(true); } #endif + if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + DBUG_RETURN(true); + } table->init(thd, table_list); @@ -3650,8 +3655,9 @@ lock_table_names(THD *thd, const DDL_options_st &options, DBUG_RETURN(FALSE); /* Check if CREATE TABLE without REPLACE was used */ - create_table= thd->lex->sql_command == SQLCOM_CREATE_TABLE && - !options.or_replace(); + create_table= ((thd->lex->sql_command == SQLCOM_CREATE_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) && + !options.or_replace()); if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) { @@ -4518,7 +4524,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, /* Set requested lock type. */ table_l->lock_type= lock_type; /* Allow to open real tables only. */ - table_l->required_type= FRMTYPE_TABLE; + table_l->required_type= TABLE_TYPE_NORMAL; /* Open the table. */ if (open_and_lock_tables(thd, table_l, FALSE, flags, @@ -4574,7 +4580,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, THD_STAGE_INFO(thd, stage_opening_tables); thd->current_tablenr= 0; /* open_ltable can be used only for BASIC TABLEs */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; /* This function can't properly handle requests for such metadata locks. */ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE); @@ -5602,6 +5608,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, strcmp(db_name, table_list->db))))) DBUG_RETURN(0); + /* + Don't allow usage of fields in sequence table that is opened as part of + NEXT VALUE for sequence_name + */ + if (table_list->sequence) + DBUG_RETURN(0); + *actual_table= NULL; if (table_list->field_translation) diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index a40a064dc4b..6a491a3e7bb 100644 --- a/sql/sql_builtin.cc.in +++ b/sql/sql_builtin.cc.in @@ -11,7 +11,13 @@ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Note that sql_builtin.cc is automatically built by sql_bultin.cc.in + and cmake/plugin.cmake +*/ #include <my_global.h> #include <mysql/plugin.h> diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 93bc600deb9..abd680d749f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -101,6 +101,23 @@ extern "C" void free_user_var(user_var_entry *entry) my_free(entry); } +/* Functions for last-value-from-sequence hash */ + +extern "C" uchar *get_sequence_last_key(SEQUENCE_LAST_VALUE *entry, + size_t *length, + my_bool not_used + __attribute__((unused))) +{ + *length= entry->length; + return (uchar*) entry->key; +} + +extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry) +{ + delete entry; +} + + bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && @@ -879,6 +896,9 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_proc_cache= NULL; sp_func_cache= NULL; @@ -1428,6 +1448,9 @@ void THD::change_user(void) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, 0); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); } @@ -1484,6 +1507,7 @@ void THD::cleanup(void) #endif /* defined(ENABLED_DEBUG_SYNC) */ my_hash_free(&user_vars); + my_hash_free(&sequences); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); auto_inc_intervals_forced.empty(); @@ -5699,7 +5723,7 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg) int THD::decide_logging_format(TABLE_LIST *tables) { DBUG_ENTER("THD::decide_logging_format"); - DBUG_PRINT("info", ("Query: %s", query())); + DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query())); DBUG_PRINT("info", ("variables.binlog_format: %lu", variables.binlog_format)); DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x", @@ -5866,7 +5890,9 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (prev_write_table && prev_write_table->file->ht != table->table->file->ht) multi_write_engine= TRUE; - if (table->table->s->non_determinstic_insert) + if (table->table->s->non_determinstic_insert && + lex->sql_command != SQLCOM_CREATE_SEQUENCE && + lex->sql_command != SQLCOM_CREATE_TABLE) has_write_tables_with_unsafe_statements= true; trans= table->table->file->has_transactions(); diff --git a/sql/sql_class.h b/sql/sql_class.h index 974596910b2..52d2d12ae43 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -699,6 +699,7 @@ typedef struct system_variables ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; + my_bool sequence_read_skip_cache; ulong threadpool_priority; } SV; @@ -2215,6 +2216,8 @@ public: chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ HASH ull_hash; + /* Hash of used seqeunces (for PREVIOUS value) */ + HASH sequences; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif @@ -2419,6 +2422,8 @@ private: uint binlog_table_maps; public: void issue_unsafe_warnings(); + void reset_unsafe_warnings() + { binlog_unsafe_warning_flags= 0; } uint get_binlog_table_maps() const { return binlog_table_maps; @@ -5689,6 +5694,14 @@ public: SP Bulk execution optimized */ #define CF_SP_BULK_OPTIMIZED (1U << 20) +/** + If command creates or drops a table +*/ +#define CF_SCHEMA_CHANGE (1U << 21) +/** + If command creates or drops a database +*/ +#define CF_DB_CHANGE (1U << 22) /* Bits in server_command_flags */ diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index e33f8e443dc..875dcf0e575 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -96,6 +96,8 @@ enum enum_sql_command { SQLCOM_ALTER_USER, SQLCOM_SHOW_CREATE_USER, SQLCOM_EXECUTE_IMMEDIATE, + SQLCOM_CREATE_SEQUENCE, + SQLCOM_DROP_SEQUENCE, /* When a command is added here, be sure it's also added in mysqld.cc diff --git a/sql/sql_const.h b/sql/sql_const.h index c8e60305eab..2fa4b01b0a5 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -114,6 +114,7 @@ #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 +#define SEQUENCES_HASH_SIZE 16 #define TABLE_OPEN_CACHE_MIN 400 #define TABLE_OPEN_CACHE_DEFAULT 2000 #define TABLE_DEF_CACHE_DEFAULT 400 diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 6b29d345432..6054c45b1c3 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -902,7 +902,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, true, false))) + mysql_rm_table_no_locks(thd, tables, true, false, true, false, true, + false))) { /* We temporarily disable the binary log while dropping the objects diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4a27244f5b9..d4673605e13 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -286,7 +286,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* for now HANDLER can be used only for real TABLES */ - tables->required_type= FRMTYPE_TABLE; + tables->required_type= TABLE_TYPE_NORMAL; /* We use open_tables() here, rather than, say, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8e5dfb4f69c..f97732860ad 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4120,7 +4120,7 @@ static TABLE *create_table_from_items(THD *thd, if (!mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, alter_info, NULL, - select_field_count)) + select_field_count, create_table)) { DEBUG_SYNC(thd,"create_table_select_before_open"); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 38a6e84f509..e474a2cb313 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -750,7 +750,7 @@ void LEX::start(THD *thd_arg) in_sum_func= NULL; used_tables= 0; - only_view= FALSE; + table_type= TABLE_TYPE_UNKNOWN; reset_slave_info.all= false; limit_rows_examined= 0; limit_rows_examined_cnt= ULONGLONG_MAX; @@ -3379,6 +3379,7 @@ void LEX::set_trg_event_type_for_tables() REPLACE SELECT is handled later in this method. */ case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: new_trg_event_map|= static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)); break; @@ -6394,6 +6395,39 @@ Item *LEX::create_item_ident(THD *thd, sp_variable *spv; if (spcont && (spv= spcont->find_variable(a, false))) return create_item_spvar_row_field(thd, a, b, spv, pos_in_q, length_in_q); + + if ((thd->variables.sql_mode & MODE_ORACLE) && b.length == 7) + { + if (!my_strnncoll(system_charset_info, + (const uchar *) b.str, 7, + (const uchar *) "NEXTVAL", 7)) + { + TABLE_LIST *table; + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(a)) || + !(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + return NULL; + return new (thd->mem_root) Item_func_nextval(thd, table); + } + else if (!my_strnncoll(system_charset_info, + (const uchar *) b.str, 7, + (const uchar *) "CURRVAL", 7)) + { + TABLE_LIST *table; + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(a)) || + !(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + return NULL; + return new (thd->mem_root) Item_func_lastval(thd, table); + } + } + return create_item_ident_nospvar(thd, a, b); } @@ -6921,3 +6955,21 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } return 0; } + + +bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) +{ + uint offset; + sp_instr_cfetch *i; + + if (!spcont->find_cursor(name, &offset, false)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name.str); + return true; + } + i= new (thd->mem_root) + sp_instr_cfetch(sphead->instructions(), spcont, offset); + if (i == NULL || sphead->add_instr(i)) + return true; + return false; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6211330c2c2..e131241ea52 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -249,6 +249,7 @@ enum enum_drop_mode #define TL_OPTION_FORCE_INDEX 2 #define TL_OPTION_IGNORE_LEAVES 4 #define TL_OPTION_ALIAS 8 +#define TL_OPTION_SEQUENCE 16 typedef List<Item> List_item; typedef Mem_root_array<ORDER*, true> Group_list_ptrs; @@ -2632,6 +2633,7 @@ struct LEX: public Query_tables_list */ LEX_USER *definer; + Table_type table_type; /* Used for SHOW CREATE */ List<Key_part_spec> ref_list; List<LEX_USER> users_list; List<LEX_COLUMN> columns; @@ -2804,7 +2806,6 @@ public: Event_parse_data *event_parse_data; - bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* field_list was created for view and should be removed before PS/SP rexecuton @@ -3533,6 +3534,8 @@ public: create_info.add(options); return check_create_options(create_info); } + bool sp_add_cfetch(THD *thd, const LEX_STRING &name); + bool set_command_with_check(enum_sql_command command, uint scope, DDL_options_st options) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index efcde7950eb..161e1086bba 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -101,6 +101,7 @@ #include "set_var.h" #include "log_slow.h" #include "sql_bootstrap.h" +#include "sql_sequence.h" #include "my_json_writer.h" @@ -448,6 +449,7 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: skip= (lex->tmp_table() || (thd->variables.option_bits & OPTION_GTID_BEGIN)); break; @@ -456,6 +458,7 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) skip= (lex->tmp_table()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If CREATE TABLE of non-temporary table and the table is not part if a BEGIN GTID ... COMMIT group, do a implicit commit. @@ -542,19 +545,25 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | + CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]= (CF_CHANGES_DATA | + CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS | + CF_SCHEMA_CHANGE); sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; - sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -721,6 +730,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* We don't want to replicate DROP for temp tables in row format */ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* One can change replication mode with SET */ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; @@ -763,6 +773,7 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES; @@ -795,7 +806,9 @@ void init_update_queries(void) have to be closed before temporary tables are pre-opened. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE; @@ -813,8 +826,10 @@ void init_update_queries(void) even temporary table DDL should be disallowed. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS; @@ -1407,7 +1422,7 @@ out: This is a helper function to mysql_execute_command. - @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere. + @note SQLCOM_MULTI_UPDATE is an exception and dealt with elsewhere. @see mysql_execute_command @returns Status code @@ -1425,13 +1440,12 @@ static my_bool deny_updates_if_read_only_option(THD *thd, LEX *lex= thd->lex; - const my_bool user_is_super= - ((ulong)(thd->security_ctx->master_access & SUPER_ACL) == - (ulong)SUPER_ACL); - - if (user_is_super) + /* Super user is allowed to do changes */ + if (((ulong)(thd->security_ctx->master_access & SUPER_ACL) == + (ulong)SUPER_ACL)) DBUG_RETURN(FALSE); + /* Check if command doesn't update anything */ if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA)) DBUG_RETURN(FALSE); @@ -1439,29 +1453,17 @@ static my_bool deny_updates_if_read_only_option(THD *thd, if (lex->sql_command == SQLCOM_UPDATE_MULTI) DBUG_RETURN(FALSE); - const my_bool create_temp_tables= - (lex->sql_command == SQLCOM_CREATE_TABLE) && lex->tmp_table(); - - const my_bool drop_temp_tables= - (lex->sql_command == SQLCOM_DROP_TABLE) && lex->tmp_table(); - - const my_bool update_real_tables= - some_non_temp_table_to_be_updated(thd, all_tables) && - !(create_temp_tables || drop_temp_tables); - - - const my_bool create_or_drop_databases= - (lex->sql_command == SQLCOM_CREATE_DB) || - (lex->sql_command == SQLCOM_DROP_DB); + /* Check if we created and dropped temporary tables */ + if ((sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE) && + lex->tmp_table()) + DBUG_RETURN(FALSE); - if (update_real_tables || create_or_drop_databases) - { - /* - An attempt was made to modify one or more non-temporary tables. - */ - DBUG_RETURN(TRUE); - } + /* Check if we created or dropped databases */ + if ((sql_command_flags[lex->sql_command] & CF_DB_CHANGE)) + DBUG_RETURN(TRUE); + if (some_non_temp_table_to_be_updated(thd, all_tables)) + DBUG_RETURN(TRUE); /* Assuming that only temporary tables are modified. */ DBUG_RETURN(FALSE); @@ -3192,7 +3194,8 @@ mysql_execute_command(THD *thd) */ if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) && !(lex->sql_command == SQLCOM_SET_OPTION) && - !(lex->sql_command == SQLCOM_DROP_TABLE && + !((lex->sql_command == SQLCOM_DROP_TABLE || + lex->sql_command == SQLCOM_DROP_SEQUENCE) && lex->tmp_table() && lex->if_exists()) && all_tables_not_ok(thd, all_tables)) { @@ -3817,6 +3820,7 @@ mysql_execute_command(THD *thd) res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX); break; } + case SQLCOM_CREATE_SEQUENCE: case SQLCOM_CREATE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4074,6 +4078,7 @@ mysql_execute_command(THD *thd) /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); } + if (!res) { /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4282,9 +4287,9 @@ end_with_restore_list: */ DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s", - lex->only_view, + lex->table_type == TABLE_TYPE_VIEW, first_table->db, first_table->table_name)); - if (lex->only_view) + if (lex->table_type == TABLE_TYPE_VIEW) { if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) { @@ -4298,7 +4303,6 @@ end_with_restore_list: /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->open_type= OT_BASE_ONLY; - } else { @@ -4806,6 +4810,7 @@ end_with_restore_list: } break; } + case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4816,7 +4821,7 @@ end_with_restore_list: } else { - status_var_decrement(thd->status_var.com_stat[SQLCOM_DROP_TABLE]); + status_var_decrement(thd->status_var.com_stat[lex->sql_command]); status_var_increment(thd->status_var.com_drop_tmp_table); /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4846,10 +4851,13 @@ end_with_restore_list: lex->create_info.set(DDL_options_st::OPT_IF_EXISTS); /* DDL and binlog write order are protected by metadata locks. */ - res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); + res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(), + lex->table_type == TABLE_TYPE_SEQUENCE); - /* when dropping temporary tables if @@session_track_state_change is ON then - send the boolean tracker in the OK packet */ + /* + When dropping temporary tables if @@session_track_state_change is ON + then send the boolean tracker in the OK packet + */ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); @@ -8095,6 +8103,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES); + ptr->sequence= MY_TEST(table_options & TL_OPTION_SEQUENCE); ptr->derived= table->sel; if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) { @@ -8135,8 +8144,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->cacheable_table= !table->is_derived_table(); ptr->index_hints= index_hints_arg; ptr->option= option ? option->str : 0; - /* check that used name is unique */ - if (lock_type != TL_IGNORE) + /* check that used name is unique. Sequences are ignored */ + if (lock_type != TL_IGNORE && !ptr->sequence) { TABLE_LIST *first_table= table_list.first; if (lex->sql_command == SQLCOM_CREATE_VIEW) @@ -8146,7 +8155,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, tables=tables->next_local) { if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && - !strcmp(ptr->db, tables->db)) + !strcmp(ptr->db, tables->db) && ! tables->sequence) { my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ @@ -8154,7 +8163,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } /* Store the table reference preceding the current one. */ - if (table_list.elements > 0) + if (table_list.elements > 0 && !ptr->sequence) { /* table_list.next points to the last inserted TABLE_LIST->next_local' @@ -8179,8 +8188,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, Notice that as a side effect here we set the next_local field of the previous table reference to 'ptr'. Here we also add one element to the list 'table_list'. + We don't store sequences into the local list to hide them from INSERT + and SELECT. */ - table_list.link_in_list(ptr, &ptr->next_local); + if (!ptr->sequence) + table_list.link_in_list(ptr, &ptr->next_local); ptr->next_name_resolution_table= NULL; #ifdef WITH_PARTITION_STORAGE_ENGINE ptr->partition_names= partition_names; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 101ea3fd3c7..fe779cf01b4 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1651,9 +1651,10 @@ int plugin_init(int *argc, char **argv, int flags) build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0); char engine_name_buf[NAME_CHAR_LEN + 1]; LEX_STRING maybe_myisam= { engine_name_buf, 0 }; - frm_type_enum frm_type= dd_frm_type(NULL, path, &maybe_myisam); + bool is_sequence; + Table_type frm_type= dd_frm_type(NULL, path, &maybe_myisam, &is_sequence); /* if mysql.plugin table is MyISAM - load it right away */ - if (frm_type == FRMTYPE_TABLE && !strcasecmp(maybe_myisam.str, "MyISAM")) + if (frm_type == TABLE_TYPE_NORMAL && !strcasecmp(maybe_myisam.str, "MyISAM")) { plugin_load(&tmp_root); flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4b2d8b5fb36..3aa6f531e01 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2388,6 +2388,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) } break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: res= mysql_test_create_table(stmt); break; case SQLCOM_SHOW_CREATE: @@ -2489,6 +2490,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) */ case SQLCOM_SHOW_EXPLAIN: case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: case SQLCOM_RENAME_TABLE: case SQLCOM_ALTER_TABLE: case SQLCOM_COMMIT: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5ca19e34bac..e6cfd923d05 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1550,7 +1550,14 @@ JOIN::optimize_inner() } if (const_tables && !thd->locked_tables_mode && !(select_options & SELECT_NO_UNLOCK)) - mysql_unlock_some_tables(thd, table, const_tables); + { + /* + Unlock all tables, except sequences, as accessing these may still + require table updates + */ + mysql_unlock_some_tables(thd, table, const_tables, + GET_LOCK_SKIP_SEQUENCES); + } if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc new file mode 100644 index 00000000000..d021ee63ebf --- /dev/null +++ b/sql/sql_sequence.cc @@ -0,0 +1,670 @@ +/* + Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_class.h" +#include "sql_list.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_base.h" +#include "transaction.h" +#include "lock.h" + +struct Field_definition +{ + const char *field_name; + uint length; + enum enum_field_types sql_type; + LEX_STRING comment; + ulong flags; +}; + +/* + Structure for all SEQUENCE tables + + Note that the first field is named "next_val" to all us to have + NEXTVAL a reserved word that will on access be changed to + NEXTVAL(sequence_table). For this to work, the table can't have + a column named NEXTVAL. +*/ + +#define FL (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG) + +static Field_definition sequence_structure[]= +{ + {"next_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("next not cached value")}, + FL}, + {"min_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("min value")}, FL}, + {"max_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("max value")}, FL}, + {"start", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("start value")}, FL}, + {"increment", 21, MYSQL_TYPE_LONGLONG, + {C_STRING_WITH_LEN("increment value")}, FL}, + {"cache", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("cache size")}, FL}, + {"cycle", 1, MYSQL_TYPE_TINY, {C_STRING_WITH_LEN("cycle state")}, + FL | UNSIGNED_FLAG }, + {"round", 21, MYSQL_TYPE_LONGLONG, + {C_STRING_WITH_LEN("How many cycles has been done")}, FL}, + {NULL, 0, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("")}, 0} +}; + +#undef FL + + +#define MAX_AUTO_INCREMENT_VALUE 65535 + +/* + Check whether sequence values are valid. + Sets default values for fields that are not used, according to Oracle spec. + + Note that reserved_until is not checked as it's ok that it's outside of + the range (to indicate that sequence us used up). + + RETURN VALUES + false valid + true invalid +*/ + +bool sequence_definition::check_and_adjust() +{ + longlong max_increment; + DBUG_ENTER("sequence_definition::check"); + + /* + If min_value is not set, set it to LONGLONG_MIN or 1, depending on + increment + */ + if (!(used_fields & seq_field_used_min_value)) + min_value= increment < 0 ? LONGLONG_MIN+1 : 1; + + /* + If min_value is not set, set it to LONGLONG_MAX or -1, depending on + increment + */ + if (!(used_fields & seq_field_used_max_value)) + max_value= increment < 0 ? -1 : LONGLONG_MAX-1; + + if (!(used_fields & seq_field_used_start)) + { + /* Use min_value or max_value for start depending on increment */ + start= increment < 0 ? max_value : min_value; + } + + /* To ensure that cache * increment will never overflow */ + max_increment= increment ? labs(increment) : MAX_AUTO_INCREMENT_VALUE; + + if (max_value >= start && + max_value > min_value && + start >= min_value && + max_value != LONGLONG_MAX && + min_value != LONGLONG_MIN && + cache < (LONGLONG_MAX - max_increment) / max_increment) + DBUG_RETURN(FALSE); + DBUG_RETURN(TRUE); +} + + +/* + Read sequence values from a table +*/ + +void sequence_definition::read_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + reserved_until= table->field[0]->val_int(); + min_value= table->field[1]->val_int(); + max_value= table->field[2]->val_int(); + start= table->field[3]->val_int(); + increment= table->field[4]->val_int(); + cache= table->field[5]->val_int(); + cycle= table->field[6]->val_int(); + round= table->field[7]->val_int(); + dbug_tmp_restore_column_map(table->read_set, old_map); + used_fields= ~(uint) 0; + print_dbug(); +} + + +/* + Store sequence into a table row +*/ + +void sequence_definition::store_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + + /* zero possible delete markers & null bits */ + memcpy(table->record[0], table->s->default_values, table->s->null_bytes); + table->field[0]->store(reserved_until, 0); + table->field[1]->store(min_value, 0); + table->field[2]->store(max_value, 0); + table->field[3]->store(start, 0); + table->field[4]->store(increment, 0); + table->field[5]->store(cache, 0); + table->field[6]->store((longlong) cycle != 0, 0); + table->field[7]->store((longlong) round, 1); + + dbug_tmp_restore_column_map(table->write_set, old_map); + print_dbug(); +} + + +/* + Check the sequence fields through seq_fields when create sequence.qq + + RETURN VALUES + false Success + true Failure +*/ + +bool check_sequence_fields(LEX *lex, List<Create_field> *fields) +{ + Create_field *field; + List_iterator_fast<Create_field> it(*fields); + uint field_count; + uint field_no; + const char *reason; + DBUG_ENTER("check_sequence_fields"); + + field_count= fields->elements; + if (field_count != array_elements(sequence_structure)-1) + { + reason= "Wrong number of columns"; + goto err; + } + if (lex->alter_info.key_list.elements > 0) + { + reason= "Sequence tables cannot have any keys"; + goto err; + } + + for (field_no= 0; (field= it++); field_no++) + { + Field_definition *field_def= &sequence_structure[field_no]; + if (my_strcasecmp(system_charset_info, field_def->field_name, + field->field_name) || + field->flags != field_def->flags || + field->sql_type != field_def->sql_type) + { + reason= field->field_name; + goto err; + } + } + DBUG_RETURN(FALSE); + +err: + my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name, reason); + DBUG_RETURN(TRUE); +} + + +/* + Create the fields for a SEQUENCE TABLE + + RETURN VALUES + false Success + true Failure (out of memory) +*/ + +bool prepare_sequence_fields(THD *thd, List<Create_field> *fields) +{ + Field_definition *field_info; + DBUG_ENTER("prepare_sequence_fields"); + + for (field_info= sequence_structure; field_info->field_name ; field_info++) + { + Create_field *new_field; + if (unlikely(!(new_field= new Create_field()))) + DBUG_RETURN(TRUE); /* purify inspected */ + + new_field->field_name= field_info->field_name; + new_field->sql_type= field_info->sql_type; + new_field->length= field_info->length; + new_field->char_length= field_info->length; + new_field->comment= field_info->comment; + new_field->flags= field_info->flags; + if (unlikely(fields->push_back(new_field))) + DBUG_RETURN(TRUE); /* purify inspected */ + } + DBUG_RETURN(FALSE); +} + +/* + Initialize the sequence table record as part of CREATE SEQUENCE + + Store one row with sequence information. + + RETURN VALUES + false Success + true Failure. Error reported. + + NOTES + This function is called as part of CREATE SEQUENCE. When called + there are now active transactions and no open tables. + There is also a MDL lock on the table. +*/ + +bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list) +{ + int error; + TABLE *table; + TABLE_LIST::enum_open_strategy save_open_strategy; + sequence_definition *seq= lex->create_info.seq_create_info; + bool temporary_table= table_list->table != 0; + MY_BITMAP *save_write_set; + DBUG_ENTER("sequence_insert"); + + /* If not temporary table */ + if (!temporary_table) + { + /* Table was locked as part of create table. Free it but keep MDL locks */ + close_thread_tables(thd); + table_list->lock_type= TL_WRITE_DEFAULT; + table_list->updating= 1; + /* + The FOR CREATE flag is needed to ensure that ha_open() doesn't try to + read the not yet existing row in the sequence table + */ + thd->open_options|= HA_OPEN_FOR_CREATE; + save_open_strategy= table_list->open_strategy; + table_list->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + table_list->open_type= OT_BASE_ONLY; + error= open_and_lock_tables(thd, table_list, FALSE, + MYSQL_LOCK_IGNORE_TIMEOUT | + MYSQL_OPEN_HAS_MDL_LOCK); + table_list->open_strategy= save_open_strategy; + thd->open_options&= ~HA_OPEN_FOR_CREATE; + if (error) + DBUG_RETURN(TRUE); /* purify inspected */ + } + + table= table_list->table; + + /* + seq is 0 if sequence was created with CREATE TABLE instead of + CREATE SEQUENCE + */ + if (!seq) + { + if (!(seq= new (thd->mem_root) sequence_definition)) + DBUG_RETURN(TRUE); // EOM + } + + seq->reserved_until= seq->start; + seq->store_fields(table); + /* Store the sequence values in table share */ + table->s->sequence->copy(seq); + + /* + Sequence values will be replicated as a statement + like 'create sequence'. So disable binary log temporarily + */ + tmp_disable_binlog(thd); + save_write_set= table->write_set; + table->write_set= &table->s->all_set; + error= table->file->ha_write_row(table->record[0]); + reenable_binlog(thd); + table->write_set= save_write_set; + + if (error) + table->file->print_error(error, MYF(0)); + else + { + /* + Sequence structure is up to date and table has one row, + sequence is now usable + */ + table->s->sequence->initialized= 1; + } + + trans_commit_stmt(thd); + trans_commit_implicit(thd); + if (!temporary_table) + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + DBUG_RETURN(error); +} + + +/* Create a SQUENCE object */ + +SEQUENCE::SEQUENCE() :initialized(0), all_values_used(0), table(0) +{ + mysql_mutex_init(key_LOCK_SEQUENCE, &mutex, MY_MUTEX_INIT_SLOW); +} + +SEQUENCE::~SEQUENCE() +{ + mysql_mutex_destroy(&mutex); +} + + +/** + Read values from the sequence tables to table_share->sequence. + This is called from ha_open() when the table is not yet locked +*/ + +int SEQUENCE::read_initial_values(TABLE *table_arg) +{ + int error= 0; + enum thr_lock_type save_lock_type; + MDL_request mdl_request; // Empty constructor! + DBUG_ENTER("SEQUENCE::read_initial_values"); + + if (likely(initialized)) + DBUG_RETURN(0); + table= table_arg; + mysql_mutex_lock(&mutex); + if (unlikely(!initialized)) + { + MYSQL_LOCK *lock; + bool mdl_lock_used= 0; + THD *thd= table->in_use; + bool has_active_transaction= !thd->transaction.stmt.is_empty(); + /* + There is already a mdl_ticket for this table. However, for list_fields + the MDL lock is of type MDL_SHARED_HIGH_PRIO which is not usable + for doing a able lock. Get a proper read lock to solve this. + */ + if (table->mdl_ticket == 0) + { + MDL_request_list mdl_requests; + mdl_lock_used= 1; + /* + This happens if first request is SHOW CREATE TABLE or LIST FIELDS + where we don't have a mdl lock on the table + */ + + mdl_request.init(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_SHARED_READ, MDL_EXPLICIT); + mdl_requests.push_front(&mdl_request); + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + save_lock_type= table->reginfo.lock_type; + table->reginfo.lock_type= TL_READ; + if (!(lock= mysql_lock_tables(thd, &table, 1, + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY))) + { + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + if (!(error= read_stored_values())) + initialized= 1; + mysql_unlock_tables(thd, lock, 0); + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + + /* Reset value to default */ + table->reginfo.lock_type= save_lock_type; + /* + Doing mysql_lock_tables() may have started a read only transaction. + If that happend, it's better that we commit it now, as a lot of + code assumes that there is no active stmt transaction directly after + open_tables() + */ + if (!has_active_transaction && !thd->transaction.stmt.is_empty()) + trans_commit_stmt(thd); + } + mysql_mutex_unlock(&mutex); + DBUG_RETURN(error); +} + +/* + Read data from sequence table and update values + Done when table is opened +*/ + +int SEQUENCE::read_stored_values() +{ + int error; + my_bitmap_map *save_read_set; + DBUG_ENTER("SEQUENCE::read_stored_values"); + mysql_mutex_assert_owner(&mutex); + + save_read_set= tmp_use_all_columns(table, table->read_set); + error= table->file->ha_read_first_row(table->record[0], MAX_KEY); + tmp_restore_column_map(table->read_set, save_read_set); + + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + read_fields(table); + adjust_values(); + + all_values_used= 0; + DBUG_RETURN(0); +} + + +/* + Adjust values after reading a the stored state +*/ + +void SEQUENCE::adjust_values() +{ + offset= 0; + next_free_value= reserved_until; + if (!(real_increment= increment)) + { + longlong off, to_add; + /* Use auto_increment_increment and auto_increment_offset */ + + if ((real_increment= global_system_variables.auto_increment_increment) + != 1) + offset= global_system_variables.auto_increment_offset; + + /* + Ensure that next_free_value has the right offset, so that we + can generate a serie by just adding real_increment. + */ + off= next_free_value % real_increment; + if (off < 0) + off+= real_increment; + to_add= (real_increment + offset - off) % real_increment; + + /* + Check if add will make next_free_value bigger than max_value, + taken into account that next_free_value or max_value addition + may overflow + */ + if (next_free_value > max_value - to_add || + next_free_value + to_add > max_value) + next_free_value= max_value+1; + else + { + next_free_value+= to_add; + DBUG_ASSERT(next_free_value % real_increment == offset && + next_free_value >= reserved_until); + } + } +} + + +/** + Get next value for sequence + + @param in table Sequence table + @param in second_round + 1 if recursive call (out of values once) + @param out error Set this to <> 0 in case of error + push_warning_printf(WARN_LEVEL_WARN) has been called + + + @retval 0 Next number or error. Check error variable + # Next sequence number + + NOTES: + Return next_free_value and increment next_free_value to next allowed + value or reserved_value if out of range + if next_free_value >= reserved_value reserve a new range by writing + a record to the sequence table. + + The state of the variables: + next_free_value contains next value to use. It may be + bigger than max_value or less than min_value if end of sequence. + reserved_until contains the last value written to the file. All + values up to this one can be used. + If next_free_value >= reserved_until we have to reserve new + values from the sequence. +*/ + +longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error) +{ + longlong res_value, org_reserved_until, add_to; + bool out_of_values; + MY_BITMAP *save_rpl_write_set, *save_write_set; + DBUG_ENTER("SEQUENCE::next_value"); + + *error= 0; + if (!second_round) + lock(); + + res_value= next_free_value; + + /* Increment next_free_value */ + if (real_increment > 0) + { + if (next_free_value + real_increment > max_value || + next_free_value > max_value - real_increment) + next_free_value= max_value + 1; + else + next_free_value+= real_increment; + } + else + { + if (next_free_value + real_increment < min_value || + next_free_value < min_value - real_increment) + next_free_value= min_value - 1; + else + next_free_value+= real_increment; + } + + if ((real_increment > 0 && res_value < reserved_until) || + (real_increment < 0 && res_value > reserved_until)) + { + unlock(); + DBUG_RETURN(res_value); + } + + if (all_values_used) + goto err; + + org_reserved_until= reserved_until; + + /* + Out of cached values, reserve 'cache' new ones + The cache value is checked on insert so the following can't + overflow + */ + add_to= cache ? real_increment * cache : 1; + out_of_values= 0; + + if (real_increment > 0) + { + if (reserved_until + add_to > max_value || + reserved_until > max_value - add_to) + { + reserved_until= max_value + 1; + out_of_values= res_value >= reserved_until; + } + else + reserved_until+= add_to; + } + else + { + if (reserved_until + add_to < min_value || + reserved_until < min_value - add_to) + { + reserved_until= min_value - 1; + out_of_values= res_value <= reserved_until; + } + else + reserved_until+= add_to; + } + if (out_of_values) + { + if (!cycle || second_round) + goto err; + round++; + reserved_until= real_increment >0 ? min_value : max_value; + adjust_values(); // Fix next_free_value + /* + We have to do everything again to ensure that the given range was + not empty, which could happen if increment == 0 + */ + DBUG_RETURN(next_value(table, 1, error)); + } + + /* Log a full insert (ok as table is small) */ + save_rpl_write_set= table->rpl_write_set; + + /* Update table */ + save_write_set= table->write_set; + table->rpl_write_set= table->write_set= &table->s->all_set; + store_fields(table); + /* Tell ha_sequence::write_row that we already hold the mutex */ + ((ha_sequence*) table->file)->sequence_locked= 1; + if ((*error= table->file->ha_write_row(table->record[0]))) + { + table->file->print_error(*error, MYF(0)); + /* Restore original range */ + reserved_until= org_reserved_until; + next_free_value= res_value; + } + ((ha_sequence*) table->file)->sequence_locked= 0; + table->rpl_write_set= save_rpl_write_set; + table->write_set= save_write_set; + + unlock(); + DBUG_RETURN(res_value); + +err: + unlock(); + my_error(ER_SEQUENCE_RUN_OUT, MYF(0), table->s->db.str, + table->s->table_name.str); + *error= ER_SEQUENCE_RUN_OUT; + all_values_used= 1; + DBUG_RETURN(0); +} + + +/* + The following functions is to detect if a table has been dropped + and re-created since last call to PREVIOUS VALUE. + + This is needed as we don't delete dropped sequences from THD->sequence + for DROP TABLE. +*/ + +bool SEQUENCE_LAST_VALUE::check_version(TABLE *table) +{ + DBUG_ASSERT(table->s->tabledef_version.length == MY_UUID_SIZE); + return memcmp(table->s->tabledef_version.str, table_version, + MY_UUID_SIZE) != 0; +} + +void SEQUENCE_LAST_VALUE::set_version(TABLE *table) +{ + memcpy(table_version, table->s->tabledef_version.str, MY_UUID_SIZE); +} diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h new file mode 100644 index 00000000000..f5534d55d9b --- /dev/null +++ b/sql/sql_sequence.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2017, MariaDB corporation + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SQL_SEQUENCE_INCLUDED +#define SQL_SEQUENCE_INCLUDED + +#define seq_field_used_min_value 1 +#define seq_field_used_max_value 2 +#define seq_field_used_start 4 + +/** + sequence_definition is used when defining a sequence as part of create +*/ + +class sequence_definition :public Sql_alloc +{ +public: + sequence_definition(): + min_value(1), max_value(LONGLONG_MAX-1), start(1), increment(1), + cache(1000), round(0), cycle(0), used_fields(0) + {} + longlong reserved_until; + longlong min_value; + longlong max_value; + longlong start; + longlong increment; + longlong cache; + ulonglong round; + bool cycle; + uint used_fields; // Which fields where used in CREATE + + bool check_and_adjust(); + void store_fields(TABLE *table); + void read_fields(TABLE *table); + void print_dbug() + { + DBUG_PRINT("sequence", ("reserved: %lld start: %lld increment: %lld min_value: %lld max_value: %lld cache: %lld round: %lld", + reserved_until, start, increment, min_value, + max_value, cache, round)); + } +}; + +/** + SEQUENCE is in charge of managing the sequence values. + It's also responsible to generate new values and updating the sequence + table (engine=SQL_SEQUENCE) trough it's specialized handler interface. + + If increment is 0 then the sequence will be be using + auto_increment_increment and auto_increment_offset variables, just like + AUTO_INCREMENT is using. +*/ + +class SEQUENCE :public sequence_definition +{ +public: + SEQUENCE(); + ~SEQUENCE(); + int read_initial_values(TABLE *table); + int read_stored_values(); + void lock() + { + mysql_mutex_lock(&mutex); + } + void unlock() + { + mysql_mutex_unlock(&mutex); + } + /* This must be called after sequence data has been updated */ + void adjust_values(); + void copy(sequence_definition *seq) + { + sequence_definition::operator= (*seq); + adjust_values(); + } + longlong next_value(TABLE *table, bool second_round, int *error); + + bool initialized; // If row has been read + bool all_values_used; +private: + TABLE *table; + mysql_mutex_t mutex; + longlong next_free_value; + /* + The following values are the values from sequence_definition + merged with global auto_increment_offset and auto_increment_increment + */ + longlong real_increment; + longlong offset; +}; + + +/** + Class to cache last value of NEXT VALUE from the sequence +*/ + +class SEQUENCE_LAST_VALUE +{ +public: + SEQUENCE_LAST_VALUE(uchar *key_arg, uint length_arg) + :key(key_arg), length(length_arg) + {} + ~SEQUENCE_LAST_VALUE() + { my_free((void*) key); } + /* Returns 1 if table hasn't been dropped or re-created */ + bool check_version(TABLE *table); + void set_version(TABLE *table); + + const uchar *key; + uint length; + bool null_value; + longlong value; + uchar table_version[MY_UUID_SIZE]; +}; + + +class Create_field; +extern bool prepare_sequence_fields(THD *thd, List<Create_field> *fields); +extern bool check_sequence_fields(LEX *lex, List<Create_field> *fields); +extern bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list); +#endif /* SQL_SEQUENCE_INCLUDED */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 4fb1b2e8c84..01f474a73fb 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -58,10 +58,11 @@ #include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH #include "debug_sync.h" #include "keycaches.h" - +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif + enum enum_i_s_events_fields { ISE_EVENT_CATALOG= 0, @@ -130,6 +131,8 @@ static void get_cs_converted_string_value(THD *thd, #endif static int show_create_view(THD *thd, TABLE_LIST *table, String *buff); +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet); static const LEX_STRING *view_algorithm(TABLE_LIST *table); @@ -1169,12 +1172,19 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, } /* TODO: add environment variables show when it become possible */ - if (thd->lex->only_view && !table_list->view) + if (thd->lex->table_type == TABLE_TYPE_VIEW && !table_list->view) { my_error(ER_WRONG_OBJECT, MYF(0), table_list->db, table_list->table_name, "VIEW"); goto exit; } + else if (thd->lex->table_type == TABLE_TYPE_SEQUENCE && + table_list->table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_WRONG_OBJECT, MYF(0), + table_list->db, table_list->table_name, "SEQUENCE"); + goto exit; + } buffer->length(0); @@ -1183,6 +1193,8 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, if ((table_list->view ? show_create_view(thd, table_list, buffer) : + thd->lex->table_type == TABLE_TYPE_SEQUENCE ? + show_create_sequence(thd, table_list, buffer) : show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) goto exit; @@ -1761,6 +1773,179 @@ static void append_create_options(THD *thd, String *packet, packet->append(STRING_WITH_LEN(" */")); } +/** + Add table options to end of CREATE statement + + @param schema_table 1 if schema table + @param sequence 1 if sequence. If sequence, we flush out options + not relevant for sequences. +*/ + +static void add_table_options(THD *thd, TABLE *table, + Table_specification_st *create_info_arg, + bool schema_table, bool sequence, + String *packet) +{ + sql_mode_t sql_mode= thd->variables.sql_mode; + TABLE_SHARE *share= table->s; + handlerton *hton; + HA_CREATE_INFO create_info; + bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && + !create_info_arg); + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + hton= table->part_info->default_engine_type; + else +#endif + hton= table->file->ht; + + bzero((char*) &create_info, sizeof(create_info)); + /* Allow update_create_info to update row type, page checksums and options */ + create_info.row_type= share->row_type; + create_info.page_checksum= share->page_checksum; + create_info.options= share->db_create_options; + table->file->update_create_info(&create_info); + + /* + IF check_create_info + THEN add ENGINE only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) + { + LEX_STRING *engine_name= table->file->engine_name(); + + if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + packet->append(STRING_WITH_LEN(" TYPE=")); + else + packet->append(STRING_WITH_LEN(" ENGINE=")); + + packet->append(engine_name->str, engine_name->length); + } + + if (sequence) + goto end_options; + + /* + Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, + and NEXT_ID > 1 (the default). We must not print the clause + for engines that do not support this as it would break the + import of dumps, but as of this writing, the test for whether + AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... + is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) + Because of that, we do not explicitly test for the feature, + but may extrapolate its existence from that of an AUTO_INCREMENT column. + */ + + if (create_info.auto_increment_value > 1) + { + packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); + packet->append_ulonglong(create_info.auto_increment_value); + } + + if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + share->table_type != TABLE_TYPE_SEQUENCE) + { + /* + IF check_create_info + THEN add DEFAULT CHARSET only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) + { + packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); + packet->append(share->table_charset->csname); + if (!(share->table_charset->state & MY_CS_PRIMARY)) + { + packet->append(STRING_WITH_LEN(" COLLATE=")); + packet->append(table->s->table_charset->name); + } + } + } + + if (share->min_rows) + { + packet->append(STRING_WITH_LEN(" MIN_ROWS=")); + packet->append_ulonglong(share->min_rows); + } + + if (share->max_rows && !schema_table && !sequence) + { + packet->append(STRING_WITH_LEN(" MAX_ROWS=")); + packet->append_ulonglong(share->max_rows); + } + + if (share->avg_row_length) + { + packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); + packet->append_ulonglong(share->avg_row_length); + } + + if (create_info.options & HA_OPTION_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); + if (create_info.options & HA_OPTION_NO_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); + if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); + if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); + if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); + else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); + if (share->stats_sample_pages != 0) + { + packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); + packet->append_ulonglong(share->stats_sample_pages); + } + + /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ + if (create_info.options & HA_OPTION_CHECKSUM) + packet->append(STRING_WITH_LEN(" CHECKSUM=1")); + if (create_info.page_checksum != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); + packet->append(ha_choice_values[create_info.page_checksum], 1); + } + if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) + packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); + if (create_info.row_type != ROW_TYPE_DEFAULT) + { + packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); + packet->append(ha_row_type[(uint) create_info.row_type]); + } + if (share->transactional != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); + packet->append(ha_choice_values[(uint) share->transactional], 1); + } + if (share->table_type == TABLE_TYPE_SEQUENCE) + packet->append(STRING_WITH_LEN(" SEQUENCE=1")); + if (table->s->key_block_size) + { + packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); + packet->append_ulonglong(table->s->key_block_size); + } + table->file->append_create_info(packet); + +end_options: + if (share->comment.length) + { + packet->append(STRING_WITH_LEN(" COMMENT=")); + append_unescaped(packet, share->comment.str, share->comment.length); + } + if (share->connect_string.length) + { + packet->append(STRING_WITH_LEN(" CONNECTION=")); + append_unescaped(packet, share->connect_string.str, share->connect_string.length); + } + append_create_options(thd, packet, share->option_list, check_options, + hton->table_options); + append_directory(thd, packet, "DATA", create_info.data_file_name); + append_directory(thd, packet, "INDEX", create_info.index_file_name); +} + /* Build a CREATE TABLE statement for a table. @@ -1790,7 +1975,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, enum_with_db_name with_db_name) { List<Item> field_list; - char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH]; + char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH]; const char *alias; String type; String def_value; @@ -1798,9 +1983,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, uint primary_key; KEY *key_info; TABLE *table= table_list->table; - handler *file= table->file; TABLE_SHARE *share= table->s; - HA_CREATE_INFO create_info; sql_mode_t sql_mode= thd->variables.sql_mode; bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | MODE_DB2 | @@ -1811,8 +1994,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, !foreign_db_mode; bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && !create_info_arg; - handlerton *hton; my_bitmap_map *old_map; + handlerton *hton; int error= 0; DBUG_ENTER("show_create_table"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -1822,7 +2005,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton= table->part_info->default_engine_type; else #endif - hton= file->ht; + hton= table->file->ht; restore_record(table, s->default_values); // Get empty record @@ -1971,12 +2154,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } key_info= table->key_info; - bzero((char*) &create_info, sizeof(create_info)); - /* Allow update_create_info to update row type, page checksums and options */ - create_info.row_type= share->row_type; - create_info.page_checksum= share->page_checksum; - create_info.options= share->db_create_options; - file->update_create_info(&create_info); primary_key= share->primary_key; for (uint i=0 ; i < share->keys ; i++,key_info++) @@ -2043,10 +2220,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, to the CREATE TABLE statement */ - if ((for_str= file->get_foreign_key_create_info())) + if ((for_str= table->file->get_foreign_key_create_info())) { packet->append(for_str, strlen(for_str)); - file->free_foreign_key_create_info(for_str); + table->file->free_foreign_key_create_info(for_str); } /* Add table level check constraints */ @@ -2073,146 +2250,9 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (show_table_options) - { - /* - IF check_create_info - THEN add ENGINE only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) - { - if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) - packet->append(STRING_WITH_LEN(" TYPE=")); - else - packet->append(STRING_WITH_LEN(" ENGINE=")); - packet->append(hton_name(hton)); - } - - /* - Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, - and NEXT_ID > 1 (the default). We must not print the clause - for engines that do not support this as it would break the - import of dumps, but as of this writing, the test for whether - AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... - is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) - Because of that, we do not explicitly test for the feature, - but may extrapolate its existence from that of an AUTO_INCREMENT column. - */ - - if (create_info.auto_increment_value > 1) - { - char *end; - packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); - end= longlong10_to_str(create_info.auto_increment_value, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) - { - /* - IF check_create_info - THEN add DEFAULT CHARSET only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) - { - packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); - packet->append(share->table_charset->csname); - if (!(share->table_charset->state & MY_CS_PRIMARY)) - { - packet->append(STRING_WITH_LEN(" COLLATE=")); - packet->append(table->s->table_charset->name); - } - } - } - - if (share->min_rows) - { - char *end; - packet->append(STRING_WITH_LEN(" MIN_ROWS=")); - end= longlong10_to_str(share->min_rows, buff, 10); - packet->append(buff, (uint) (end- buff)); - } - - if (share->max_rows && !table_list->schema_table) - { - char *end; - packet->append(STRING_WITH_LEN(" MAX_ROWS=")); - end= longlong10_to_str(share->max_rows, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->avg_row_length) - { - char *end; - packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); - end= longlong10_to_str(share->avg_row_length, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (create_info.options & HA_OPTION_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); - if (create_info.options & HA_OPTION_NO_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); - if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); - if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); - if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); - else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); - if (share->stats_sample_pages != 0) - { - char *end; - packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); - end= longlong10_to_str(share->stats_sample_pages, buff, 10); - packet->append(buff, (uint) (end - buff)); - } + add_table_options(thd, table, create_info_arg, + table_list->schema_table != 0, 0, packet); - /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ - if (create_info.options & HA_OPTION_CHECKSUM) - packet->append(STRING_WITH_LEN(" CHECKSUM=1")); - if (create_info.page_checksum != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); - packet->append(ha_choice_values[create_info.page_checksum], 1); - } - if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) - packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); - if (create_info.row_type != ROW_TYPE_DEFAULT) - { - packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); - packet->append(ha_row_type[(uint) create_info.row_type]); - } - if (share->transactional != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); - packet->append(ha_choice_values[(uint) share->transactional], 1); - } - if (table->s->key_block_size) - { - char *end; - packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); - end= longlong10_to_str(table->s->key_block_size, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - table->file->append_create_info(packet); - if (share->comment.length) - { - packet->append(STRING_WITH_LEN(" COMMENT=")); - append_unescaped(packet, share->comment.str, share->comment.length); - } - if (share->connect_string.length) - { - packet->append(STRING_WITH_LEN(" CONNECTION=")); - append_unescaped(packet, share->connect_string.str, share->connect_string.length); - } - append_create_options(thd, packet, share->option_list, check_options, - hton->table_options); - append_directory(thd, packet, "DATA", create_info.data_file_name); - append_directory(thd, packet, "INDEX", create_info.index_file_name); - } #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && @@ -2423,6 +2463,55 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) } +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet) +{ + TABLE *table= table_list->table; + SEQUENCE *seq= table->s->sequence; + LEX_STRING alias; + sql_mode_t sql_mode= thd->variables.sql_mode; + bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | + MODE_MSSQL | MODE_DB2 | + MODE_MAXDB | MODE_ANSI); + bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) && + !foreign_db_mode; + + if (lower_case_table_names == 2) + { + alias.str= table->alias.c_ptr(); + alias.length= table->alias.length(); + } + else + alias= table->s->table_name; + + packet->append(STRING_WITH_LEN("CREATE SEQUENCE ")); + append_identifier(thd, packet, alias.str, alias.length); + packet->append(STRING_WITH_LEN(" start with ")); + packet->append_longlong(seq->start); + packet->append(STRING_WITH_LEN(" minvalue ")); + packet->append_longlong(seq->min_value); + packet->append(STRING_WITH_LEN(" maxvalue ")); + packet->append_longlong(seq->max_value); + packet->append(STRING_WITH_LEN(" increment by ")); + packet->append_longlong(seq->increment); + if (seq->cache) + { + packet->append(STRING_WITH_LEN(" cache ")); + packet->append_longlong(seq->cache); + } + else + packet->append(STRING_WITH_LEN(" nocache")); + if (seq->cycle) + packet->append(STRING_WITH_LEN(" cycle")); + else + packet->append(STRING_WITH_LEN(" nocycle")); + + if (show_table_options) + add_table_options(thd, table, 0, 0, 1, packet); + return 0; +} + + /**************************************************************************** Return info about all processes returns for each thread: thread id, user, host, db, command, info @@ -4143,7 +4232,8 @@ static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl, char path[FN_REFLEN]; build_table_filename(path, sizeof(path) - 1, db->str, table->str, reg_ext, 0); - if (dd_frm_type(thd, path, &engine_name) == FRMTYPE_TABLE) + bool is_sequence; + if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL) tl->option= engine_name.str; } } @@ -4360,10 +4450,14 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, { CHARSET_INFO *cs= system_charset_info; handlerton *hton; - if (ha_table_exists(thd, db_name->str, table_name->str, &hton)) + bool is_sequence; + if (ha_table_exists(thd, db_name->str, table_name->str, &hton, + &is_sequence)) { if (hton == view_pseudo_hton) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); + else if (is_sequence) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); } @@ -5041,7 +5135,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, String str(option_buff,sizeof(option_buff), system_charset_info); TABLE *show_table= tables->table; TABLE_SHARE *share= show_table->s; - handler *file= show_table->file; + handler *file= show_table->db_stat ? show_table->file : 0; handlerton *tmp_db_type= share->db_type(); #ifdef WITH_PARTITION_STORAGE_ENGINE bool is_partitioned= FALSE; @@ -5051,6 +5145,8 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else if (share->tmp_table) table->field[3]->store(STRING_WITH_LEN("LOCAL TEMPORARY"), cs); + else if (share->table_type == TABLE_TYPE_SEQUENCE) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index b12f0332035..4d577fffb3e 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -546,6 +546,15 @@ bool String::append_ulonglong(ulonglong val) return FALSE; } +bool String::append_longlong(longlong val) +{ + if (realloc(str_length+MAX_BIGINT_WIDTH+2)) + return TRUE; + char *end= (char*) longlong10_to_str(val, (char*) Ptr + str_length, -10); + str_length= end - Ptr; + return FALSE; +} + /* Append a string in the given charset to the string with character set recoding diff --git a/sql/sql_string.h b/sql/sql_string.h index 0a930554d8a..3d7703b9f61 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -473,6 +473,7 @@ public: bool append(const LEX_CSTRING *ls) { return append(ls->str, ls->length); } bool append(const char *s, uint32 arg_length); bool append(const char *s, uint32 arg_length, CHARSET_INFO *cs); + bool append_longlong(longlong val); bool append_ulonglong(ulonglong val); bool append(IO_CACHE* file, uint32 arg_length); bool append_with_prefill(const char *s, uint32 arg_length, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 213b6eff6d2..5bd423b6ac7 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -54,7 +54,7 @@ #include "sql_show.h" #include "transaction.h" #include "sql_audit.h" - +#include "sql_sequence.h" #ifdef __WIN__ #include <io.h> @@ -1992,6 +1992,8 @@ int write_bin_log(THD *thd, bool clear_error, thd Thread handle tables List of tables to delete if_exists If 1, don't give error if one table doesn't exists + drop_temporary 1 if DROP TEMPORARY + drop_seqeunce 1 if DROP SEQUENCE NOTES Will delete all tables that can be deleted and give a compact error @@ -2008,8 +2010,8 @@ int write_bin_log(THD *thd, bool clear_error, */ -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary) +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence) { bool error; Drop_table_error_handler err_handler; @@ -2086,7 +2088,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, - false, false, false); + false, drop_sequence, false, false); thd->pop_internal_handler(); if (error) @@ -2175,6 +2177,7 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool dont_log_query, bool dont_free_locks) { @@ -2189,7 +2192,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; - bool was_view= 0; + bool was_view= 0, was_table, is_sequence; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2231,17 +2234,19 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, */ if (!dont_log_query) { + const char *object_to_drop= (drop_sequence) ? "SEQUENCE" : "TABLE"; + if (!drop_temporary) { const char *comment_start; uint32 comment_len; built_query.set_charset(thd->charset()); + built_query.append("DROP TABLE "); if (if_exists) - built_query.append("DROP TABLE IF EXISTS "); - else - built_query.append("DROP TABLE "); + built_query.append("IF EXISTS "); + /* Preserve comment in original query */ if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start))) { built_query.append(comment_start, comment_len); @@ -2249,21 +2254,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } } + built_trans_tmp_query.set_charset(system_charset_info); + built_trans_tmp_query.append("DROP TEMPORARY "); + built_trans_tmp_query.append(object_to_drop); + built_trans_tmp_query.append(' '); if (thd->is_current_stmt_binlog_format_row() || if_exists) { is_drop_tmp_if_exists_added= true; - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - } - else - { - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE "); + built_trans_tmp_query.append("IF EXISTS "); } + built_non_trans_tmp_query.set_charset(system_charset_info); + built_non_trans_tmp_query.copy(built_trans_tmp_query); } for (table= tables; table; table= table->next_local) @@ -2288,7 +2289,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) || + (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE)) error= 1; else { @@ -2396,26 +2398,31 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; if (drop_temporary || - (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) || - (!drop_view && (was_view= (table_type == view_pseudo_hton)))) + (ha_table_exists(thd, db, alias, &table_type, &is_sequence) == 0 && + table_type == 0) || + (!drop_view && (was_view= (table_type == view_pseudo_hton))) || + (drop_sequence && !is_sequence)) { /* One of the following cases happened: . "DROP TEMPORARY" but a temporary table was not found. . "DROP" but table was not found . "DROP TABLE" statement, but it's a view. + . "DROP SEQUENCE", but it's not a sequence */ + was_table= drop_sequence && table_type; if (if_exists) { char buff[FN_REFLEN]; + int err= (drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR); String tbl_name(buff, sizeof(buff), system_charset_info); tbl_name.length(0); tbl_name.append(db); tbl_name.append('.'); tbl_name.append(table->table_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_BAD_TABLE_ERROR, - ER_THD(thd, ER_BAD_TABLE_ERROR), + err, ER_THD(thd, err), tbl_name.c_ptr_safe()); } else @@ -2536,8 +2543,12 @@ err: DBUG_ASSERT(errors); if (errors == 1 && was_view) my_error(ER_IT_IS_A_VIEW, MYF(0), wrong_tables.c_ptr_safe()); + else if (errors == 1 && drop_sequence && was_table) + my_error(ER_NOT_SEQUENCE2, MYF(0), wrong_tables.c_ptr_safe()); else if (errors > 1 || !thd->is_error()) - my_error(ER_BAD_TABLE_ERROR, MYF(0), wrong_tables.c_ptr_safe()); + my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR), + MYF(0), wrong_tables.c_ptr_safe()); error= 1; } @@ -3181,11 +3192,26 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); - select_field_pos= alter_info->create_list.elements - select_field_count; null_fields=blob_columns=0; create_info->varchar= 0; max_key_length= file->max_key_length(); + /* Handle creation of sequences */ + if (create_info->sequence) + { + if (!(file->ha_table_flags() & HA_CAN_TABLES_WITHOUT_ROLLBACK)) + { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), file->engine_name()->str, + "SEQUENCE"); + DBUG_RETURN(TRUE); + } + + /* The user specified fields: check that structure is ok */ + if (check_sequence_fields(thd->lex, &alter_info->create_list)) + DBUG_RETURN(TRUE); + } + + select_field_pos= alter_info->create_list.elements - select_field_count; for (field_no=0; (sql_field=it++) ; field_no++) { CHARSET_INFO *save_cs; @@ -4655,7 +4681,7 @@ int create_table_impl(THD *thd, */ (void) trans_rollback_stmt(thd); /* Remove normal table without logging. Keep tables locked */ - if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1)) + if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 0, 1, 1)) goto err; /* @@ -4802,7 +4828,7 @@ int create_table_impl(THD *thd, } } #endif - + error= 0; err: THD_STAGE_INFO(thd, stage_after_create); @@ -4825,10 +4851,11 @@ warn: */ int mysql_create_table_no_lock(THD *thd, - const char *db, const char *table_name, - Table_specification_st *create_info, - Alter_info *alter_info, bool *is_trans, - int create_table_mode) + const char *db, const char *table_name, + Table_specification_st *create_info, + Alter_info *alter_info, bool *is_trans, + int create_table_mode, + TABLE_LIST *table_list) { KEY *not_used_1; uint not_used_2; @@ -4857,6 +4884,17 @@ int mysql_create_table_no_lock(THD *thd, alter_info, create_table_mode, is_trans, ¬_used_1, ¬_used_2, &frm); my_free(const_cast<uchar*>(frm.str)); + + if (!res && create_info->sequence) + { + /* Set create_info.table if temporary table */ + if (create_info->tmp_table()) + table_list->table= create_info->table; + else + table_list->table= 0; + res= sequence_insert(thd, thd->lex, table_list); + } + return res; } @@ -4918,7 +4956,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, promote_first_timestamp_column(&alter_info->create_list); if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, - &is_trans, create_table_mode) > 0) + &is_trans, create_table_mode, + create_table) > 0) { result= 1; goto err; @@ -5305,7 +5344,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, res= ((create_res= mysql_create_table_no_lock(thd, table->db, table->table_name, &local_create_info, &local_alter_info, - &is_trans, C_ORDINARY_CREATE)) > 0); + &is_trans, C_ORDINARY_CREATE, + table)) > 0); /* Remember to log if we deleted something */ do_logging= thd->log_current_statement; if (res) @@ -5542,7 +5582,7 @@ int mysql_discard_or_import_tablespace(THD *thd, table_list->mdl_request.set_type(MDL_EXCLUSIVE); table_list->lock_type= TL_WRITE; /* Do not open views. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; if (open_and_lock_tables(thd, table_list, FALSE, 0, &alter_prelocking_strategy)) @@ -7442,6 +7482,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!(used_fields & HA_CREATE_USED_CONNECTION)) create_info->connect_string= table->s->connect_string; + if (!(used_fields & HA_CREATE_USED_SEQUENCE)) + create_info->sequence= table->s->table_type == TABLE_TYPE_SEQUENCE; + restore_record(table, s->default_values); // Empty record for DEFAULT if ((create_info->fields_option_struct= (ha_field_option_struct**) @@ -8480,7 +8523,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Note that RENAME TABLE the only ALTER clause which is supported for views has been already processed. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; Alter_table_prelocking_strategy alter_prelocking_strategy; @@ -8587,7 +8630,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. */ - if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0)) + if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name)) { /* Table will be closed in do_command() */ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); @@ -9930,7 +9973,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, table->next_global= NULL; table->lock_type= TL_READ; /* Allow to open real tables only. */ - table->required_type= FRMTYPE_TABLE; + table->required_type= TABLE_TYPE_NORMAL; if (thd->open_temporary_tables(table) || open_and_lock_tables(thd, table, FALSE, 0)) diff --git a/sql/sql_table.h b/sql/sql_table.h index 5b2c9f32443..0ab6dcaa3ef 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -196,7 +196,7 @@ int mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode); + int create_table_mode, TABLE_LIST *table); handler *mysql_create_frm_image(THD *thd, const char *db, const char *table_name, @@ -239,10 +239,11 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list); bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary); +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence); int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool log_query, bool dont_free_locks); bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, const char *table_name, size_t table_name_length, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ca9b1431785..ce80d53feb2 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -525,7 +525,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } /* We also don't allow creation of triggers on views. */ - tables->required_type= FRMTYPE_TABLE; + tables->required_type= TABLE_TYPE_NORMAL; /* Also prevent DROP TRIGGER from opening temporary table which might shadow the subject table on which trigger to be dropped is defined. diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index c08c75f771a..daa295d768e 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -216,7 +216,7 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, or writing into the table. Yet, to open a write cursor we need a thr_lock lock. Allow to open base tables only. */ - table_ref->required_type= FRMTYPE_TABLE; + table_ref->required_type= TABLE_TYPE_NORMAL; /* Ignore pending FLUSH TABLES since we don't want to release the MDL lock taken above and otherwise there is no way to @@ -248,8 +248,8 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, table_ref->table->file->print_error(error, MYF(0)); /* If truncate method is not implemented then we don't binlog the - statement. If truncation has failed in a transactional engine then also we - donot binlog the statment. Only in non transactional engine we binlog + statement. If truncation has failed in a transactional engine then also + we don't binlog the statment. Only in non transactional engine we binlog inspite of errors. */ if (error == HA_ERR_WRONG_COMMAND || @@ -311,14 +311,17 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, } else { + handlerton *hton; + bool is_sequence; + /* Acquire an exclusive lock. */ DBUG_ASSERT(table_ref->next_global == NULL); if (lock_table_names(thd, table_ref, NULL, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); - handlerton *hton; - if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, &hton) || + if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, + &hton, &is_sequence) || hton == view_pseudo_hton) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name); @@ -337,7 +340,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, *hton_can_recreate= false; } else - *hton_can_recreate= hton->flags & HTON_CAN_RECREATE; + *hton_can_recreate= !is_sequence && hton->flags & HTON_CAN_RECREATE; } /* diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 90da7034ae3..09b1fbf65d0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -683,8 +683,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, buff.append(views->source.str, views->source.length); int errcode= query_error_code(thd, TRUE); + /* + Don't log any unsafe warnings for CREATE VIEW as it's safely replicated + with statement based replication + */ + thd->reset_unsafe_warnings(); if (thd->binlog_query(THD::STMT_QUERY_TYPE, - buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode)) + buff.ptr(), buff.length(), FALSE, FALSE, FALSE, + errcode)) res= TRUE; } @@ -1015,7 +1021,7 @@ loop_out: fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); - if (ha_table_exists(thd, view->db, view->table_name, NULL)) + if (ha_table_exists(thd, view->db, view->table_name)) { if (lex->create_info.if_not_exists()) { @@ -1153,7 +1159,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); - if (table->required_type == FRMTYPE_TABLE) + if (table->required_type == TABLE_TYPE_NORMAL) { my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "BASE TABLE"); @@ -1541,7 +1547,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) { - tbl->lock_type= table->lock_type; + /* We have to keep the lock type for sequence tables */ + if (!tbl->sequence) + tbl->lock_type= table->lock_type; tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ? MDL_SHARED_WRITE : MDL_SHARED_READ); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 869323d03cc..e06993bf9ca 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -66,6 +66,7 @@ #include "rpl_mi.h" #include "lex_token.h" #include "sql_lex.h" +#include "sql_sequence.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -858,10 +859,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 102 shift/reduce conflicts. + Currently there are 103 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 102 +%expect 103 /* Comments for TOKENS. @@ -995,6 +996,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ %token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM %token DATABASE %token DATABASES %token DATAFILE_SYM @@ -1133,6 +1135,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IGNORE_SERVER_IDS_SYM %token IMMEDIATE_SYM /* SQL-2003-R */ %token IMPORT +%token INCREMENT_SYM %token INDEXES %token INDEX_SYM %token INFILE @@ -1165,6 +1168,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LANGUAGE_SYM /* SQL-2003-R */ %token LAST_SYM /* SQL-2003-N */ %token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ %token LE /* OPERATOR */ %token LEADING /* SQL-2003-R */ %token LEAVES @@ -1223,7 +1227,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_UPDATES_PER_HOUR %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MAXVALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -1236,6 +1240,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM %token MIN_ROWS %token MIN_SYM /* SQL-2003-N */ %token MODE_SYM @@ -1259,6 +1264,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NEG %token NEW_SYM /* SQL-2003-R */ %token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM %token NODEGROUP_SYM %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM @@ -1266,6 +1274,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM @@ -1323,6 +1333,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM +%token PREVIOUS_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ %token PROCEDURE_SYM /* SQL-2003-R */ @@ -1403,6 +1414,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SELECT_SYM /* SQL-2003-R */ %token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM +%token SEQUENCE_SYM %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ @@ -1682,6 +1694,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <ulonglong_number> ulonglong_num real_ulonglong_num size_number +%type <longlong_number> + longlong_num + %type <choice> choice %type <lock_type> @@ -2450,6 +2465,64 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_lex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust()) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident opt_key_algorithm_clause ON table_ident @@ -2527,6 +2600,75 @@ create: { } ; +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | MAXVALUE_SYM opt_equal longlong_num + + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + } + ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3741,24 +3883,26 @@ sp_proc_stmt_open: } ; -sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor($3, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); - i= new (thd->mem_root) - sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_add_cfetch(thd, $2)) MYSQL_YYABORT; } - sp_fetch_list - {} + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3779,12 +3923,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -4605,7 +4743,7 @@ create_body: if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ - src_table->required_type= FRMTYPE_TABLE; + src_table->required_type= TABLE_TYPE_NORMAL; } ; @@ -5055,7 +5193,7 @@ opt_part_values: ; part_func_max: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; @@ -5169,7 +5307,7 @@ part_value_item_list: ; part_value_expr_item: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; if (part_info->part_type == LIST_PARTITION) @@ -5694,6 +5832,11 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } ; default_charset: @@ -6920,7 +7063,7 @@ alter: ALTER { Lex->name= null_lex_str; - Lex->only_view= FALSE; + Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; Lex->duplicates= DUP_ERROR; Lex->select_lex.init_order(); @@ -7796,7 +7939,9 @@ opt_checksum_type: repair_table_or_view: table_or_tables table_list opt_mi_repair_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type ; repair: @@ -7961,7 +8106,9 @@ binlog_base64_event: check_view_or_table: table_or_tables table_list opt_mi_check_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type ; check: CHECK_SYM @@ -9204,6 +9351,50 @@ column_default_non_parenthesized_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } ; simple_expr: @@ -11693,8 +11884,6 @@ delete_limit_clause: int_num: NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - ; ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } @@ -11713,6 +11902,13 @@ real_ulong_num: | dec_num_error { MYSQL_YYABORT; } ; +longlong_num: + NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + 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); } @@ -12027,6 +12223,17 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12245,6 +12452,16 @@ opt_equal: | equal {} ; +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + no_braces: '(' { @@ -12791,7 +13008,15 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; - lex->only_view= 1; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; } | MASTER_SYM STATUS_SYM { @@ -13083,8 +13308,10 @@ opt_flush_lock: for (; tables; tables= tables->next_global) { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; } } ; @@ -14377,6 +14604,7 @@ keyword_sp: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} | DATETIME {} @@ -14434,6 +14662,7 @@ keyword_sp: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14449,6 +14678,7 @@ keyword_sp: | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} @@ -14493,6 +14723,7 @@ keyword_sp: | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} @@ -14508,7 +14739,12 @@ keyword_sp: | NATIONAL_SYM {} | NCHAR_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} @@ -14535,6 +14771,7 @@ keyword_sp: | POLYGON {} | PRESERVE_SYM {} | PREV_SYM {} + | PREVIOUS_SYM {} | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} @@ -14579,6 +14816,7 @@ keyword_sp: | SCHEDULE_SYM {} | SCHEMA_NAME_SYM {} | SECOND_SYM {} + | SEQUENCE_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 791e7a386c9..4718a290627 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -66,6 +66,7 @@ #include "rpl_mi.h" #include "lex_token.h" #include "sql_lex.h" +#include "sql_sequence.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -273,10 +274,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 103 shift/reduce conflicts. + Currently there are 104 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 103 +%expect 104 /* Comments for TOKENS. @@ -410,6 +411,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ %token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM %token DATABASE %token DATABASES %token DATAFILE_SYM @@ -548,6 +550,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IGNORE_SERVER_IDS_SYM %token IMMEDIATE_SYM /* SQL-2003-R */ %token IMPORT +%token INCREMENT_SYM %token INDEXES %token INDEX_SYM %token INFILE @@ -580,6 +583,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LANGUAGE_SYM /* SQL-2003-R */ %token LAST_SYM /* SQL-2003-N */ %token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ %token LE /* OPERATOR */ %token LEADING /* SQL-2003-R */ %token LEAVES @@ -638,7 +642,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_UPDATES_PER_HOUR %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MAXVALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -651,6 +655,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM %token MIN_ROWS %token MIN_SYM /* SQL-2003-N */ %token MODE_SYM @@ -674,6 +679,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NEG %token NEW_SYM /* SQL-2003-R */ %token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM %token NODEGROUP_SYM %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM @@ -681,6 +689,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM @@ -738,6 +748,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM +%token PREVIOUS_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ %token PROCEDURE_SYM /* SQL-2003-R */ @@ -818,6 +829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SELECT_SYM /* SQL-2003-R */ %token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM +%token SEQUENCE_SYM %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ @@ -1111,6 +1123,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <ulonglong_number> ulonglong_num real_ulonglong_num size_number +%type <longlong_number> + longlong_num + %type <choice> choice %type <lock_type> @@ -1904,6 +1919,64 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_lex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust()) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident opt_key_algorithm_clause ON table_ident @@ -1981,6 +2054,75 @@ create: { } ; +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | MAXVALUE_SYM opt_equal longlong_num + + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + } + ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3425,24 +3567,26 @@ sp_proc_stmt_open: } ; -sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor($3, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); - i= new (thd->mem_root) - sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_add_cfetch(thd, $2)) MYSQL_YYABORT; } - sp_fetch_list - {} + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3463,12 +3607,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -4490,7 +4628,7 @@ create_body: if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ - src_table->required_type= FRMTYPE_TABLE; + src_table->required_type= TABLE_TYPE_NORMAL; } ; @@ -4940,7 +5078,7 @@ opt_part_values: ; part_func_max: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; @@ -5054,7 +5192,7 @@ part_value_item_list: ; part_value_expr_item: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; if (part_info->part_type == LIST_PARTITION) @@ -5579,6 +5717,11 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } ; default_charset: @@ -6939,7 +7082,7 @@ alter: ALTER { Lex->name= null_lex_str; - Lex->only_view= FALSE; + Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; Lex->duplicates= DUP_ERROR; Lex->select_lex.init_order(); @@ -7815,7 +7958,9 @@ opt_checksum_type: repair_table_or_view: table_or_tables table_list opt_mi_repair_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type ; repair: @@ -7980,7 +8125,9 @@ binlog_base64_event: check_view_or_table: table_or_tables table_list opt_mi_check_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type ; check: CHECK_SYM @@ -9292,6 +9439,50 @@ column_default_non_parenthesized_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } ; simple_expr: @@ -11811,8 +12002,6 @@ delete_limit_clause: int_num: NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - ; ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } @@ -11831,6 +12020,13 @@ real_ulong_num: | dec_num_error { MYSQL_YYABORT; } ; +longlong_num: + NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + 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); } @@ -12145,6 +12341,17 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12363,6 +12570,16 @@ opt_equal: | equal {} ; +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + no_braces: '(' { @@ -12916,7 +13133,15 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; - lex->only_view= 1; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; } | MASTER_SYM STATUS_SYM { @@ -13208,8 +13433,10 @@ opt_flush_lock: for (; tables; tables= tables->next_global) { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; } } ; @@ -14650,6 +14877,7 @@ keyword_sp_not_data_type: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} | DAY_SYM {} @@ -14698,6 +14926,7 @@ keyword_sp_not_data_type: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14713,6 +14942,7 @@ keyword_sp_not_data_type: | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} @@ -14755,6 +14985,7 @@ keyword_sp_not_data_type: | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} @@ -14765,7 +14996,12 @@ keyword_sp_not_data_type: | NAME_SYM {} | NAMES_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} @@ -14788,6 +15024,7 @@ keyword_sp_not_data_type: | PLUGINS_SYM {} | PRESERVE_SYM {} | PREV_SYM {} + | PREVIOUS_SYM {} | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} @@ -14828,6 +15065,7 @@ keyword_sp_not_data_type: | SCHEDULE_SYM {} | SCHEMA_NAME_SYM {} | SECOND_SYM {} + | SEQUENCE_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} | SIMPLE_SYM {} diff --git a/sql/table.cc b/sql/table.cc index ee592f120d3..fae17fd8f20 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -42,6 +42,7 @@ #include "sql_view.h" #include "rpl_filter.h" #include "sql_cte.h" +#include "ha_sequence.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -431,6 +432,7 @@ void TABLE_SHARE::destroy() ha_share= NULL; // Safety } + delete sequence; free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; stats_cb.stats_is_read= FALSE; @@ -1327,6 +1329,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->db_create_options= db_create_options= uint2korr(frm_image+30); share->db_options_in_use= share->db_create_options; share->mysql_version= uint4korr(frm_image+51); + share->table_type= TABLE_TYPE_NORMAL; share->null_field_first= 0; if (!frm_image[32]) // New frm file in 3.23 { @@ -1340,6 +1343,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX); share->page_checksum= (ha_choice) enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX); + if (((ha_choice) enum_value_with_check(thd, share, "sequence", + (frm_image[39] >> 4) & 3, + HA_CHOICE_MAX)) == HA_CHOICE_YES) + { + share->table_type= TABLE_TYPE_SEQUENCE; + share->sequence= new (&share->mem_root) SEQUENCE(); + share->non_determinstic_insert= true; + } share->row_type= (enum row_type) enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); @@ -2534,7 +2545,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, HA_CREATE_INFO *create_info= &lex->create_info; // ... not CREATE TABLE - if (lex->sql_command != SQLCOM_CREATE_TABLE) + if (lex->sql_command != SQLCOM_CREATE_TABLE && + lex->sql_command != SQLCOM_CREATE_SEQUENCE) return 1; // ... create like if (lex->create_info.like()) @@ -2985,6 +2997,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!db_stat); } + if (share->sequence && outparam->file) + { + ha_sequence *file; + /* SEQUENCE table. Create a sequence handler over the original handler */ + if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share, + &outparam->mem_root))) + goto err; + file->register_original_handler(outparam->file); + outparam->file= file; + } + outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock= F_UNLCK; records=0; @@ -3679,7 +3702,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, create_info->default_table_charset->number : 0); fileinfo[38]= (uchar) csid; fileinfo[39]= (uchar) ((uint) create_info->transactional | - ((uint) create_info->page_checksum << 2)); + ((uint) create_info->page_checksum << 2) | + ((create_info->sequence ? HA_CHOICE_YES : 0) << 4)); fileinfo[40]= (uchar) create_info->row_type; /* Bytes 41-46 were for RAID support; now reused for other purposes */ fileinfo[41]= (uchar) (csid >> 8); @@ -3716,6 +3740,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; create_info->option_list= share->option_list; + create_info->sequence= MY_TEST(share->sequence); DBUG_VOID_RETURN; } diff --git a/sql/table.h b/sql/table.h index 4885196a817..94a0d483413 100644 --- a/sql/table.h +++ b/sql/table.h @@ -54,6 +54,7 @@ struct TDC_element; class Virtual_column_info; class Table_triggers_list; class TMP_TABLE_PARAM; +class SEQUENCE; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -642,6 +643,7 @@ struct TABLE_SHARE db_plugin ? plugin_hton(db_plugin) : NULL; } enum row_type row_type; /* How rows are stored */ + enum Table_type table_type; enum tmp_table_type tmp_table; /** Transactional or not. */ @@ -716,6 +718,9 @@ struct TABLE_SHARE */ const File_parser *view_def; + /* For sequence tables, the current sequence state */ + SEQUENCE *sequence; + /* Cache for row-based replication table share checks that does not need to be repeated. Possible values are: -1 when cache value is @@ -2058,8 +2063,8 @@ struct TABLE_LIST bool where_processed; /* TRUE <=> VIEW CHECK OPTION expression has been processed */ bool check_option_processed; - /* FRMTYPE_ERROR if any type is acceptable */ - enum frm_type_enum required_type; + /* TABLE_TYPE_UNKNOWN if any type is acceptable */ + Table_type required_type; handlerton *db_type; /* table_type for handler */ char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ /* @@ -2098,6 +2103,7 @@ struct TABLE_LIST bool merged_for_insert; /* TRUE <=> don't prepare this derived table/view as it should be merged.*/ bool skip_prepare_derived; + bool sequence; /* Part of NEXTVAL/CURVAL/LASTVAL */ /* Items created by create_view_field and collected to change them in case diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index c05fc632a94..0aa5396f35b 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -73,7 +73,9 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, if ((share= create_temporary_table(hton, frm, path, db, table_name))) { + open_options|= HA_OPEN_FOR_CREATE; table= open_temporary_table(share, table_name, open_in_engine); + open_options&= ~HA_OPEN_FOR_CREATE; /* Failed to open a temporary table instance. As we are not passing @@ -1117,8 +1119,10 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, if (open_table_from_share(this, share, alias, open_in_engine ? (uint)HA_OPEN_KEYFILE : 0, - EXTRA_RECORD, ha_open_options, table, - open_in_engine ? false : true)) + EXTRA_RECORD, + (ha_open_options | + (open_options & HA_OPEN_FOR_CREATE)), + table, open_in_engine ? false : true)) { my_free(table); DBUG_RETURN(NULL); @@ -1126,7 +1130,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, table->reginfo.lock_type= TL_WRITE; /* Simulate locked */ table->grant.privilege= TMP_TABLE_ACLS; - share->tmp_table= (table->file->has_transactions() ? + share->tmp_table= (table->file->has_transaction_manager() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); table->pos_in_table_list= 0; diff --git a/sql/udf_example.c b/sql/udf_example.c index a48801d1c4a..6a0d78078a6 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -56,7 +56,7 @@ ** ** Function 'myfunc_int' returns summary length of all its arguments. ** -** Function 'sequence' returns an sequence starting from a certain number. +** Function 'udf_sequence' returns an sequence starting from a certain number. ** ** Function 'myfunc_argument_name' returns name of argument. ** @@ -80,7 +80,7 @@ ** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; -** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so"; +** CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "udf_example.so"; ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; @@ -162,9 +162,9 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); - void sequence_deinit(UDF_INIT *initid); -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); + void udf_sequence_deinit(UDF_INIT *initid); +longlong udf_sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); void avgcost_deinit( UDF_INIT* initid ); @@ -642,7 +642,7 @@ my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), or 1 if no arguments have been given */ -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if (args->arg_count > 1) { @@ -659,22 +659,22 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } bzero(initid->ptr,sizeof(longlong)); /* - sequence() is a non-deterministic function : it has different value + udf_sequence() is a non-deterministic function : it has different value even if called with the same arguments. */ initid->const_item=0; return 0; } -void sequence_deinit(UDF_INIT *initid) +void udf_sequence_deinit(UDF_INIT *initid) { if (initid->ptr) free(initid->ptr); } -longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, - char *is_null __attribute__((unused)), - char *error __attribute__((unused))) +longlong udf_sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) { ulonglong val=0; if (args->arg_count) diff --git a/sql/udf_example.def b/sql/udf_example.def index 41150b24e8f..74230b638bf 100644 --- a/sql/udf_example.def +++ b/sql/udf_example.def @@ -14,9 +14,9 @@ EXPORTS myfunc_double myfunc_int_init myfunc_int - sequence_init - sequence_deinit - sequence + udf_sequence_init + udf_sequence_deinit + udf_sequence avgcost_init avgcost_deinit avgcost_reset diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index 79dad571585..0ccd533eac1 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -95,7 +95,8 @@ void wsrep_register_hton(THD* thd, bool all) * replicated unless we declare wsrep hton as read/write here */ if (i->is_trx_read_write() || - (thd->lex->sql_command == SQLCOM_CREATE_TABLE && + ((thd->lex->sql_command == SQLCOM_CREATE_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) && thd->wsrep_exec_mode == LOCAL_STATE)) { thd->ha_data[wsrep_hton->slot].ha_info[all].set_trx_read_write(); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index c9dc31105c1..bbeb56d9b21 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1781,7 +1781,8 @@ bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx, /* Print some debug information. */ if (wsrep_debug) { - if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE) + if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE || + request_thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) { WSREP_DEBUG("DROP caused BF abort"); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 6aacdd48ad8..bb8f4c6ff41 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3200,6 +3200,7 @@ ha_innobase::ha_innobase( */ | HA_CAN_EXPORT | HA_CAN_RTREEKEYS + | HA_CAN_TABLES_WITHOUT_ROLLBACK | HA_CONCURRENT_OPTIMIZE | (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0) ), @@ -17365,6 +17366,8 @@ ha_innobase::store_lock( && lock_type <= TL_WRITE)) || sql_command == SQLCOM_CREATE_INDEX || sql_command == SQLCOM_DROP_INDEX + || sql_command == SQLCOM_CREATE_SEQUENCE + || sql_command == SQLCOM_DROP_SEQUENCE || sql_command == SQLCOM_DELETE)) { ib_senderrf(trx->mysql_thd, @@ -17394,7 +17397,8 @@ ha_innobase::store_lock( } /* Check for DROP TABLE */ - } else if (sql_command == SQLCOM_DROP_TABLE) { + } else if (sql_command == SQLCOM_DROP_TABLE || + sql_command == SQLCOM_DROP_SEQUENCE) { /* MySQL calls this function in DROP TABLE though this table handle may belong to another thd that is running a query. Let @@ -17438,6 +17442,7 @@ ha_innobase::store_lock( && (sql_command == SQLCOM_INSERT_SELECT || sql_command == SQLCOM_REPLACE_SELECT || sql_command == SQLCOM_UPDATE + || sql_command == SQLCOM_CREATE_SEQUENCE || sql_command == SQLCOM_CREATE_TABLE))) { /* If we either have innobase_locks_unsafe_for_binlog diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 557da9ad2bb..86bb49c5625 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -987,7 +987,8 @@ int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | HA_FILE_BASED | HA_CAN_GEOMETRY | CANNOT_ROLLBACK_FLAG | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | HA_CAN_REPAIR | HA_CAN_VIRTUAL_COLUMNS | - HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT), + HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | + HA_CAN_TABLES_WITHOUT_ROLLBACK), can_enable_indexes(1), bulk_insert_single_undo(BULK_INSERT_NONE) {} @@ -3099,6 +3100,13 @@ int ha_maria::create(const char *name, register TABLE *table_arg, ER_ILLEGAL_HA_CREATE_OPTION, "Row format set to PAGE because of TRANSACTIONAL=1 option"); + if (share->table_type == TABLE_TYPE_SEQUENCE) + { + /* For sequences, the simples record type is appropriate */ + row_type= STATIC_RECORD; + ha_create_info->transactional= HA_CHOICE_NO; + } + bzero((char*) &create_info, sizeof(create_info)); if ((error= table2maria(table_arg, row_type, &keydef, &recinfo, &record_count, &create_info))) diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 2b5536a9ce5..9a0d3ab7b5f 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -693,7 +693,8 @@ ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg) HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS | HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | - HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR), + HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR | + HA_CAN_TABLES_WITHOUT_ROLLBACK), can_enable_indexes(1) {} diff --git a/storage/rocksdb/rdb_datadic.cc b/storage/rocksdb/rdb_datadic.cc index 255a54cbdce..fd5424c2c23 100644 --- a/storage/rocksdb/rdb_datadic.cc +++ b/storage/rocksdb/rdb_datadic.cc @@ -2834,15 +2834,15 @@ bool Rdb_validate_tbls::check_frm_file(const std::string &fullpath, */ char eng_type_buf[NAME_CHAR_LEN+1]; LEX_STRING eng_type_str = {eng_type_buf, 0}; - //enum legacy_db_type eng_type; - frm_type_enum type = dd_frm_type(nullptr, fullfilename.c_ptr(), &eng_type_str); - if (type == FRMTYPE_ERROR) { + bool is_sequence; + enum Table_type type = dd_frm_type(nullptr, fullfilename.c_ptr(), &eng_type_str, &is_sequence); + if (type == TABLE_TYPE_UNKNOWN) { sql_print_warning("RocksDB: Failed to open/read .from file: %s", fullfilename.ptr()); return false; } - if (type == FRMTYPE_TABLE) { + if (type == TABLE_TYPE_NORMAL) { /* For a RocksDB table do we have a reference in the data dictionary? */ if (!strncmp(eng_type_str.str, "ROCKSDB", eng_type_str.length)) { /* diff --git a/storage/sequence/sequence.cc b/storage/sequence/sequence.cc index 599374c6f8c..ad617c79a5a 100644 --- a/storage/sequence/sequence.cc +++ b/storage/sequence/sequence.cc @@ -29,7 +29,7 @@ #include <table.h> #include <field.h> -handlerton *sequence_hton; +static handlerton *sequence_hton; class Sequence_share : public Handler_share { public: |