diff options
| author | Monty <monty@mariadb.org> | 2017-03-23 09:11:49 +0200 |
|---|---|---|
| committer | Monty <monty@mariadb.org> | 2017-03-23 09:11:49 +0200 |
| commit | f9d67a39f3b8f8ebb0248d06c170cc1cb2c1f1a9 (patch) | |
| tree | 4c1840264637017816a8fcb33c8db2dd21e826f9 | |
| parent | 9111639ca5ab218f5a5044378ba0931ea66cb17e (diff) | |
| download | mariadb-git-f9d67a39f3b8f8ebb0248d06c170cc1cb2c1f1a9.tar.gz | |
MDEV-10139 Support for SEQUENCE objects
Temporary push for marko to look at
Working features:
CREATE [ TEMPORARY | TEMP ] SEQUENCE name [ INCREMENT [ BY ] increment ]
[ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
[ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ] ENGINE=xxx
SELECT NEXT VALUE FOR sequence_name;
SHOW CREATE SEQUENCE sequence_name;
SHOW CREATE TABLE sequence_name;
CREATE TABLE sequence-structure ... SEQUENCE=1
Missing features
- Adding support for sequence for InnoDB (HA_CREATE_NO_ROLLBACK)
One can check if table is a sequence in create with:
if (share->table_type == TABLE_TYPE_SEQUENCE)
Note that sequences are initialized with an ha_write but all updates
from NEXT VALUE FOR sequences comes trough the new handler call
update_first_row().
The user can change the sequence values with INSERT or UPDATE and
later with ALTER SEQUENCE.
- LASTVAL
- SETVAL(),to be used with replication.
- ALTER SEQUENCE. ANSI SQL version of setval.
- Share identical sequence entries to not add things twice to table list
- testing insert/delete/update/truncate/load data
- Running alibaba sequence test
- Write documentation
- Write differences compared to alibaba patch
- NEXTVAL in DEFAULT
- Ensure that NEXTVAL in DEFAULT uses database from base table
- Two NEXTVAL for same row should give same answer.
- Oracle syntax squence_table.nextval, without any FOR or FROM.
- DROP SEQUENCE should only drop sequences, not tables.
70 files changed, 6658 insertions, 322 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 eb96c98c507..f85295b68ee 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/r/sequence.result b/mysql-test/r/sequence.result new file mode 100644 index 00000000000..eed4c130a9b --- /dev/null +++ b/mysql-test/r/sequence.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/r/sequence_create.result b/mysql-test/r/sequence_create.result new file mode 100644 index 00000000000..63c14dc5863 --- /dev/null +++ b/mysql-test/r/sequence_create.result @@ -0,0 +1,383 @@ +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 table 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 +10 1 9223372036854775806 10 1 1000 0 0 +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 +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 table t1; +create table t1 (a int); +show create sequence t1; +ERROR HY000: 'test.t1' is not SEQUENCE +drop sequence t1; +drop table if exists t1; +Warnings: +Note 1051 Unknown table '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= 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 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' +drop 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 +) 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 +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 +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 +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 +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 +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 +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 +drop table if exists t1; +Warnings: +Note 1051 Unknown table 'test.t1' +create sequence t1; +create sequence t2; +drop sequence if exists t1,t2,t3; +Warnings: +Note 1051 Unknown table 'test.t3' +create table t1 (a int) engine=sql_sequence; +ERROR 42000: Unknown storage engine 'sql_sequence' diff --git a/mysql-test/r/sequence_gtid.result b/mysql-test/r/sequence_gtid.result new file mode 100644 index 00000000000..f9a2d8ab60a --- /dev/null +++ b/mysql-test/r/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/r/sequence_next.result b/mysql-test/r/sequence_next.result new file mode 100644 index 00000000000..6e6c60f7713 --- /dev/null +++ b/mysql-test/r/sequence_next.result @@ -0,0 +1,175 @@ +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 +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 +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; +ERROR HY000: Sequence 'test.t1' has run out +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +drop sequence t1; +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 +flush tables; +select next value for t1; +next value for t1 +11 +drop sequence t1; +create table t1 (a int); +select next value for t1; +ERROR HY000: 'test.t1' is not a SEQUENCE +drop table t1; diff --git a/mysql-test/r/sequence_read_only.result b/mysql-test/r/sequence_read_only.result new file mode 100644 index 00000000000..0c9a75a616f --- /dev/null +++ b/mysql-test/r/sequence_read_only.result @@ -0,0 +1,61 @@ +use test; +create sequence s cache 2; +########################################### +sequence write error. +########################################### +use test; +SET GLOBAL DEBUG='d,sequence_write_error'; +create sequence s1 cache 2; +ERROR HY000: Sequence 'test.s1' access error. +select * 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 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +drop sequence s1; +SET GLOBAL DEBUG=''; +########################################### +sequence engine error. +########################################### +SET GLOBAL DEBUG='d,sequence_engine_error'; +create sequence s1 cache 2; +ERROR 42000: Unknown storage engine 'InnoDB' +########################################### +sequence handler when create sequence. +########################################### +SET GLOBAL DEBUG='d,sequence_handler_error'; +create sequence s1 cache 2; +ERROR HY000: Sequence '(null).(null)' access error. +select * for s; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +flush tables; +select * for s; +ERROR HY000: Sequence '(null).(null)' access error. +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 s1 cache 5; +ERROR HY000: InnoDB is in read only mode. +########################################### +read_only query error. +########################################### +select * for s; +ERROR HY000: Table 's' is read only +select * for s; +ERROR HY000: Table 's' is read only +select * for s; +ERROR HY000: Table 's' is read only 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 67b66819d92..0e4b36cc17e 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 191 +GLOBAL_VALUE 193 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 191 +DEFAULT_VALUE 193 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Maximum number of statement instruments. diff --git a/mysql-test/t/sequence-master.opt b/mysql-test/t/sequence-master.opt new file mode 100644 index 00000000000..bbea8eabc91 --- /dev/null +++ b/mysql-test/t/sequence-master.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 diff --git a/mysql-test/t/sequence-slave.opt b/mysql-test/t/sequence-slave.opt new file mode 100644 index 00000000000..a4e068e4b43 --- /dev/null +++ b/mysql-test/t/sequence-slave.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 --read_only=true diff --git a/mysql-test/t/sequence.test b/mysql-test/t/sequence.test new file mode 100644 index 00000000000..95bfcae9535 --- /dev/null +++ b/mysql-test/t/sequence.test @@ -0,0 +1,950 @@ +--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 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 engine=sequence; + +CREATE TABLE `s2` ( + `next_val` 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` tinyint(1) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB engine=sequence; + +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; + +--error ER_SEQUENCE_INVALID +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 ER_SEQUENCE_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 ER_SEQUENCE_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; + +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; +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 nextval for s2; +commit; + +select * for s2; + +--error ER_SYNTAX_ERROR +select * for t2; + +select * from s2, t2; + +--error 1064 +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; + +--error ER_ILLEGAL_HA_CREATE_OPTION +alter table s2 rename to s2_1; + +rename table s2 to s2_1; +select * for s2_1; + +--error ER_ILLEGAL_HA +truncate table s2_1; +rename table s2_1 to s2; +drop sequence s2; + + +--echo ########################################### +--echo not support create temproary sequence. +--echo ########################################### +connection master; + +--error 1064 +create 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 +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +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 * for s1; + +unlock tables; + +drop sequence s_db.s1; +--echo ########################################### +--echo session setting +--echo ########################################### +connection master; +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +select nextval for s1; +select * for s1; +select nextval for s1; + +set session sequence_read_skip_cache=false; +select * for s1; +select nextval for s1; +select * for s1; +select nextval for s1; + +drop sequence 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 * for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select * 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 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 ER_SEQUENCE_RUN_OUT +insert into t select nextval for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select nextval 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 * for s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select * 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 nextval for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select nextval from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select nextval for s_t; +update s_t set nextval= 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; +set session sequence_read_skip_cache=off; +select * 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 nextval for s_t; +update s_t set nextval= 11,start=10, minvalue=11; +commit; + +create table t_1(id int); +insert into t_1 value(1111); +--error ER_SEQUENCE_INVALID +select nextval for s_t; +--error ER_SEQUENCE_INVALID +insert into t_1 select nextval for s_t; +commit; + +select * from t_1; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +delete from s_t; +commit; + +select nextval 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; +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; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 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; +rollback; + +select * from t_1; +select nextval for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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; +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; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 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; +rollback; + +select * from t_1; +select nextval for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 nextval for s1; +select nextval for s1; +select nextval for s1; +select nextval 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 nextval for s1; +select nextval for s1; +select nextval for s1; +select nextval 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 sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_SEQUENCE_BINLOG_FORMAT +select nextval for s1; + +set session binlog_format=row; +select * for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; +set session sequence_read_skip_cache=off; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ------------------------------------------ +--echo set binlog_format=mixed +--echo ------------------------------------------ +connection master; +set session sequence_read_skip_cache=off; +set session binlog_format=mixed; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_SEQUENCE_BINLOG_FORMAT +select nextval for s1; + +set session binlog_format=row; +select * for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; +set session sequence_read_skip_cache=off; + +connection m_normal_1; +use s_db; +drop sequence s1; +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +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; +rollback to sp1; +select * from t1; +select nextval for s1; + +commit; + +drop sequence s1; +drop table t1; + +--echo ########################################### +--echo create as +--echo ########################################### +connection m_normal_1; +set session sequence_read_skip_cache=off; + +create sequence s1 cache 2; +create table t as select * for s1; +select * from t; + +drop sequence s1; +drop table t; + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +set session sequence_read_skip_cache=off; +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 nextval 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; +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; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select nextval 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 nextval for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +set session sequence_read_skip_cache=off; +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 nextval for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select nextval 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 * for s1; +select * for s1; +select * for s1; +select * for s1; +select * for s1; +drop sequence s1; + +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 10 nocycle; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +--error ER_SEQUENCE_RUN_OUT +select nextval, round for s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 nocache cycle; +select * for s1; +select * for s1; +select * for s1; +select * for s1; +select * for s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 cache 2 nocycle; +select * for s1; +--error ER_SEQUENCE_RUN_OUT +select * for s1; +drop sequence s1; + +--echo ------------------------------------------ +--echo beyond ulonglong maxvalue +--echo ------------------------------------------ +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 1 cycle; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +drop sequence s1; + +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 10 cycle; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for s1; +select nextval, round for 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/t/sequence_create.test b/mysql-test/t/sequence_create.test new file mode 100644 index 00000000000..8496490e561 --- /dev/null +++ b/mysql-test/t/sequence_create.test @@ -0,0 +1,286 @@ +# +# 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 table 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 table t1; + + +# +# Wrong usage and arguments to create sequence +# + +create table t1 (a int); +--error ER_WRONG_OBJECT +show create sequence t1; +# Following should give error +drop sequence t1; +drop table 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= 10 maxvalue=13; +--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; +drop table 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 table if exists t1; + +# +# DROP SEQUENCE +# + +create sequence t1; +create sequence t2; +drop sequence if exists t1,t2,t3; + +# +# 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/t/sequence_gtid-master.opt b/mysql-test/t/sequence_gtid-master.opt new file mode 100644 index 00000000000..5d75b12ad28 --- /dev/null +++ b/mysql-test/t/sequence_gtid-master.opt @@ -0,0 +1,5 @@ +--binlog_format=row +--query_cache_type=1 +--gtid_mode=ON +--enforce-gtid-consistency=ON +--log-slave-updates diff --git a/mysql-test/t/sequence_gtid-slave.opt b/mysql-test/t/sequence_gtid-slave.opt new file mode 100644 index 00000000000..28b287b0c9e --- /dev/null +++ b/mysql-test/t/sequence_gtid-slave.opt @@ -0,0 +1,6 @@ +--binlog_format=row +--query_cache_type=1 +--read_only=true +--gtid_mode=ON +--enforce-gtid-consistency=ON +--log-slave-updates diff --git a/mysql-test/t/sequence_gtid.test b/mysql-test/t/sequence_gtid.test new file mode 100644 index 00000000000..b917ab63104 --- /dev/null +++ b/mysql-test/t/sequence_gtid.test @@ -0,0 +1,690 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.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_ILLEGAL_HA_CREATE_OPTION +create table t(id int)engine=sequence; + +create table t(id int)engine=innodb; + +--error ER_ILLEGAL_HA_CREATE_OPTION +alter table t engine=sequence; + +drop table t; +--echo ########################################### +--echo not support alter sequence table. +--echo ########################################### +connection master; + +create sequence s2; + +--error ER_ILLEGAL_HA_CREATE_OPTION +alter table s2 add id int; + +--error ER_ILLEGAL_HA_CREATE_OPTION +alter table s2 add index ind_x(start); +drop sequence s2; + +--echo ########################################### +--echo not support create temproary sequence. +--echo ########################################### +connection master; + +--error 1064 +create 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 +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID +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 * for s1; + +unlock tables; + +drop sequence s_db.s1; +--echo ########################################### +--echo session setting +--echo ########################################### +connection master; +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +select nextval for s1; +select * for s1; +select nextval for s1; + +set session sequence_read_skip_cache=false; +select * for s1; +select nextval for s1; +select * for s1; +select nextval for s1; + +drop sequence 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 * for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select * 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 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 ER_SEQUENCE_RUN_OUT +insert into t select nextval for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select nextval 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 * for s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select * 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 nextval for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select nextval from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select nextval for s_t; +update s_t set nextval= 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; +set session sequence_read_skip_cache=off; +select * 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 nextval for s_t; +update s_t set nextval= 11,start=10, minvalue=11; +commit; + +create table t_1(id int); +insert into t_1 value(1111); +--error ER_SEQUENCE_INVALID +select nextval for s_t; +--error ER_SEQUENCE_INVALID +insert into t_1 select nextval for s_t; +commit; + +select * from t_1; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +delete from s_t; +commit; + +select nextval 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; +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; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 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; +rollback; + +select * from t_1; +select nextval for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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; +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; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 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; +rollback; + +select * from t_1; +select nextval for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +set session sequence_read_skip_cache=off; +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 nextval for s1; +select nextval for s1; +select nextval for s1; +select nextval 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 nextval for s1; +select nextval for s1; +select nextval for s1; +select nextval 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 sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_SEQUENCE_BINLOG_FORMAT +select nextval for s1; + +set session binlog_format=row; +select * for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; +set session sequence_read_skip_cache=off; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +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; +rollback to sp1; +select * from t1; +select nextval for s1; + +commit; + +drop sequence s1; +drop table t1; + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +set session sequence_read_skip_cache=off; +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 nextval 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; +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; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select nextval 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 nextval for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +set session sequence_read_skip_cache=off; +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 nextval for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select nextval 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/t/sequence_next.test b/mysql-test/t/sequence_next.test new file mode 100644 index 00000000000..716a47dea86 --- /dev/null +++ b/mysql-test/t/sequence_next.test @@ -0,0 +1,79 @@ +# +# 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; +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; +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; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +drop sequence t1; + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 100 increment by 1 cache 10; +select next value for t1; +flush tables; +select next value for t1; +drop sequence t1; + +# +# Some error testing +# +create table t1 (a int); +--error ER_NOT_SEQUENCE +select next value for t1; +drop table t1; diff --git a/mysql-test/t/sequence_read_only.test b/mysql-test/t/sequence_read_only.test new file mode 100644 index 00000000000..7feb3c36a65 --- /dev/null +++ b/mysql-test/t/sequence_read_only.test @@ -0,0 +1,81 @@ +connection default; +use test; +create sequence s cache 2; + + +--echo ########################################### +--echo sequence write error. +--echo ########################################### +use test; +SET GLOBAL DEBUG='d,sequence_write_error'; + +--error ER_SEQUENCE_ACCESS_ERROR +create sequence s1 cache 2; + +--error ER_SEQUENCE_BINLOG_FORMAT +select * for s1; + +set session binlog_format= row; +select * for s1; +select * for s1; +select * for s1; + +drop sequence s1; +SET GLOBAL DEBUG=''; + +--echo ########################################### +--echo sequence engine error. +--echo ########################################### +SET GLOBAL DEBUG='d,sequence_engine_error'; +--error ER_UNKNOWN_STORAGE_ENGINE +create sequence s1 cache 2; + + +--echo ########################################### +--echo sequence handler when create sequence. +--echo ########################################### +SET GLOBAL DEBUG='d,sequence_handler_error'; +--error ER_SEQUENCE_ACCESS_ERROR +create sequence s1 cache 2; + +select * for s; + +flush tables; + +--error ER_SEQUENCE_ACCESS_ERROR +select * for s; + + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--exec $MYSQLADMIN -u root -S $MASTER_MYSOCK -P $MASTER_MYPORT shutdown +--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_INNODB_READ_ONLY +create sequence s1 cache 5; + +--echo ########################################### +--echo read_only query error. +--echo ########################################### +--error ER_OPEN_AS_READONLY +select * for s; +--error ER_OPEN_AS_READONLY +select * for s; +--error ER_OPEN_AS_READONLY +select * for s; + 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 314208b4a7a..414f6a064f7 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 7edc2cb95f2..c76a687b25f 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 c1e1e85c377..c11cbb7e161 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -43,39 +43,41 @@ static int read_string(File file, uchar**to, size_t length) @param[in] path path to FRM file. @param[out] dbt db_type of the table if FRMTYPE_TABLE, otherwise undefined. - @retval FRMTYPE_ERROR error - @retval FRMTYPE_TABLE table - @retval FRMTYPE_VIEW view + @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, enum legacy_db_type *dbt) +Table_type dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt) { File file; uchar header[10]; //"TYPE=VIEW\n" it is 10 characters size_t error; - frm_type_enum type= FRMTYPE_ERROR; + Table_type type= TABLE_TYPE_UNKNOWN; DBUG_ENTER("dd_frm_type"); *dbt= DB_TYPE_UNKNOWN; - if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0) - DBUG_RETURN(FRMTYPE_ERROR); + 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))) { - type= FRMTYPE_VIEW; + type= TABLE_TYPE_VIEW; goto err; } - type= FRMTYPE_TABLE; + type= TABLE_TYPE_NORMAL; /* This is just a check for DB_TYPE. We'll return default unknown type if the following test is true (arg #3). This should not have effect - on return value from this function (default FRMTYPE_TABLE) + on return value from this function (default TABLE_TYPE_NORMAL) */ if (!is_binary_frm_header(header)) goto err; diff --git a/sql/datadict.h b/sql/datadict.h index dd80942daca..e8ba21b6042 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,12 +37,12 @@ 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, enum legacy_db_type *dbt); +enum Table_type dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt); static inline bool dd_frm_is_view(THD *thd, char *path) { enum legacy_db_type not_used; - return dd_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW; + return dd_frm_type(thd, path, ¬_used) == 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 d1a5a7bb0ea..53f75de3dec 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..3eef0ce8455 --- /dev/null +++ b/sql/ha_sequence.cc @@ -0,0 +1,398 @@ +/* + 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) +#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; + error= file->open(name, mode, flags); + if (!error) + { + /* 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(); + } + } + /* Reuse original storage engine data for duplicate key reference */ + ref= file->ref; + ref_length= file->ref_length; + dup_ref= file->dup_ref; + 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"); + + row_already_logged= 0; + + /* Point fields to new_data */ + table->move_fields(table->field, table->record[1], table->record[0]); + tmp_seq.read_fields(table); + table->move_fields(table->field, table->record[0], table->record[1]); + 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->ha_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; + } + } + 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..e2b366fa6e9 --- /dev/null +++ b/sql/ha_sequence.h @@ -0,0 +1,135 @@ +#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_UNSUPPORTED; } + /* One can't delete from sequence engine */ + int truncate() + { return HA_ERR_UNSUPPORTED; } + /* 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; } + + /* 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 fb5d1699419..90d338c74fd 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" @@ -618,7 +619,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 +631,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; }; @@ -2542,6 +2546,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); } @@ -2790,10 +2795,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) { @@ -5057,7 +5063,7 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, if (hton) { enum legacy_db_type db_type; - if (dd_frm_type(thd, path, &db_type) != FRMTYPE_VIEW) + if (dd_frm_type(thd, path, &db_type) != TABLE_TYPE_VIEW) { handlerton *ht= ha_resolve_by_legacy_type(thd, db_type); if ((*hton= ht)) @@ -5769,8 +5775,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, @@ -5811,10 +5815,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; @@ -5966,7 +5970,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); @@ -5998,7 +6002,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); @@ -6006,6 +6010,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 98bfbb93dc9..d4bdc4742aa 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,15 @@ 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) + +/* Set of all binlog flags. Currently only contain the capabilities flags. */ @@ -380,6 +391,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 +446,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 +535,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 +1699,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 +1713,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 +2663,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 +2767,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), @@ -3905,7 +3925,7 @@ public: return 0; } - LEX_STRING *engine_name() { return hton_name(ht); } + virtual LEX_STRING *engine_name() { return hton_name(ht); } TABLE* get_table() { return table; } TABLE_SHARE* get_table_share() { return table_share; } @@ -3991,6 +4011,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; @@ -4053,6 +4079,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 @@ -4341,6 +4368,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 f0cf550cbe9..aaa4abdf3a4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6945,3 +6945,65 @@ 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; + TABLE *table= table_list->table; + DBUG_ASSERT(table && table->s->sequence); + null_value= 0; + value= table->s->sequence->next_value(table,0, &error); + if (error) // Warning already printed + null_value= 1; // For not strict mode + 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(')'); +} + +longlong Item_func_lastval::val_int() +{ + return 0; +} diff --git a/sql/item_func.h b/sql/item_func.h index ea738a5097e..75276dd090f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2753,6 +2753,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 2f60a8eed72..7873f515067 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -161,6 +161,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)}, @@ -288,6 +289,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)}, @@ -325,6 +327,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)}, @@ -394,6 +397,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)}, @@ -413,7 +417,10 @@ static SYMBOL symbols[] = { { "NCHAR", SYM(NCHAR_SYM)}, { "NEW", SYM(NEW_SYM)}, { "NEXT", SYM(NEXT_SYM)}, + { "NEXTVAL", SYM(NEXTVAL_SYM)}, { "NO", SYM(NO_SYM)}, + { "NOCACHE", SYM(NOCACHE_SYM)}, + { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, @@ -547,6 +554,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..a25670ba444 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -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 69d0f52d211..5098a5a1a07 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4076,10 +4076,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 @@ -5319,7 +5321,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) @@ -11980,7 +11983,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/mysqld.cc b/sql/mysqld.cc index 9d55b20be76..0544c5fdcba 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -916,7 +916,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, @@ -1000,6 +1001,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} @@ -3797,6 +3799,7 @@ SHOW_VAR com_status_vars[]= { {"create_package_body", STMT_STATUS(SQLCOM_CREATE_PACKAGE_BODY)}, {"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)}, @@ -3817,6 +3820,7 @@ SHOW_VAR com_status_vars[]= { {"drop_package_body", STMT_STATUS(SQLCOM_DROP_PACKAGE_BODY)}, {"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 7e0a95ddab4..a2080e92195 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 38d75d90025..44e1e22dfea 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7461,3 +7461,15 @@ ER_END_IDENTIFIER_DOES_NOT_MATCH eng "END identifier '%-.192s' does not match '%-.192s'" ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD eng "Row variable '%-.192s' does not have a field '%-.192s'" +ER_SEQUENCE_RUN_OUT + eng "Sequence '%-.192s.%-.192s' has run out" +ER_SEQUENCE_INVALID_DATA + eng "Sequence '%-.192s.%-.192s' values are conflicting" +ER_SEQUENCE_INVALID_TABLE_STRUCTURE + eng "Sequence '%-.192s.%-.192s' table structure is invalid" +ER_SEQUENCE_ACCESS_ERROR + eng "Sequence '%-.192s.%-.192s' access error" +ER_SEQUENCE_BINLOG_FORMAT + eng "Sequences requires binlog_format mixed or row" +ER_NOT_SEQUENCE + eng "'%-.192s.%-.192s' is not a SEQUENCE" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index d0c03d16c7d..8896fb2ae6d 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 @@ -4434,7 +4436,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; @@ -4498,7 +4501,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_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 7aa4c45936a..c039a17a82b 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); 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.h b/sql/sql_class.h index a9f3e127413..974bf0e7e7d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -693,6 +693,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; @@ -5683,6 +5684,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 6003fd968c6..b020d598556 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -100,6 +100,8 @@ enum enum_sql_command { SQLCOM_DROP_PACKAGE, SQLCOM_CREATE_PACKAGE_BODY, SQLCOM_DROP_PACKAGE_BODY, + 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_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_lex.cc b/sql/sql_lex.cc index a1cb1467843..ad8eba310ec 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; @@ -3420,6 +3420,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; @@ -6974,3 +6975,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 fff016c5c96..2491a8ea528 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -217,6 +217,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; @@ -2621,6 +2622,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; @@ -2793,7 +2795,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 @@ -3522,6 +3523,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 3093853b840..f2593da7e44 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,21 +545,27 @@ 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_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_PACKAGE]= 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_CREATE_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; + sql_command_flags[SQLCOM_DROP_PACKAGE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; sql_command_flags[SQLCOM_CREATE_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_PACKAGE_BODY]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; @@ -725,6 +734,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; @@ -799,7 +809,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; @@ -817,8 +829,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; @@ -1415,7 +1429,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 @@ -1433,13 +1447,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); @@ -1447,31 +1460,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 || - lex->sql_command == SQLCOM_CREATE_PACKAGE || - lex->sql_command == SQLCOM_DROP_PACKAGE; + /* 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); @@ -3356,7 +3355,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)) { @@ -3972,6 +3972,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); @@ -4229,6 +4230,9 @@ mysql_execute_command(THD *thd) /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); } + if (!res && lex->create_info.sequence) + res= sequence_insert(thd, lex, create_table); + if (!res) { /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4432,9 +4436,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)) { @@ -4448,7 +4452,6 @@ end_with_restore_list: /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->open_type= OT_BASE_ONLY; - } else { @@ -4956,6 +4959,7 @@ end_with_restore_list: } break; } + case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4966,7 +4970,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 */ @@ -4998,8 +5002,10 @@ end_with_restore_list: /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); - /* 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); @@ -8216,6 +8222,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)) { diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 31d59ea47ef..aa4043bd2f9 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1658,9 +1658,9 @@ int plugin_init(int *argc, char **argv, int flags) char path[FN_REFLEN + 1]; build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0); enum legacy_db_type db_type; - frm_type_enum frm_type= dd_frm_type(NULL, path, &db_type); + Table_type frm_type= dd_frm_type(NULL, path, &db_type); /* if mysql.plugin table is MyISAM - load it right away */ - if (frm_type == FRMTYPE_TABLE && db_type == DB_TYPE_MYISAM) + if (frm_type == TABLE_TYPE_NORMAL && db_type == DB_TYPE_MYISAM) { plugin_load(&tmp_root); flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c8765f45273..58f4969b2d3 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 7d0356a6368..5437c9f8ffd 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..e93919e3b8e --- /dev/null +++ b/sql/sql_sequence.cc @@ -0,0 +1,584 @@ +/* + 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; +} + + +/* + 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); + + 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); +} + + +/* + 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; + DBUG_ENTER("check_sequence_fields"); + + field_count= fields->elements; + if (field_count != array_elements(sequence_structure)-1 || + lex->alter_info.key_list.elements > 0) + 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) + 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); + 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; + DBUG_ENTER("sequence_insert"); + + /* 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; + 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); + error= table->file->ha_write_row(table->record[0]); + reenable_binlog(thd); + 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); + 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; + + 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; + } + 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; + DBUG_ENTER("SEQUENCE::read_stored_values"); + mysql_mutex_assert_owner(&mutex); + + error= table->file->ha_read_first_row(table->record[0], MAX_KEY); + 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, add_to; + bool out_of_values; + 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; + + /* Out of cached values, reserve 'cache' new ones */ + add_to= real_increment * cache; // Checked on insert, can't overflow + 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= next_free_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= next_free_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)); + } + + /* Update table */ + store_fields(table); + ((ha_sequence*) table->file)->sequence_locked= 1; + if ((*error= table->file->ha_write_row(table->record[0]))) + table->file->print_error(*error, MYF(0)); + ((ha_sequence*) table->file)->sequence_locked= 0; + 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); +} diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h new file mode 100644 index 00000000000..3201b186100 --- /dev/null +++ b/sql/sql_sequence.h @@ -0,0 +1,102 @@ +/* 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); +}; + +/** + 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 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 70ac2b54e46..5d2153f230a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -130,6 +130,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 +1171,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 +1192,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 +1772,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 +1974,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 +1982,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 +1993,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 +2004,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 +2153,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 +2219,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 +2249,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)); - } - - /* 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); - } + add_table_options(thd, table, create_info_arg, + table_list->schema_table != 0, 0, packet); + #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && @@ -2423,6 +2462,56 @@ 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 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 728416c8df6..062436e34c9 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> @@ -3181,11 +3181,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; @@ -5542,7 +5557,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)) @@ -7439,6 +7454,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**) @@ -8477,7 +8495,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; @@ -9927,7 +9945,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_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..68a3bb1f3cd 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 diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 90da7034ae3..1591b07f441 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1153,7 +1153,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"); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b23bb0731cd..166d9951a36 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 @@ -994,6 +995,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 @@ -1132,6 +1134,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 @@ -1164,6 +1167,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 @@ -1235,6 +1239,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 @@ -1258,6 +1263,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 @@ -1402,6 +1410,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 */ @@ -1672,6 +1681,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> @@ -2431,6 +2443,64 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace 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, 0, $1 | $3)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $4, 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, + $4->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 @@ -2508,6 +2578,65 @@ 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; + } + + | MAX_VALUE_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 MAX_VALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | START_SYM 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 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 { @@ -3600,24 +3729,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; + } + | 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_fetch_list - {} + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3638,12 +3769,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -4464,7 +4589,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; } ; @@ -5553,6 +5678,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: @@ -6777,7 +6907,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(); @@ -7653,7 +7783,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: @@ -7818,7 +7950,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 @@ -9061,6 +9195,39 @@ 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; + } + | LASTVAL_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_lastval(thd, table))) + MYSQL_YYABORT; + } ; simple_expr: @@ -11550,8 +11717,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); } @@ -11570,6 +11735,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); } @@ -11886,6 +12058,16 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, 0, $3); + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12650,7 +12832,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 { @@ -12942,8 +13132,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; } } ; @@ -14241,6 +14433,7 @@ keyword_sp: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} | DATETIME {} @@ -14298,6 +14491,7 @@ keyword_sp: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14313,6 +14507,7 @@ keyword_sp: | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} @@ -14357,6 +14552,7 @@ keyword_sp: | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} @@ -14372,7 +14568,10 @@ keyword_sp: | NATIONAL_SYM {} | NCHAR_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} @@ -14443,6 +14642,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 2bf4996e4bb..9121481b2ce 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -410,6 +410,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 +549,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 +582,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 @@ -651,6 +654,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 +678,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 @@ -818,6 +825,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 */ @@ -4611,7 +4619,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; } ; @@ -7063,7 +7071,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(); @@ -7939,7 +7947,7 @@ 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: @@ -8104,7 +8112,7 @@ 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 @@ -13054,7 +13062,7 @@ 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; } | MASTER_SYM STATUS_SYM { @@ -13346,8 +13354,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; } } ; diff --git a/sql/table.cc b/sql/table.cc index 785b7c18d7f..bbdd263e704 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; @@ -1326,6 +1328,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 { @@ -1339,6 +1342,13 @@ 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->row_type= (enum row_type) enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); @@ -2529,7 +2539,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()) @@ -2984,6 +2995,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!db_stat); } + if (share->sequence) + { + ha_sequence *file; + /* SEQUENCE table. Create a sequence handler over the orignal 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; @@ -3678,7 +3700,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); 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/wsrep_hton.cc b/sql/wsrep_hton.cc index 2a93484002e..a17f0c908d3 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 b0275fc2fd5..2c7c589c146 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 5bcc695186e..93c15c74943 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3187,6 +3187,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) ), diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 6a84894d386..0fb42a12d8f 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 46a7734ef22..fb8988f67bb 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/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: |
