From 546e7aa96f13091dc32f87809e7b6ea19d20e1ad Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 7 Apr 2017 10:19:10 +0300 Subject: [PATCH 1/6] MDEV-8203 Assert in Query_log_event::do_apply_event() This happens because the master writes a table_map event to the binary log, but no row event. The slave has a check that there should always be a row event if there was a table_map event, which causes a crash. Fixed by remembering in the cache what kind of events are logged and ignore cached statements which is just a table map event. --- .../suite/rpl/r/rpl_trans_no_trans.result | 44 +++++++++++ .../suite/rpl/t/rpl_trans_no_trans.test | 72 +++++++++++++++++ sql/log.cc | 77 ++++++++++++++----- sql/log.h | 6 +- sql/log_event.cc | 2 +- sql/log_event.h | 32 +++++++- sql/wsrep_binlog.cc | 2 +- sql/wsrep_mysqld.cc | 2 +- 8 files changed, 209 insertions(+), 28 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_trans_no_trans.result create mode 100644 mysql-test/suite/rpl/t/rpl_trans_no_trans.test diff --git a/mysql-test/suite/rpl/r/rpl_trans_no_trans.result b/mysql-test/suite/rpl/r/rpl_trans_no_trans.result new file mode 100644 index 0000000000000..a7a6d921bc126 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_trans_no_trans.result @@ -0,0 +1,44 @@ +include/master-slave.inc +[connection master] +create or replace table t1(id int)engine=innodb; +create or replace table t3(id int)engine=myisam; +create or replace function t99 (a int) +returns int(10) +MODIFIES SQL DATA +begin +if (a > 100) +then +insert into t3 values (a); +end if; +return a; +end// +begin; +insert into t1 values(t99(1)); +insert into t1 values(t99(101)); +commit; +select * from t1; +id +1 +101 +select * from t3; +id +101 +insert into t1 values(t99(1)); +drop function t99; +drop table t1,t3; +connection slave; +connection master; +CREATE TABLE t1 (i INT) ENGINE=InnoDB; +CREATE TABLE t2 (j INT) ENGINE=MyISAM; +CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW +BEGIN +SET @a = unknown_column_just_to_raise_an_error; +INSERT INTO t2 VALUES (NULL) ; +END|| +INSERT INTO t1 VALUES (1); +ERROR 42S22: Unknown column 'unknown_column_just_to_raise_an_error' in 'field list' +connection slave; +connection master; +drop trigger tr; +drop table t1,t2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_trans_no_trans.test b/mysql-test/suite/rpl/t/rpl_trans_no_trans.test new file mode 100644 index 0000000000000..f6e3731dbf8ab --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_trans_no_trans.test @@ -0,0 +1,72 @@ +# +# Test mixing transactional and non transactional tables +# + +--source include/master-slave.inc +--source include/have_innodb.inc +--source include/have_binlog_format_row.inc + +# +# Test updating conditionally a non transactinal table in a function +# This verifies that we don't write only a table map for a non transactional, +# without any row events +# The original bug caused a crash on the slave when doing a sync_slave +# + +create or replace table t1(id int)engine=innodb; +create or replace table t3(id int)engine=myisam; + +delimiter //; +create or replace function t99 (a int) +returns int(10) +MODIFIES SQL DATA +begin + if (a > 100) + then + insert into t3 values (a); + end if; + return a; +end// +delimiter ;// +begin; +insert into t1 values(t99(1)); +insert into t1 values(t99(101)); +commit; +select * from t1; +select * from t3; +insert into t1 values(t99(1)); + +drop function t99; +drop table t1,t3; + +sync_slave_with_master; +connection master; + +# +# MDEV-8203 +# Assertion `!current_stmt_is_commit || !rgi->tables_to_lock' failed in +# Query_log_event::do_apply_event(rpl_group_info*, const char*, uint32) +# + +CREATE TABLE t1 (i INT) ENGINE=InnoDB; +CREATE TABLE t2 (j INT) ENGINE=MyISAM; + +--delimiter || +CREATE TRIGGER tr AFTER INSERT ON t1 FOR EACH ROW +BEGIN + SET @a = unknown_column_just_to_raise_an_error; + INSERT INTO t2 VALUES (NULL) ; +END|| +--delimiter ; + +--error ER_BAD_FIELD_ERROR +INSERT INTO t1 VALUES (1); +--sync_slave_with_master + +connection master; + +drop trigger tr; +drop table t1,t2; + +# End of 4.1 tests +--source include/rpl_end.inc diff --git a/sql/log.cc b/sql/log.cc index 64c7698e2a2a5..91da94af878ec 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -250,7 +250,8 @@ void make_default_log_name(char **out, const char* log_ext, bool once) class binlog_cache_data { public: - binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF), + binlog_cache_data(): m_pending(0), status(0), + before_stmt_pos(MY_OFF_T_UNDEF), incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE), saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0), ptr_binlog_cache_disk_use(0) @@ -262,9 +263,22 @@ class binlog_cache_data close_cached_file(&cache_log); } + /* + Return 1 if there is no relevant entries in the cache + + This is: + - Cache is empty + - There are row or critical (DDL?) events in the cache + + The status test is needed to avoid writing entries with only + a table map entry, which would crash in do_apply_event() on the slave + as it assumes that there is always a row entry after a table map. + */ bool empty() const { - return pending() == NULL && my_b_tell(&cache_log) == 0; + return (pending() == NULL && + (my_b_write_tell(&cache_log) == 0 || + ((status & (LOGGED_ROW_EVENT | LOGGED_CRITICAL)) == 0))); } Rows_log_event *pending() const @@ -305,6 +319,7 @@ class binlog_cache_data my_chsize(cache_log.file, 0, 0, MYF(MY_WME)); changes_to_non_trans_temp_table_flag= FALSE; + status= 0; incident= FALSE; before_stmt_pos= MY_OFF_T_UNDEF; /* @@ -376,6 +391,11 @@ class binlog_cache_data cache_log.end_of_file= saved_max_binlog_cache_size; } + void add_status(enum_logged_status status_arg) + { + status|= status_arg; + } + /* Cache to store data before copying it to the binary log. */ @@ -388,6 +408,13 @@ class binlog_cache_data */ Rows_log_event *m_pending; + /* + Bit flags for what has been writting to cache. Used to + discard logs without any data changes. + see enum_logged_status; + */ + uint32 status; + /* Binlog position before the start of the current statement. */ @@ -459,6 +486,13 @@ class binlog_cache_data binlog_cache_data(const binlog_cache_data& info); }; + +void Log_event_writer::add_status(enum_logged_status status) +{ + if (likely(cache_data)) + cache_data->add_status(status); +} + class binlog_cache_mngr { public: binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size, @@ -5207,12 +5241,14 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) DBUG_RETURN(error); } -bool MYSQL_BIN_LOG::write_event(Log_event *ev, IO_CACHE *file) +bool MYSQL_BIN_LOG::write_event(Log_event *ev, binlog_cache_data *cache_data, + IO_CACHE *file) { - Log_event_writer writer(file, &crypto); + Log_event_writer writer(file, 0, &crypto); if (crypto.scheme && file == &log_file) writer.ctx= alloca(crypto.ctx_size); - + if (cache_data) + cache_data->add_status(ev->logged_status()); return writer.write(ev); } @@ -5649,10 +5685,11 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, binlog_cache_mngr *const cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton); + binlog_cache_data *cache_data= (cache_mngr-> + get_binlog_cache_data(is_transactional)); + IO_CACHE *file= &cache_data->cache_log; + Log_event_writer writer(file, cache_data); - IO_CACHE *file= - cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional)); - Log_event_writer writer(file); if (with_annotate && *with_annotate) { Annotate_rows_log_event anno(table->in_use, is_transactional, false); @@ -5788,7 +5825,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, if (Rows_log_event* pending= cache_data->pending()) { - Log_event_writer writer(&cache_data->cache_log); + Log_event_writer writer(&cache_data->cache_log, cache_data); /* Write pending event to the cache. @@ -6200,8 +6237,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) goto err; is_trans_cache= use_trans_cache(thd, using_trans); - file= cache_mngr->get_binlog_cache_log(is_trans_cache); cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache); + file= &cache_data->cache_log; if (thd->lex->stmt_accessed_non_trans_temp_table()) cache_data->set_changes_to_non_trans_temp_table(); @@ -6225,21 +6262,19 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) Annotate_rows_log_event anno(thd, using_trans, direct); /* Annotate event should be written not more than once */ *with_annotate= 0; - if (write_event(&anno, file)) + if (write_event(&anno, cache_data, file)) goto err; } - if (thd) { if (!thd->is_current_stmt_binlog_format_row()) { - if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, thd->first_successful_insert_id_in_prev_stmt_for_binlog, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) @@ -6250,14 +6285,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT, thd->auto_inc_intervals_in_cur_stmt_for_binlog. minimum(), using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->rand_used) { Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->user_var_events.elements) @@ -6281,7 +6316,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) flags, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } } @@ -6291,7 +6326,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) /* Write the event. */ - if (write_event(event_info, file) || + if (write_event(event_info, cache_data, file) || DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0)) goto err; @@ -6676,7 +6711,8 @@ class CacheWriter: public Log_event_writer CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum, Binlog_crypt_data *cr) - : Log_event_writer(file_arg, cr), remains(0), thd(thd_arg), first(true) + : Log_event_writer(file_arg, 0, cr), remains(0), thd(thd_arg), + first(true) { checksum_len= do_checksum ? BINLOG_CHECKSUM_LEN : 0; } ~CacheWriter() @@ -6684,6 +6720,7 @@ class CacheWriter: public Log_event_writer int write(uchar* pos, size_t len) { + DBUG_ENTER("CacheWriter::write"); if (first) write_header(pos, len); else @@ -6692,7 +6729,7 @@ class CacheWriter: public Log_event_writer remains -= len; if ((first= !remains)) write_footer(); - return 0; + DBUG_RETURN(0); } private: THD *thd; diff --git a/sql/log.h b/sql/log.h index f1a025edfb9f5..ae8f8170a98b5 100644 --- a/sql/log.h +++ b/sql/log.h @@ -415,8 +415,10 @@ class MYSQL_QUERY_LOG: public MYSQL_LOG ( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID ) class binlog_cache_mngr; +class binlog_cache_data; struct rpl_gtid; struct wait_for_commit; + class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG { private: @@ -743,8 +745,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); - bool write_event(Log_event *ev, IO_CACHE *file); - bool write_event(Log_event *ev) { return write_event(ev, &log_file); } + bool write_event(Log_event *ev, binlog_cache_data *data, IO_CACHE *file); + bool write_event(Log_event *ev) { return write_event(ev, 0, &log_file); } bool write_event_buffer(uchar* buf,uint len); bool append(Log_event* ev); diff --git a/sql/log_event.cc b/sql/log_event.cc index 3aea3eaf2f1e0..a6f2de2c6c576 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -9292,7 +9292,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi) char *ext; int fd = -1; IO_CACHE file; - Log_event_writer lew(&file); + Log_event_writer lew(&file, 0); int error = 1; Relay_log_info const *rli= rgi->rli; diff --git a/sql/log_event.h b/sql/log_event.h index 5689d36aea8fb..2bc0a85854283 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -716,6 +716,21 @@ enum Log_event_type ENUM_END_EVENT /* end marker */ }; + +/* + Bit flags for what has been writting to cache. Used to + discard logs with table map events but not row events and + nothing else important. This is stored by cache. +*/ + +enum enum_logged_status +{ + LOGGED_TABLE_MAP= 1, + LOGGED_ROW_EVENT= 2, + LOGGED_NO_DATA= 4, + LOGGED_CRITICAL= 8 +}; + static inline bool LOG_EVENT_IS_QUERY(enum Log_event_type type) { return type == QUERY_EVENT || type == QUERY_COMPRESSED_EVENT; @@ -785,6 +800,7 @@ class THD; class Format_description_log_event; class Relay_log_info; +class binlog_cache_data; #ifdef MYSQL_CLIENT enum enum_base64_output_mode { @@ -896,6 +912,7 @@ typedef struct st_print_event_info This class encapsulates writing of Log_event objects to IO_CACHE. Automatically calculates the checksum and encrypts the data, if necessary. */ + class Log_event_writer { public: @@ -907,13 +924,16 @@ class Log_event_writer int write_data(const uchar *pos, size_t len); int write_footer(); my_off_t pos() { return my_b_safe_tell(file); } + void add_status(enum_logged_status status); -Log_event_writer(IO_CACHE *file_arg, Binlog_crypt_data *cr= 0) + Log_event_writer(IO_CACHE *file_arg, binlog_cache_data *cache_data_arg, + Binlog_crypt_data *cr= 0) : bytes_written(0), ctx(0), - file(file_arg), crypto(cr) { } + file(file_arg), cache_data(cache_data_arg), crypto(cr) { } private: IO_CACHE *file; + binlog_cache_data *cache_data; /** Placeholder for event checksum while writing to binlog. */ @@ -1236,7 +1256,7 @@ class Log_event bool is_more); void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more); -#endif +#endif /* MYSQL_SERVER */ /* The following code used for Flashback */ #ifdef MYSQL_CLIENT @@ -1382,6 +1402,7 @@ class Log_event } #endif virtual Log_event_type get_type_code() = 0; + virtual enum_logged_status logged_status() { return LOGGED_CRITICAL; } virtual bool is_valid() const = 0; virtual my_off_t get_header_len(my_off_t len) { return len; } void set_artificial_event() { flags |= LOG_EVENT_ARTIFICIAL_F; } @@ -3361,6 +3382,7 @@ class Gtid_log_event: public Log_event const Format_description_log_event *description_event); ~Gtid_log_event() { } Log_event_type get_type_code() { return GTID_EVENT; } + enum_logged_status logged_status() { return LOGGED_NO_DATA; } int get_data_size() { return GTID_HEADER_LEN + ((flags2 & FL_GROUP_COMMIT_ID) ? 2 : 0); @@ -3857,6 +3879,7 @@ class Annotate_rows_log_event: public Log_event virtual int get_data_size(); virtual Log_event_type get_type_code(); + enum_logged_status logged_status() { return LOGGED_NO_DATA; } virtual bool is_valid() const; virtual bool is_part_of_group() { return 1; } @@ -4271,6 +4294,7 @@ class Table_map_log_event : public Log_event const char *get_db_name() const { return m_dbnam; } virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; } + virtual enum_logged_status logged_status() { return LOGGED_TABLE_MAP; } virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ } virtual bool is_part_of_group() { return 1; } @@ -4398,6 +4422,7 @@ class Rows_log_event : public Log_event flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; } Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */ + enum_logged_status logged_status() { return LOGGED_ROW_EVENT; } virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */ #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -5160,6 +5185,7 @@ inline int Log_event_writer::write(Log_event *ev) ev->writer= this; int res= ev->write(); IF_DBUG(ev->writer= 0,); // writer must be set before every Log_event::write + add_status(ev->logged_status()); return res; } diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc index 08b7e3f1e3d8c..9820bf983caa5 100644 --- a/sql/wsrep_binlog.cc +++ b/sql/wsrep_binlog.cc @@ -447,7 +447,7 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf, char filename[PATH_MAX]= {0}; File file; IO_CACHE cache; - Log_event_writer writer(&cache); + Log_event_writer writer(&cache, 0); Format_description_log_event *ev; int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld_v2.log", diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 9a99dffc7248c..c9dc31105c126 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1245,7 +1245,7 @@ int wsrep_to_buf_helper( THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len) { IO_CACHE tmp_io_cache; - Log_event_writer writer(&tmp_io_cache); + Log_event_writer writer(&tmp_io_cache,0); if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX, 65536, MYF(MY_WME))) return 1; From 17a87d606302b55d547104d7fe6c536c6c288a8b Mon Sep 17 00:00:00 2001 From: Monty Date: Sat, 25 Mar 2017 23:36:56 +0200 Subject: [PATCH 2/6] MDEV-10139 Support for SEQUENCE objects Working features: CREATE OR REPLACE [TEMPORARY] SEQUENCE [IF NOT EXISTS] name [ INCREMENT [ BY | = ] increment ] [ MINVALUE [=] minvalue | NO MINVALUE ] [ MAXVALUE [=] maxvalue | NO MAXVALUE ] [ START [ WITH | = ] start ] [ CACHE [=] cache ] [ [ NO ] CYCLE ] ENGINE=xxx COMMENT=".." SELECT NEXT VALUE FOR sequence_name; SELECT NEXTVAL(sequence_name); SELECT PREVIOUS VALUE FOR sequence_name; SELECT LASTVAL(sequence_name); SHOW CREATE SEQUENCE sequence_name; SHOW CREATE TABLE sequence_name; CREATE TABLE sequence-structure ... SEQUENCE=1 ALTER TABLE sequence RENAME TO sequence2; RENAME TABLE sequence TO sequence2; DROP [TEMPORARY] SEQUENCE [IF EXISTS] sequence_names Missing features - SETVAL(value,sequence_name), to be used with replication. - Check replication, including checking that sequence tables are marked not transactional. - Check that a commit happens for NEXT VALUE that changes table data (may already work) - 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 - Run and fix Alibaba sequence tests (part of mysql-test/suite/sql_sequence) - Write documentation for NEXT VALUE / PREVIOUS_VALUE - NEXTVAL in DEFAULT - Ensure that NEXTVAL in DEFAULT uses database from base table - Two NEXTVAL for same row should give same answer. - Oracle syntax sequence_table.nextval, without any FOR or FROM. - Sequence tables are treated as 'not read constant tables' by SELECT; Would be better if we would have a separate list for sequence tables so that select doesn't know about them, except if refereed to with FROM. Other things done: - Improved output for safemalloc backtrack - frm_type_enum changed to Table_type - Removed lex->is_view and replaced with lex->table_type. This allows use to more easy check if item is view, sequence or table. - Added table flag HA_CAN_TABLES_WITHOUT_ROLLBACK, needed for handlers that want's to support sequences - Added handler calls: - engine_name(), to simplify getting engine name for partition and sequences - update_first_row(), to be able to do efficient sequence implementations. - Made binlog_log_row() global to be able to call it from ha_sequence.cc - Added handler variable: row_already_logged, to be able to flag that the changed row is already logging to replication log. - Added CF_DB_CHANGE and CF_SCHEMA_CHANGE flags to simplify deny_updates_if_read_only_option() - Added sp_add_cfetch() to avoid new conflicts in sql_yacc.yy - Moved code for add_table_options() out from sql_show.cc::show_create_table() - Added String::append_longlong() and used it in sql_show.cc to simplify code. - Added extra option to dd_frm_type() and ha_table_exists to indicate if the table is a sequence. Needed by DROP SQUENCE to not drop a table. --- include/my_base.h | 7 +- include/my_handler_errors.h | 4 +- libmysqld/CMakeLists.txt | 2 + mysql-test/mysql-test-run.pl | 1 + mysql-test/r/mysqld--help.result | 2 +- mysql-test/r/udf.result | 16 +- mysql-test/r/udf_notembedded.result | 6 +- .../suite/binlog/r/binlog_unsafe.result | 35 - .../suite/compat/oracle/r/sequence.result | 60 + .../suite/compat/oracle/t/sequence.test | 33 + mysql-test/suite/sql_sequence/binlog.result | 31 + mysql-test/suite/sql_sequence/binlog.test | 26 + mysql-test/suite/sql_sequence/create.result | 497 ++++++++ mysql-test/suite/sql_sequence/create.test | 374 ++++++ mysql-test/suite/sql_sequence/disabled.def | 2 + mysql-test/suite/sql_sequence/gtid-master.opt | 3 + mysql-test/suite/sql_sequence/gtid-slave.opt | 4 + mysql-test/suite/sql_sequence/gtid.result | 722 ++++++++++++ mysql-test/suite/sql_sequence/gtid.test | 660 +++++++++++ mysql-test/suite/sql_sequence/next.result | 404 +++++++ mysql-test/suite/sql_sequence/next.test | 201 ++++ .../suite/sql_sequence/read_only.result | 25 + mysql-test/suite/sql_sequence/read_only.test | 39 + .../suite/sql_sequence/replication-master.opt | 1 + .../suite/sql_sequence/replication-slave.opt | 1 + .../suite/sql_sequence/replication.result | 1030 +++++++++++++++++ .../suite/sql_sequence/replication.test | 881 ++++++++++++++ .../r/sysvars_server_notembedded.result | 4 +- mysql-test/t/udf.test | 16 +- mysql-test/t/udf_notembedded.test | 6 +- mysys/safemalloc.c | 8 +- sql/CMakeLists.txt | 3 + sql/create_options.cc | 5 +- sql/datadict.cc | 33 +- sql/datadict.h | 19 +- sql/ha_partition.cc | 4 +- sql/ha_partition.h | 1 + sql/ha_sequence.cc | 417 +++++++ sql/ha_sequence.h | 138 +++ sql/handler.cc | 69 +- sql/handler.h | 65 +- sql/item_func.cc | 145 +++ sql/item_func.h | 42 + sql/lex.h | 13 +- sql/lock.cc | 15 +- sql/lock.h | 3 +- sql/log_event.cc | 18 +- sql/log_event.h | 1 + sql/mysqld.cc | 6 +- sql/mysqld.h | 2 +- sql/share/errmsg-utf8.txt | 18 + sql/sp_head.cc | 8 +- sql/sql_acl.cc | 2 +- sql/sql_admin.cc | 13 +- sql/sql_base.cc | 21 +- sql/sql_builtin.cc.in | 8 +- sql/sql_class.cc | 30 +- sql/sql_class.h | 13 + sql/sql_cmd.h | 2 + sql/sql_const.h | 1 + sql/sql_db.cc | 3 +- sql/sql_handler.cc | 2 +- sql/sql_insert.cc | 2 +- sql/sql_lex.cc | 54 +- sql/sql_lex.h | 5 +- sql/sql_parse.cc | 100 +- sql/sql_plugin.cc | 5 +- sql/sql_prepare.cc | 2 + sql/sql_select.cc | 9 +- sql/sql_sequence.cc | 670 +++++++++++ sql/sql_sequence.h | 133 +++ sql/sql_show.cc | 410 ++++--- sql/sql_string.cc | 9 + sql/sql_string.h | 1 + sql/sql_table.cc | 119 +- sql/sql_table.h | 7 +- sql/sql_trigger.cc | 2 +- sql/sql_truncate.cc | 15 +- sql/sql_view.cc | 16 +- sql/sql_yacc.yy | 308 ++++- sql/sql_yacc_ora.yy | 308 ++++- sql/table.cc | 29 +- sql/table.h | 10 +- sql/temporary_tables.cc | 10 +- sql/udf_example.c | 22 +- sql/udf_example.def | 6 +- sql/wsrep_hton.cc | 3 +- sql/wsrep_mysqld.cc | 3 +- storage/innobase/handler/ha_innodb.cc | 7 +- storage/maria/ha_maria.cc | 10 +- storage/myisam/ha_myisam.cc | 3 +- storage/rocksdb/rdb_datadic.cc | 8 +- storage/sequence/sequence.cc | 2 +- 93 files changed, 8009 insertions(+), 500 deletions(-) create mode 100644 mysql-test/suite/compat/oracle/r/sequence.result create mode 100644 mysql-test/suite/compat/oracle/t/sequence.test create mode 100644 mysql-test/suite/sql_sequence/binlog.result create mode 100644 mysql-test/suite/sql_sequence/binlog.test create mode 100644 mysql-test/suite/sql_sequence/create.result create mode 100644 mysql-test/suite/sql_sequence/create.test create mode 100644 mysql-test/suite/sql_sequence/disabled.def create mode 100644 mysql-test/suite/sql_sequence/gtid-master.opt create mode 100644 mysql-test/suite/sql_sequence/gtid-slave.opt create mode 100644 mysql-test/suite/sql_sequence/gtid.result create mode 100644 mysql-test/suite/sql_sequence/gtid.test create mode 100644 mysql-test/suite/sql_sequence/next.result create mode 100644 mysql-test/suite/sql_sequence/next.test create mode 100644 mysql-test/suite/sql_sequence/read_only.result create mode 100644 mysql-test/suite/sql_sequence/read_only.test create mode 100644 mysql-test/suite/sql_sequence/replication-master.opt create mode 100644 mysql-test/suite/sql_sequence/replication-slave.opt create mode 100644 mysql-test/suite/sql_sequence/replication.result create mode 100644 mysql-test/suite/sql_sequence/replication.test create mode 100644 sql/ha_sequence.cc create mode 100644 sql/ha_sequence.h create mode 100644 sql/sql_sequence.cc create mode 100644 sql/sql_sequence.h diff --git a/include/my_base.h b/include/my_base.h index 89f5e826fd5f4..ea8fd623b2805 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 bdea4f71eaf95..4b85832a6dece 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 07e7d433ddee4..6dabc5e0192bb 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -113,6 +113,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/wsrep_dummy.cc ../sql/encryption.cc ../sql/item_windowfunc.cc ../sql/sql_window.cc ../sql/sql_cte.cc + ../sql/sql_sequence.cc ../sql/sql_sequence.h + ../sql/ha_sequence.cc ../sql/ha_sequence.h ../sql/temporary_tables.cc ../sql/session_tracker.cc ${GEN_SOURCES} diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5d81ee3265bb2..17a2d7db632e2 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -194,6 +194,7 @@ END roles- rpl- sys_vars- + sql_sequence- unit- vcol- wsrep- diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index cfd2ede6ef9c7..778bfb13bd3ee 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1396,7 +1396,7 @@ performance-schema-max-rwlock-instances -1 performance-schema-max-socket-classes 10 performance-schema-max-socket-instances -1 performance-schema-max-stage-classes 150 -performance-schema-max-statement-classes 187 +performance-schema-max-statement-classes 189 performance-schema-max-table-handles -1 performance-schema-max-table-instances -1 performance-schema-max-thread-classes 50 diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 98aa2b50fc623..6af6b167511eb 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -4,7 +4,7 @@ CREATE FUNCTION myfunc_double RETURNS REAL SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION myfunc_nonexist RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; ERROR HY000: Can't find symbol 'myfunc_nonexist' in library CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; -CREATE FUNCTION sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION lookup RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; @@ -226,7 +226,7 @@ DROP FUNCTION myfunc_double; DROP FUNCTION myfunc_nonexist; ERROR 42000: FUNCTION test.myfunc_nonexist does not exist DROP FUNCTION myfunc_int; -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP FUNCTION lookup; DROP FUNCTION reverse_lookup; DROP FUNCTION avgcost; @@ -340,32 +340,32 @@ DROP FUNCTION check_const_len; DROP PROCEDURE check_const_len_sp; DROP TRIGGER check_const_len_trigger; DROP TABLE const_len_bug; -CREATE FUNCTION sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; CREATE TABLE t1 (a INT); CREATE TABLE t2 (a INT PRIMARY KEY); INSERT INTO t1 VALUES (4),(3),(2),(1); INSERT INTO t2 SELECT * FROM t1; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq ASC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq ASC; seq a 1 4 2 3 3 2 4 1 -SELECT sequence() AS seq, a FROM t1 ORDER BY seq DESC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq DESC; seq a 4 1 3 2 2 3 1 4 -SELECT * FROM t1 WHERE a = sequence(); +SELECT * FROM t1 WHERE a = udf_sequence(); a -SELECT * FROM t2 WHERE a = sequence(); +SELECT * FROM t2 WHERE a = udf_sequence(); a 1 2 3 4 -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP TABLE t1,t2; drop function if exists test.metaphon; drop function if exists metaphon; diff --git a/mysql-test/r/udf_notembedded.result b/mysql-test/r/udf_notembedded.result index 3fdcdbbe9d392..377af563d3eee 100644 --- a/mysql-test/r/udf_notembedded.result +++ b/mysql-test/r/udf_notembedded.result @@ -1,6 +1,6 @@ -create function sequence returns integer soname "UDF_EXAMPLE_LIB"; -create table t1 (n int key not null auto_increment, msg int as (sequence()) virtual); +create function udf_sequence returns integer soname "UDF_EXAMPLE_LIB"; +create table t1 (n int key not null auto_increment, msg int as (udf_sequence()) virtual); select * from t1; n msg drop table t1; -drop function sequence; +drop function udf_sequence; diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 1cde61620a62c..a2a5f434f187f 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -85,8 +85,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe UUID() function. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -149,8 +147,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe UUID() function. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -411,8 +407,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe @@hostname variable. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -475,8 +469,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe @@hostname variable. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -657,8 +649,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe SELECT...LIMIT statement. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1049,8 +1039,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe update of two autoinc columns. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1245,8 +1233,6 @@ DROP TRIGGER trig_2; Invoking view view_retval_2 returning value from function func_retval_1 returning value from unsafe UDF. CREATE VIEW view_retval_2 AS SELECT func_retval_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_retval_2; Warnings: @@ -1309,8 +1295,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe UDF. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1551,8 +1535,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking unsafe use of mysql.general_log. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves * binlog_format = STATEMENT: expect 1 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -1823,14 +1805,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking statement that is unsafe in many ways. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a UDF which may not return the same value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave * binlog_format = STATEMENT: expect 7 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -2099,9 +2073,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking function func_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_2 AS SELECT func_sidef_1(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: @@ -2235,9 +2206,6 @@ DROP TRIGGER trig_1; Invoking view view_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_1 AS SELECT multi_unsafe_func(); -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t1 SELECT * FROM view_sidef_1; Warnings: @@ -2281,9 +2249,6 @@ DROP TRIGGER trig_2; Invoking view view_sidef_2 invoking view view_sidef_1 invoking statement that is unsafe several times. CREATE VIEW view_sidef_2 AS SELECT * FROM view_sidef_1; -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system function that may return a different value on the slave * binlog_format = STATEMENT: expect 2 warnings. INSERT INTO t2 SELECT * FROM view_sidef_2; Warnings: diff --git a/mysql-test/suite/compat/oracle/r/sequence.result b/mysql-test/suite/compat/oracle/r/sequence.result new file mode 100644 index 0000000000000..e3bf9d4daee90 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sequence.result @@ -0,0 +1,60 @@ +SET sql_mode=ORACLE; +CREATE SEQUENCE s1; +SHOW CREATE SEQUENCE s1; +Table Create Table +s1 CREATE SEQUENCE "s1" start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle +SELECT s1.currval; +s1.currval +NULL +SELECT s1.nextval; +s1.nextval +1 +SELECT s1.nextval; +s1.nextval +2 +SELECT s1.nextval; +s1.nextval +3 +EXPLAIN EXTENDED SELECT s1.nextval; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select nextval("test"."s1") AS "s1.nextval" +SELECT nextval(s1); +nextval(s1) +4 +EXPLAIN EXTENDED SELECT s1.currval; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select lastval("test"."s1") AS "s1.currval" +SELECT lastval(s1); +lastval(s1) +4 +DROP SEQUENCE s1; +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.nextval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +VIEW_DEFINITION +select nextval(`test`.`s1`) AS `a` +SELECT * FROM v1; +a +1 +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE VIEW "v1" AS select nextval("test"."s1") AS "a" latin1 latin1_swedish_ci +DROP VIEW v1; +DROP SEQUENCE s1; +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.currval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +VIEW_DEFINITION +select lastval(`test`.`s1`) AS `a` +SELECT * FROM v1; +a +NULL +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE VIEW "v1" AS select lastval("test"."s1") AS "a" latin1 latin1_swedish_ci +DROP VIEW v1; +DROP SEQUENCE s1; diff --git a/mysql-test/suite/compat/oracle/t/sequence.test b/mysql-test/suite/compat/oracle/t/sequence.test new file mode 100644 index 0000000000000..bc861c03014d0 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sequence.test @@ -0,0 +1,33 @@ +--source include/have_binlog_format_row.inc + +SET sql_mode=ORACLE; + +CREATE SEQUENCE s1; +SHOW CREATE SEQUENCE s1; +SELECT s1.currval; +SELECT s1.nextval; +SELECT s1.nextval; +SELECT s1.nextval; +EXPLAIN EXTENDED SELECT s1.nextval; +SELECT nextval(s1); +EXPLAIN EXTENDED SELECT s1.currval; +SELECT lastval(s1); +DROP SEQUENCE s1; + + +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.nextval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +SELECT * FROM v1; +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP SEQUENCE s1; + + +CREATE SEQUENCE s1; +CREATE VIEW v1 AS SELECT s1.currval AS a; +SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1'; +SELECT * FROM v1; +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP SEQUENCE s1; diff --git a/mysql-test/suite/sql_sequence/binlog.result b/mysql-test/suite/sql_sequence/binlog.result new file mode 100644 index 0000000000000..80746dcdfcf54 --- /dev/null +++ b/mysql-test/suite/sql_sequence/binlog.result @@ -0,0 +1,31 @@ +create or replace sequence s1 cache 3; +select next value for s1, min_value from s1 where max_value> 1; +next value for s1 min_value +1 1 +select next value for s1, min_value from s1 where max_value> 2; +next value for s1 min_value +2 1 +select next value for s1, min_value from s1 where max_value> 3; +next value for s1 min_value +3 1 +select next value for s1, min_value from s1 where max_value> 4; +next value for s1 min_value +4 1 +drop sequence s1; +"Runnig SHOW BINLOG EVENTS" +Log_name Pos Event_type Server_id End_log_pos Info +# # Gtid 1 # GTID #-#-# +# # Query 1 # use `test`; create or replace sequence s1 cache 3 +# # Gtid 1 # BEGIN GTID #-#-# +# # Annotate_rows 1 # select next value for s1, min_value from s1 where max_value> 1 +# # Table_map 1 # table_id: 30 (test.s1) +# # Write_rows_v1 1 # table_id: 30 flags: STMT_END_F +# # Query 1 # COMMIT +# # Gtid 1 # BEGIN GTID #-#-# +# # Annotate_rows 1 # select next value for s1, min_value from s1 where max_value> 4 +# # Table_map 1 # table_id: 30 (test.s1) +# # Write_rows_v1 1 # table_id: 30 flags: STMT_END_F +# # Query 1 # COMMIT +# # Gtid 1 # GTID #-#-# +# # Query 1 # use `test`; DROP TABLE `s1` /* generated by server */ +RESET MASTER; diff --git a/mysql-test/suite/sql_sequence/binlog.test b/mysql-test/suite/sql_sequence/binlog.test new file mode 100644 index 0000000000000..4ad74e0408301 --- /dev/null +++ b/mysql-test/suite/sql_sequence/binlog.test @@ -0,0 +1,26 @@ +--source include/have_udf.inc +--source include/have_log_bin.inc +--source include/binlog_start_pos.inc + +# +# Testing binary logging of sequences +# + +--let $pos=`select $binlog_start_pos + 73` +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_start=query_get_value(SHOW MASTER STATUS, Position, 1) + +create or replace sequence s1 cache 3; +select next value for s1, min_value from s1 where max_value> 1; +select next value for s1, min_value from s1 where max_value> 2; +select next value for s1, min_value from s1 where max_value> 3; +select next value for s1, min_value from s1 where max_value> 4; +drop sequence s1; + +--echo "Runnig SHOW BINLOG EVENTS" +--replace_column 1 # 2 # 5 # +--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ /Server.ver.*/VERSIONS/ +--disable_query_log +--eval SHOW BINLOG EVENTS FROM $binlog_start; +--enable_query_log +RESET MASTER; diff --git a/mysql-test/suite/sql_sequence/create.result b/mysql-test/suite/sql_sequence/create.result new file mode 100644 index 0000000000000..70f9745d409b0 --- /dev/null +++ b/mysql-test/suite/sql_sequence/create.result @@ -0,0 +1,497 @@ +drop table if exists t1; +Warnings: +Note 1051 Unknown table 'test.t1' +create or replace sequence t1 engine=myisam; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=innodb; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=InnoDB SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=maria; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=Aria +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=Aria SEQUENCE=1 +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +create or replace sequence t1 engine=archive; +ERROR HY000: Table storage engine 'ARCHIVE' does not support the create option 'SEQUENCE' +show create table t1; +ERROR 42S02: Table 'test.t1' doesn't exist +create or replace sequence t1 start with 10; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 1 9223372036854775806 10 1 1000 0 0 +create or replace sequence t1 minvalue=11; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 11 minvalue 11 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +11 11 9223372036854775806 11 1 1000 0 0 +create or replace sequence t1 maxvalue=13 increment by -1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 13 minvalue -9223372036854775807 maxvalue 13 increment by -1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +13 -9223372036854775807 13 13 -1 1000 0 0 +create or replace sequence t1 increment by -1 cache 100; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with -1 minvalue -9223372036854775807 maxvalue -1 increment by -1 cache 100 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +-1 -9223372036854775807 -1 -1 -1 100 0 0 +create or replace sequence t1 cycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 1 0 +create or replace sequence t1 nocycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +create or replace sequence t1 cycle minvalue= 14; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 14 minvalue 14 maxvalue 9223372036854775806 increment by 1 cache 1000 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +14 14 9223372036854775806 14 1 1000 1 0 +create or replace sequence t1 cycle increment by -1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with -1 minvalue -9223372036854775807 maxvalue -1 increment by -1 cache 1000 cycle ENGINE=MyISAM +drop sequence t1; +create sequence if not exists t1; +create sequence if not exists t1 start with 10; +Warnings: +Note 1050 Table 't1' already exists +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 1 1000 0 0 +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +create or replace sequence t1 start with 10 minvalue=10 maxvalue=11 nocache cycle; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue 10 maxvalue 11 increment by 1 nocache cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 10 11 10 1 0 1 0 +create or replace sequence t1 start with 10 minvalue=-10 maxvalue=11 cache=10 cycle increment by 10; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 10 minvalue -10 maxvalue 11 increment by 10 cache 10 cycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +10 -10 11 10 10 10 1 0 +create or replace sequence t1 start with 10 NO MAXVALUE NO MINVALUE; +create or replace sequence t1 start with 10 maxvalue 10; +create or replace sequence t1 start with 10 minvalue 10; +create or replace sequence t1 start with 10 minvalue 10 maxvalue 11 cycle; +create or replace sequence t1 start with 10 maxvalue=9223372036854775806; +create or replace sequence t1 start with 10 minvalue=-9223372036854775807; +drop sequence if exists t1; +create sequence t1 increment by 0; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 0 cache 1000 nocycle ENGINE=MyISAM +select * from t1; +next_value min_value max_value start increment cache cycle round +1 1 9223372036854775806 1 0 1000 0 0 +drop sequence t1; +create table t1 (a int); +show create sequence t1; +ERROR HY000: 'test.t1' is not SEQUENCE +drop sequence t1; +ERROR 42S02: 'test.t1' is not a SEQUENCE +drop sequence if exists t1; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +create sequence t1 start with 10 maxvalue=9; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 minvalue= 100 maxvalue=10; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 start with 9 minvalue=10; +ERROR HY000: Sequence 'test.t1' values are conflicting +create or replace sequence t1 maxvalue=13, increment by -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ' increment by -1' at line 1 +create or replace sequence t1 start with= 10 maxvalue=13; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '= 10 maxvalue=13' at line 1 +create or replace sequence t1 maxvalue=13, increment= -1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ' increment= -1' at line 1 +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NO MINVALUE' at line 1 +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NO MINVALUE' at line 1 +create sequence t1 start with 10 maxvalue=9223372036854775807; +ERROR HY000: Sequence 'test.t1' values are conflicting +create sequence t1 start with 10 minvalue=-9223372036854775808; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '9223372036854775808' at line 1 +create or replace sequence t1 start with 10 NO MINVALUE minvalue=1; +drop sequence t1; +create sequence t1; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +flush tables; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +create or replace sequence t1 engine=aria; +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +show fields from t1; +Field Type Null Key Default Extra +next_value bigint(21) NO NULL +min_value bigint(21) NO NULL +max_value bigint(21) NO NULL +start bigint(21) NO NULL +increment bigint(21) NO NULL +cache bigint(21) NO NULL +cycle tinyint(1) unsigned NO NULL +round bigint(21) NO NULL +flush tables; +create or replace sequence t1 comment= "test 1"; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM COMMENT='test 1' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 COMMENT='test 1' +create or replace sequence t1 comment= "test 2" min_rows=1 max_rows=2; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM COMMENT='test 2' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM MIN_ROWS=1 MAX_ROWS=2 SEQUENCE=1 COMMENT='test 2' +create or replace sequence t1 start=1 increment= 2; +create or replace sequence t1 start 1 increment 2; +drop sequence t1; +CREATE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +show create sequence t1; +Table Create Table +t1 CREATE SEQUENCE `t1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) ENGINE=MyISAM SEQUENCE=1 +drop sequence t1; +CREATE OR REPLACE TABLE t1 ( +`next_val` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_val) +CREATE OR REPLACE TABLE t1 ( +`next_value` int(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_value) +CREATE OR REPLACE TABLE t1 ( +`next_val` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` bigint(21) unsigned NOT NULL, /* error */ +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_val) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21), +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (next_value) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL, +extra_field bigint(21) +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (Wrong number of columns) +CREATE OR REPLACE TABLE t1 ( +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`next_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (min_value) +CREATE OR REPLACE TABLE t1 ( +`next_value` bigint(21) NOT NULL, +`min_value` bigint(21) NOT NULL, +`max_value` bigint(21) NOT NULL, +`start` bigint(21) NOT NULL, +`increment` bigint(21) NOT NULL, +`cache` bigint(21) NOT NULL, +`cycle` tinyint(1) unsigned NOT NULL, +`round` bigint(21) NOT NULL, +key key1 (next_value) +) sequence=1; +ERROR HY000: Sequence 'test.t1' table structure is invalid (Sequence tables cannot have any keys) +drop sequence if exists t1; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +create sequence t1; +create sequence t2; +create table t3 (a int) engine=myisam; +select table_catalog, table_schema, table_name, table_type from information_schema.tables where table_catalog="test"; +table_catalog table_schema table_name table_type +CREATE SEQUENCE s1; +drop sequence s1; +drop sequence if exists t1,t2,t3,t4; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t3' +Note 4066 Unknown SEQUENCE: 'test.t4' +drop table if exists t1,t2,t3; +Warnings: +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table t1,t2,s1; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table if exists t1,t2,s1,s2; +Warnings: +Note 1051 Unknown table 'test.s2' +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence t1,t2,s1,s2; +ERROR 42S02: Unknown SEQUENCE: 'test.t1,test.t2,test.s2' +drop table if exists t1,t2; +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence if exists t1,t2,s1,s2; +Warnings: +Note 4066 Unknown SEQUENCE: 'test.t1' +Note 4066 Unknown SEQUENCE: 'test.t2' +Note 4066 Unknown SEQUENCE: 'test.s2' +drop table if exists t1,t2; +CREATE TEMPORARY SEQUENCE s1; +DROP SEQUENCE s1; +DROP TEMPORARY SEQUENCE s1; +ERROR 42S02: Unknown SEQUENCE: 'test.s1' +CREATE TEMPORARY SEQUENCE s1; +CREATE SEQUENCE s2; +CREATE TEMPORARY TABLE t1 (a int); +CREATE TABLE t2 (a int); +DROP TEMPORARY SEQUENCE t1,t2,s1,s2; +ERROR 42S02: Unknown SEQUENCE: 'test.t1,test.t2,test.s2' +DROP TEMPORARY SEQUENCE s1; +ERROR 42S02: Unknown SEQUENCE: 'test.s1' +DROP TEMPORARY TABLE t1; +DROP TABLE t1,t2,s1,s2; +ERROR 42S02: Unknown table 'test.t1,test.s1' +create view v1 as (select 1); +CREATE SEQUENCE s1; +DROP SEQUENCE s1,v1; +ERROR 42S02: 'test.v1' is a view +drop view v1; +CREATE TEMPORARY SEQUENCE t1; +select next value for t1; +next value for t1 +1 +drop temporary table t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +100 +select previous value for t1; +previous value for t1 +100 +drop temporary sequence t1; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +CREATE TEMPORARY SEQUENCE t1 engine=innodb; +select next value for t1; +next value for t1 +1 +drop temporary table t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10 engine=innodb; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10 engine=innodb; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +100 +select previous value for t1; +previous value for t1 +100 +drop temporary sequence t1; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +create table t1 (a int) engine=sql_sequence; +ERROR 42000: Unknown storage engine 'sql_sequence' diff --git a/mysql-test/suite/sql_sequence/create.test b/mysql-test/suite/sql_sequence/create.test new file mode 100644 index 0000000000000..6cb6dedd91be4 --- /dev/null +++ b/mysql-test/suite/sql_sequence/create.test @@ -0,0 +1,374 @@ +# +# Test create options with sequences +# +--source include/have_innodb.inc +--source include/have_archive.inc + +drop table if exists t1; + +# +# Check some sample engines +# + +create or replace sequence t1 engine=myisam; +show create sequence t1; +show create table t1; +select * from t1; +create or replace sequence t1 engine=innodb; +show create sequence t1; +show create table t1; +select * from t1; +create or replace sequence t1 engine=maria; +show create sequence t1; +show create table t1; +select * from t1; +--error ER_ILLEGAL_HA_CREATE_OPTION +create or replace sequence t1 engine=archive; +# +# The following error should be fixed. We shouldn't delete old table on errors +# +--error ER_NO_SUCH_TABLE +show create table t1; + + +# Check start values +create or replace sequence t1 start with 10; +show create sequence t1; +select * from t1; +create or replace sequence t1 minvalue=11; +show create sequence t1; +select * from t1; +create or replace sequence t1 maxvalue=13 increment by -1; +show create sequence t1; +select * from t1; + +create or replace sequence t1 increment by -1 cache 100; +show create sequence t1; +select * from t1; +create or replace sequence t1 cycle; +show create sequence t1; +select * from t1; +create or replace sequence t1 nocycle; +show create sequence t1; +select * from t1; +show create sequence t1; +create or replace sequence t1 cycle minvalue= 14; +show create sequence t1; +select * from t1; +create or replace sequence t1 cycle increment by -1; +show create sequence t1; + +drop sequence t1; +create sequence if not exists t1; +create sequence if not exists t1 start with 10; +select * from t1; +show create sequence t1; + +create or replace sequence t1 start with 10 minvalue=10 maxvalue=11 nocache cycle; +show create sequence t1; +select * from t1; +create or replace sequence t1 start with 10 minvalue=-10 maxvalue=11 cache=10 cycle increment by 10; +show create sequence t1; +select * from t1; + +# NO MINVALUE, NO MAXVALUE +create or replace sequence t1 start with 10 NO MAXVALUE NO MINVALUE; + +# Some edge cases +create or replace sequence t1 start with 10 maxvalue 10; +create or replace sequence t1 start with 10 minvalue 10; +create or replace sequence t1 start with 10 minvalue 10 maxvalue 11 cycle; +create or replace sequence t1 start with 10 maxvalue=9223372036854775806; +create or replace sequence t1 start with 10 minvalue=-9223372036854775807; +drop sequence if exists t1; + +create sequence t1 increment by 0; +show create sequence t1; +select * from t1; +drop sequence t1; + +# +# Wrong usage and arguments to create sequence +# + +create table t1 (a int); +--error ER_WRONG_OBJECT +show create sequence t1; +--error ER_NOT_SEQUENCE2 +drop sequence t1; +drop sequence if exists t1; + +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 10 maxvalue=9; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 minvalue= 100 maxvalue=10; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 9 minvalue=10; +--error ER_PARSE_ERROR +create or replace sequence t1 maxvalue=13, increment by -1; +--error ER_PARSE_ERROR +create or replace sequence t1 start with= 10 maxvalue=13; +--error ER_PARSE_ERROR +create or replace sequence t1 maxvalue=13, increment= -1; +--error ER_PARSE_ERROR +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +--error ER_PARSE_ERROR +create or replace sequence t1 start with 10 min_value=1 NO MINVALUE; +--error ER_SEQUENCE_INVALID_DATA +create sequence t1 start with 10 maxvalue=9223372036854775807; +--error ER_PARSE_ERROR +create sequence t1 start with 10 minvalue=-9223372036854775808; + +# This should probably give an error +create or replace sequence t1 start with 10 NO MINVALUE minvalue=1; +drop sequence t1; + +# +# Test with LIST COLUMNS as first command +# +create sequence t1; +show fields from t1; +flush tables; +show fields from t1; +create or replace sequence t1 engine=aria; +show fields from t1; +show fields from t1; +flush tables; + +# +# Test with other create options +# + +create or replace sequence t1 comment= "test 1"; +show create sequence t1; +show create table t1; +create or replace sequence t1 comment= "test 2" min_rows=1 max_rows=2; +show create sequence t1; +show create table t1; +create or replace sequence t1 start=1 increment= 2; +create or replace sequence t1 start 1 increment 2; +drop sequence t1; + +# +# test with create table +# + +CREATE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +show create sequence t1; +show create table t1; +drop sequence t1; + +# Wrong column name + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_val` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# Wrong type + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` int(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_val` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` bigint(21) unsigned NOT NULL, /* error */ + `round` bigint(21) NOT NULL +) sequence=1; + + +# Missing NOT NULL + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21), + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# Extra field + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL, + extra_field bigint(21) +) sequence=1; + +# Wrong field order + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `next_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL +) sequence=1; + +# key + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +CREATE OR REPLACE TABLE t1 ( + `next_value` bigint(21) NOT NULL, + `min_value` bigint(21) NOT NULL, + `max_value` bigint(21) NOT NULL, + `start` bigint(21) NOT NULL, + `increment` bigint(21) NOT NULL, + `cache` bigint(21) NOT NULL, + `cycle` tinyint(1) unsigned NOT NULL, + `round` bigint(21) NOT NULL, + key key1 (next_value) +) sequence=1; + +drop sequence if exists t1; + +# +# DROP SEQUENCE +# + +create sequence t1; +create sequence t2; +create table t3 (a int) engine=myisam; +select table_catalog, table_schema, table_name, table_type from information_schema.tables where table_catalog="test"; + +CREATE SEQUENCE s1; +drop sequence s1; + +drop sequence if exists t1,t2,t3,t4; +drop table if exists t1,t2,t3; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table t1,t2,s1; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop table if exists t1,t2,s1,s2; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +--error ER_UNKNOWN_SEQUENCES +drop sequence t1,t2,s1,s2; +drop table if exists t1,t2; + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE SEQUENCE s1; +drop sequence if exists t1,t2,s1,s2; +drop table if exists t1,t2; + +CREATE TEMPORARY SEQUENCE s1; +DROP SEQUENCE s1; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE s1; + +CREATE TEMPORARY SEQUENCE s1; +CREATE SEQUENCE s2; +CREATE TEMPORARY TABLE t1 (a int); +CREATE TABLE t2 (a int); +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE t1,t2,s1,s2; +--error ER_UNKNOWN_SEQUENCES +DROP TEMPORARY SEQUENCE s1; +DROP TEMPORARY TABLE t1; +--error ER_BAD_TABLE_ERROR +DROP TABLE t1,t2,s1,s2; + +create view v1 as (select 1); +CREATE SEQUENCE s1; +--error ER_IT_IS_A_VIEW +DROP SEQUENCE s1,v1; +drop view v1; + +# +# CREATE TEMPORARY SEQUENCE +# + +CREATE TEMPORARY SEQUENCE t1; +select next value for t1; +drop temporary table t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10; +select next value for t1; +select previous value for t1; +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop temporary sequence t1; +select previous value for t1; +drop sequence t1; + +CREATE TEMPORARY SEQUENCE t1 engine=innodb; +select next value for t1; +drop temporary table t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 10 engine=innodb; +select next value for t1; +select previous value for t1; +CREATE TEMPORARY SEQUENCE t1 start with 100 minvalue 100 maxvalue 200 increment by 1 cache 10 engine=innodb; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop temporary sequence t1; +select previous value for t1; +drop sequence t1; + +# +# Check that we can't create anything with the sequence engine +# + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t1 (a int) engine=sql_sequence; diff --git a/mysql-test/suite/sql_sequence/disabled.def b/mysql-test/suite/sql_sequence/disabled.def new file mode 100644 index 0000000000000..507617dd75d21 --- /dev/null +++ b/mysql-test/suite/sql_sequence/disabled.def @@ -0,0 +1,2 @@ +gtid : Disabled until Monty has time to check the result +replication : Disabled until Monty has time to check the result diff --git a/mysql-test/suite/sql_sequence/gtid-master.opt b/mysql-test/suite/sql_sequence/gtid-master.opt new file mode 100644 index 0000000000000..dd4fb8c5f9a2f --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid-master.opt @@ -0,0 +1,3 @@ +--binlog_format=row +--query_cache_type=1 +--log-slave-updates diff --git a/mysql-test/suite/sql_sequence/gtid-slave.opt b/mysql-test/suite/sql_sequence/gtid-slave.opt new file mode 100644 index 0000000000000..dc0ff1864e087 --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid-slave.opt @@ -0,0 +1,4 @@ +--binlog_format=row +--query_cache_type=1 +--read_only=true +--log-slave-updates diff --git a/mysql-test/suite/sql_sequence/gtid.result b/mysql-test/suite/sql_sequence/gtid.result new file mode 100644 index 0000000000000..f9a2d8ab60a3f --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid.result @@ -0,0 +1,722 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; +set global read_only=on; +########################################### +master and slave sync sequence. +########################################### +use s_db; +create sequence s1; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +drop sequence s1; +########################################### +not support create table engine=sequence. +########################################### +create table t(id int)engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +create table t(id int)engine=innodb; +alter table t engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop table t; +########################################### +not support alter sequence table. +########################################### +create sequence s2; +alter table s2 add id int; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +alter table s2 add index ind_x(start); +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop sequence s2; +########################################### +not support create temproary sequence. +########################################### +create temporary sequence s2; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sequence s2' at line 1 +########################################### +all invalid sequence value +########################################### +use s_db; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +cycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +nocache +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 5 +maxvalue 100000 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 5 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 0 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +########################################### +global read lock prevent query sequence +########################################### +use s_db; +create sequence s_db.s1; +flush table with read lock; +select * for s1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop sequence s_db.s1; +########################################### +session setting +########################################### +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +set session sequence_read_skip_cache=false; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +4 +drop sequence s1; +########################################### +query cache test +########################################### +use s_db; +show global variables like 'query_cache_type'; +Variable_name Value +query_cache_type ON +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +show status like 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 0 +########################################### +priv test +########################################### +create sequence s_db.s1; +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +create sequence s_db.s2; +drop sequence s_db.s2; +select * for s_db.s1; +ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' +create sequence s_db.s2; +ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' +drop sequence s_db.s1; +########################################### +run out sequence value +########################################### +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +commit; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +drop sequence s_t; +drop table t; +########################################### +read_only prevent query sequence +########################################### +create sequence s_db.s1; +show global variables like 'read_only'; +Variable_name Value +read_only OFF +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +show global variables like 'read_only'; +Variable_name Value +read_only ON +select * for s_db.s1; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +drop sequence s_db.s1; +########################################### +update based table +########################################### +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; +use s_db; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 20 1 1 5 1 0 +select nextval for s_t; +nextval +1 +select nextval from s_t; +nextval +7 +------------------------------------------ +master update nextval; +------------------------------------------ +select nextval for s_t; +nextval +2 +update s_t set nextval= 11; +commit; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +------------------------------------------ +show slave nextval; +------------------------------------------ +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +set session sequence_read_skip_cache=off; +select * for s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 17 1 20 1 1 5 1 0 +------------------------------------------ +update into invalid sequence +------------------------------------------ +select nextval for s_t; +nextval +12 +update s_t set nextval= 11,start=10, minvalue=11; +commit; +create table t_1(id int); +insert into t_1 value(1111); +select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +insert into t_1 select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +commit; +select * from t_1; +id +1111 +------------------------------------------ +delete sequence row +------------------------------------------ +delete from s_t; +commit; +select nextval for s_t; +nextval +drop sequence s_t; +drop table t_1; +########################################### +test transaction context (innodb) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +select * from t_1; +id +1111 +1 +2 +2222 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +test transaction context (myisam) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +close binlog +########################################### +use s_db; +create sequence s1 cache 2; +select nextval for s1; +nextval +1 +select nextval for s1; +nextval +2 +select nextval for s1; +nextval +3 +select nextval for s1; +nextval +4 +commit; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +------------------------------------------ +close session binlog. +------------------------------------------ +set session sql_log_bin=off; +select nextval for s1; +nextval +5 +select nextval for s1; +nextval +6 +select nextval for s1; +nextval +7 +select nextval for s1; +nextval +8 +set session sql_log_bin=on; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 10 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +drop sequence s1; +########################################### +statement binlog +########################################### +------------------------------------------ +set binlog_format=statement +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format STATEMENT +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +########################################### +test savepoint +########################################### +set session sequence_read_skip_cache=off; +set session binlog_format=row; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 values(2222); +select * from t1; +id +1111 +1 +2 +3 +2222 +rollback to sp1; +select * from t1; +id +1111 +select nextval for s1; +nextval +4 +commit; +drop sequence s1; +drop table t1; +########################################### +test proc +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create table t(id int)engine=innodb; +create procedure p1() +begin +create sequence s1 cache 2; +end// +create procedure p2() +begin +insert into t select nextval for s1; +commit; +end// +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); +select * from t; +id +1 +2 +3 +4 +use s_db; +select * from t; +id +1 +2 +3 +4 +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; +########################################### +test trigger +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; +CREATE TRIGGER tri_1 +before INSERT ON t2 FOR EACH ROW +BEGIN +INSERT INTO t1 select nextval for s1; +END// +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +select * from t2; +id +1111 +1111 +1111 +1111 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t2; +id +select * from t1; +id +select nextval for s1; +nextval +5 +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; +########################################### +test function +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +CREATE function f1() returns int +BEGIN +INSERT INTO t1 select nextval for s1; +return (1); +END// +begin; +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t1; +id +select nextval for s1; +nextval +5 +drop function f1; +drop table t1; +drop sequence s1; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; +include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/gtid.test b/mysql-test/suite/sql_sequence/gtid.test new file mode 100644 index 0000000000000..30717a4e53cb9 --- /dev/null +++ b/mysql-test/suite/sql_sequence/gtid.test @@ -0,0 +1,660 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/have_innodb.inc + +connection master; +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; + +--sync_slave_with_master + +connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT); +connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT); + +connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT); +connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT); + +connection slave; +set global read_only=on; + +--echo ########################################### +--echo master and slave sync sequence. +--echo ########################################### +connection master; +use s_db; + +create sequence s1; +show create table s1; + +--sync_slave_with_master +connection slave; +use s_db; +show create table s1; + +connection master; +use s_db; + +drop sequence s1; + +--echo ########################################### +--echo not support create table engine=sequence. +--echo ########################################### +connection master; + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t(id int)engine=sequence; + +create table t(id int)engine=innodb; + +--error ER_UNKNOWN_STORAGE_ENGINE +alter table t engine=sequence; + +drop table t; +--echo ########################################### +--echo not support alter sequence table. +--echo ########################################### +connection master; + +create sequence s2; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add id int; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add index ind_x(start); +drop sequence s2; + +--echo ########################################### +--echo Support create temporary sequence. +--echo ########################################### +connection master; + +create temporary sequence s2; +drop temporary sequence s2; + +--echo ########################################### +--echo all invalid sequence value +--echo ########################################### + +connection master; +use s_db; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + cycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + nocycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + nocache + nocycle; +drop sequence s2; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 0 + nocache + nocycle; + +--echo ########################################### +--echo global read lock prevent query sequence +--echo ########################################### +connection master; +use s_db; +create sequence s_db.s1; +flush table with read lock; +--error ER_CANT_UPDATE_WITH_READLOCK +select next value for s1; + +unlock tables; + +drop sequence s_db.s1; + +--echo ########################################### +--echo query cache test +--echo ########################################### +connection master; +use s_db; +show global variables like 'query_cache_type'; + +show status like 'Qcache_hits'; +show status like 'Qcache_inserts'; + +--echo ########################################### +--echo priv test +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +select next value for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select next value for s_db.s1; +--error ER_TABLEACCESS_DENIED_ERROR +create sequence s_db.s2; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo run out sequence value +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +commit; +select * from t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t; + +connection m_normal_1; +use s_db; +drop sequence s_t; +drop table t; + +--echo ########################################### +--echo read_only prevent query sequence +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +show global variables like 'read_only'; +select next value for s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select next value for s_db.s1; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo update based table +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from s_t; + + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +update s_t set next_value= 11; +commit; + +select * from s_t; + +connection master; +--sync_slave_with_master + +--echo ------------------------------------------ +--echo show slave nextval; +--echo ------------------------------------------ +connection s_normal_3; +select * from s_t; + +connection m_normal_1; +select next value for s_t; +select * from s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + + +--echo ------------------------------------------ +--echo update into invalid sequence +--echo ------------------------------------------ +connection m_normal_1; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set next_value= 11,start=10, min_value=11; +commit; + +create table t_1(id int); +insert into t_1 value(1111); +select next value for s_t; +insert into t_1 select next value for s_t; +commit; + +select * from t_1; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +--error ER_ILLEGAL_HA +delete from s_t; +commit; + +select next value for s_t; + +connection m_normal_1; +drop sequence s_t; +drop table t_1; + +--echo ########################################### +--echo test transaction context (innodb) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo test transaction context (myisam) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo close binlog +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +commit; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +--echo ------------------------------------------ +--echo close session binlog. +--echo ------------------------------------------ +connection master; +set session sql_log_bin=off; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +set session sql_log_bin=on; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo statement binlog +--echo ########################################### +--echo ------------------------------------------ +--echo set binlog_format=statement +--echo ------------------------------------------ +connection master; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_BINLOG_STMT_MODE_AND_ROW_ENGINE +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +set session binlog_format=row; + +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; + +insert into t1 values(2222); + +select * from t1; +rollback to sp1; +select * from t1; +select next value for s1; + +commit; + +drop sequence s1; +drop table t1; + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +use s_db; +create table t(id int)engine=innodb; + +delimiter //; + +create procedure p1() +begin + create sequence s1 cache 2; +end// + +create procedure p2() +begin + insert into t select next value for s1; + commit; +end// + +delimiter ;// + +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); + +select * from t; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from t; + +connection m_normal_1; +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; + +--echo ########################################### +--echo test trigger +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select next value for s1; +END// +delimiter ;// + +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); + +select * from t2; +select * from t1; +rollback; +select * from t2; +select * from t1; + +select next value for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +delimiter //; +CREATE function f1() returns int +BEGIN + INSERT INTO t1 select next value for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select next value for s1; + +drop function f1; +drop table t1; +drop sequence s1; + +connection master; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; + + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/next.result b/mysql-test/suite/sql_sequence/next.result new file mode 100644 index 0000000000000..fc28152a2b76a --- /dev/null +++ b/mysql-test/suite/sql_sequence/next.result @@ -0,0 +1,404 @@ +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 2 cycle; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=MyISAM SEQUENCE=1 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +3 0 +select next value for t1; +next value for t1 +2 +select next_value,round from t1; +next_value round +3 0 +select next value for t1; +next value for t1 +3 +select next_value,round from t1; +next_value round +5 0 +select next value for t1; +next value for t1 +4 +select next_value,round from t1; +next_value round +5 0 +select next value for t1; +next value for t1 +5 +select next_value,round from t1; +next_value round +7 0 +select next value for t1; +next value for t1 +6 +select next_value,round from t1; +next_value round +7 0 +select next value for t1; +next value for t1 +7 +select next_value,round from t1; +next_value round +9 0 +select next value for t1; +next value for t1 +8 +select next_value,round from t1; +next_value round +9 0 +select next value for t1; +next value for t1 +9 +select next_value,round from t1; +next_value round +11 0 +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +11 0 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +3 1 +select NEXT VALUE for t1,seq from seq_1_to_20; +NEXT VALUE for t1 seq +2 1 +3 2 +4 3 +5 4 +6 5 +7 6 +8 7 +9 8 +10 9 +1 10 +2 11 +3 12 +4 13 +5 14 +6 15 +7 16 +8 17 +9 18 +10 19 +1 20 +drop sequence t1; +CREATE SEQUENCE t1 minvalue 1 maxvalue 10 increment by -1 cache 2 cycle engine=aria; +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +8 0 +select next value for t1; +next value for t1 +9 +select next_value,round from t1; +next_value round +8 0 +select next value for t1; +next value for t1 +8 +select next_value,round from t1; +next_value round +6 0 +select next value for t1; +next value for t1 +7 +select next_value,round from t1; +next_value round +6 0 +select next value for t1; +next value for t1 +6 +select next_value,round from t1; +next_value round +4 0 +select next value for t1; +next value for t1 +5 +select next_value,round from t1; +next_value round +4 0 +select next value for t1; +next value for t1 +4 +select next_value,round from t1; +next_value round +2 0 +select next value for t1; +next value for t1 +3 +select next_value,round from t1; +next_value round +2 0 +select next value for t1; +next value for t1 +2 +select next_value,round from t1; +next_value round +0 0 +select next value for t1; +next value for t1 +1 +select next_value,round from t1; +next_value round +0 0 +select next value for t1; +next value for t1 +10 +select next_value,round from t1; +next_value round +8 1 +select NEXT VALUE for t1,seq from seq_1_to_20; +NEXT VALUE for t1 seq +9 1 +8 2 +7 3 +6 4 +5 5 +4 6 +3 7 +2 8 +1 9 +10 10 +9 11 +8 12 +7 13 +6 14 +5 15 +4 16 +3 17 +2 18 +1 19 +10 20 +drop sequence t1; +CREATE SEQUENCE t1 start with 8 minvalue 1 maxvalue 10 increment by 1 cache 2 nocycle; +select next value for t1; +next value for t1 +8 +select next value for t1; +next value for t1 +9 +select next value for t1; +next value for t1 +10 +select previous value for t1; +previous value for t1 +10 +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +ERROR HY000: Sequence 'test.t1' has run out +drop sequence t1; +create sequence s1 start with 1 cache 2 maxvalue 5; +select next value for s1; +next value for s1 +1 +select next value for s1; +next value for s1 +2 +select next value for s1; +next value for s1 +3 +select next value for s1; +next value for s1 +4 +select next value for s1; +next value for s1 +5 +select next value for s1; +ERROR HY000: Sequence 'test.s1' has run out +drop sequence s1; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 100 increment by 1 cache 10; +select next value for t1; +next value for t1 +1 +select * from t1; +next_value min_value max_value start increment cache cycle round +11 1 100 1 1 10 0 0 +flush tables; +select next value for t1; +next value for t1 +11 +select nextval(t1); +nextval(t1) +12 +drop sequence t1; +CREATE SEQUENCE t9 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t9; +previous value for t9 +NULL +select next value for t9; +next value for t9 +1 +select previous value for t9, lastval(t9); +previous value for t9 lastval(t9) +1 1 +select next value for t9; +next value for t9 +2 +select previous value for t9, lastval(t9); +previous value for t9 lastval(t9) +2 2 +select seq, previous value for t9, NEXT VALUE for t9, previous value for t9 from seq_1_to_20; +seq previous value for t9 NEXT VALUE for t9 previous value for t9 +1 2 3 3 +2 3 4 4 +3 4 5 5 +4 5 6 6 +5 6 7 7 +6 7 8 8 +7 8 9 9 +8 9 10 10 +9 10 1 1 +10 1 2 2 +11 2 3 3 +12 3 4 4 +13 4 5 5 +14 5 6 6 +15 6 7 7 +16 7 8 8 +17 8 9 9 +18 9 10 10 +19 10 1 1 +20 1 2 2 +select * from t9; +next_value min_value max_value start increment cache cycle round +6 1 10 1 1 5 1 2 +drop sequence t9; +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select next value for t1; +next value for t1 +1 +select previous value for t1; +previous value for t1 +1 +flush tables; +select previous value for t1; +previous value for t1 +1 +drop sequence t1; +select previous value for t1; +ERROR 42S02: Table 'test.t1' doesn't exist +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t1; +previous value for t1 +NULL +select next value for t1; +next value for t1 +5 +select previous value for t1; +previous value for t1 +5 +drop sequence t1; +CREATE or replace SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 +INCREMENT BY 1 START WITH 3984356 CACHE 20 CYCLE engine=innodb; +show create table s1; +Table Create Table +s1 CREATE TABLE `s1` ( + `next_value` bigint(21) NOT NULL COMMENT 'next not cached value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'How many cycles has been done' +) ENGINE=InnoDB SEQUENCE=1 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984356 1 9999999999 3984356 1 20 1 0 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984356 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984357 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984358 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984376 1 9999999999 3984356 1 20 1 0 +FLUSH TABLES; +select * from s1; +next_value min_value max_value start increment cache cycle round +3984376 1 9999999999 3984356 1 20 1 0 +select NEXT VALUE FOR s1; +NEXT VALUE FOR s1 +3984376 +select * from s1; +next_value min_value max_value start increment cache cycle round +3984396 1 9999999999 3984356 1 20 1 0 +drop sequence s1; +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +explain select next value for t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used +explain select next value for t1, min_value from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 +drop table t1; +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int); +insert into t1 values (next value for s1); +insert into t1 values (next value for s1); +select * from t1; +a +1 +2 +drop table t1,s1; +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int primary key auto_increment, b int default 0) engine=myisam; +insert into t1 values (),(),(),(),(),(),(); +update t1 set b= next value for s1 where a <= 3; +select * from t1; +a b +1 1 +2 2 +3 3 +4 0 +5 0 +6 0 +7 0 +drop table t1,s1; +CREATE OR REPLACE SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 3984356 nocache CYCLE engine='innodb'; +select * from s1; +next_value min_value max_value start increment cache cycle round +3984356 1 9999999999 3984356 1 0 1 0 +select next value for s1; +next value for s1 +3984356 +drop sequence s1; +create table t1 (a int); +select next value for t1; +ERROR 42S02: 'test.t1' is not a SEQUENCE +drop table t1; +create sequence t1; +select next value for t1; +next value for t1 +1 +select next value for t1, min_value; +ERROR 42S22: Unknown column 'min_value' in 'field list' +drop sequence t1; diff --git a/mysql-test/suite/sql_sequence/next.test b/mysql-test/suite/sql_sequence/next.test new file mode 100644 index 0000000000000..426ee5709a19a --- /dev/null +++ b/mysql-test/suite/sql_sequence/next.test @@ -0,0 +1,201 @@ +--source include/have_sequence.inc +--source include/have_innodb.inc + +# +# Test sequence generation +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 2 cycle; +show create table t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; + +select NEXT VALUE for t1,seq from seq_1_to_20; + +drop sequence t1; + +CREATE SEQUENCE t1 minvalue 1 maxvalue 10 increment by -1 cache 2 cycle engine=aria; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; +select next value for t1; +select next_value,round from t1; + +select NEXT VALUE for t1,seq from seq_1_to_20; + +drop sequence t1; + +CREATE SEQUENCE t1 start with 8 minvalue 1 maxvalue 10 increment by 1 cache 2 nocycle; +select next value for t1; +select next value for t1; +select next value for t1; +select previous value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +select previous value for t1; +--error ER_SEQUENCE_RUN_OUT +select next value for t1; +drop sequence t1; + +create sequence s1 start with 1 cache 2 maxvalue 5; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +drop sequence s1; + +# +# Test that flush tables jumps to next next_value +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 100 increment by 1 cache 10; +select next value for t1; +select * from t1; +flush tables; +select next value for t1; +select nextval(t1); +drop sequence t1; + +# +# Test currval/previous +# + +CREATE SEQUENCE t9 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t9; +select next value for t9; +select previous value for t9, lastval(t9); +select next value for t9; +select previous value for t9, lastval(t9); +select seq, previous value for t9, NEXT VALUE for t9, previous value for t9 from seq_1_to_20; +select * from t9; +drop sequence t9; + +# +# Check what happens when one refers to a sequence that has been closed/deleted +# + +CREATE SEQUENCE t1 start with 1 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select next value for t1; +select previous value for t1; +flush tables; +select previous value for t1; +drop sequence t1; +--error ER_NO_SUCH_TABLE +select previous value for t1; +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +select previous value for t1; +select next value for t1; +select previous value for t1; +drop sequence t1; + +# This failed in an early build + +CREATE or replace SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 +INCREMENT BY 1 START WITH 3984356 CACHE 20 CYCLE engine=innodb; +show create table s1; +select * from s1; +select NEXT VALUE FOR s1; +select NEXT VALUE FOR s1; +select NEXT VALUE FOR s1; +select * from s1; +FLUSH TABLES; +select * from s1; +select NEXT VALUE FOR s1; +select * from s1; +drop sequence s1; + +# +# Explain +# + +CREATE SEQUENCE t1 start with 5 minvalue 1 maxvalue 10 increment by 1 cache 5 cycle; +explain select next value for t1; +explain select next value for t1, min_value from t1; +drop table t1; + +# +# Using insert with NEXT VALUE +# + +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int); +insert into t1 values (next value for s1); +insert into t1 values (next value for s1); +select * from t1; +drop table t1,s1; + +# +# Using update with NEXT VALUE +# + +CREATE SEQUENCE s1; +CREATE TABLE t1 (a int primary key auto_increment, b int default 0) engine=myisam; +insert into t1 values (),(),(),(),(),(),(); +update t1 set b= next value for s1 where a <= 3; +select * from t1; +drop table t1,s1; + +# +# NO CACHE and InnoDB +# + +CREATE OR REPLACE SEQUENCE s1 MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 3984356 nocache CYCLE engine='innodb'; +select * from s1; +select next value for s1; +drop sequence s1; + + +# +# Some error testing +# + +create table t1 (a int); +--error ER_NOT_SEQUENCE +select next value for t1; +drop table t1; + +create sequence t1; +select next value for t1; +--error ER_BAD_FIELD_ERROR +select next value for t1, min_value; +drop sequence t1; diff --git a/mysql-test/suite/sql_sequence/read_only.result b/mysql-test/suite/sql_sequence/read_only.result new file mode 100644 index 0000000000000..3f6a95610dd18 --- /dev/null +++ b/mysql-test/suite/sql_sequence/read_only.result @@ -0,0 +1,25 @@ +create sequence s1 cache 2 engine=innodb; +connection default; +show global variables like 'innodb_read_only'; +Variable_name Value +innodb_read_only ON +use test; +set session binlog_format= row; +########################################### +read_only create error. +########################################### +show global variables like 'innodb_read_only'; +Variable_name Value +innodb_read_only ON +use test; +create sequence s2 cache 5 engine=innodb; +ERROR HY000: Can't create table `test`.`s2` (errno: 165 "Table is read only") +########################################### +read_only query error. +########################################### +select next value for s1; +ERROR HY000: Table 's1' is read only +select next value for s1; +ERROR HY000: Table 's1' is read only +select next value for s1; +ERROR HY000: Table 's1' is read only diff --git a/mysql-test/suite/sql_sequence/read_only.test b/mysql-test/suite/sql_sequence/read_only.test new file mode 100644 index 0000000000000..d8743617ad20c --- /dev/null +++ b/mysql-test/suite/sql_sequence/read_only.test @@ -0,0 +1,39 @@ +--source include/have_innodb.inc + +# +# Test innodb read only +# + +create sequence s1 cache 2 engine=innodb; + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server 10 +--source include/wait_until_disconnected.inc +--enable_reconnect +--exec echo "restart":--innodb_read_only=1 > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--source include/wait_until_connected_again.inc + +connection default; +show global variables like 'innodb_read_only'; +use test; +set session binlog_format= row; + +--echo ########################################### +--echo read_only create error. +--echo ########################################### + +show global variables like 'innodb_read_only'; +use test; + +--error ER_CANT_CREATE_TABLE +create sequence s2 cache 5 engine=innodb; + +--echo ########################################### +--echo read_only query error. +--echo ########################################### +--error ER_OPEN_AS_READONLY +select next value for s1; +--error ER_OPEN_AS_READONLY +select next value for s1; +--error ER_OPEN_AS_READONLY +select next value for s1; diff --git a/mysql-test/suite/sql_sequence/replication-master.opt b/mysql-test/suite/sql_sequence/replication-master.opt new file mode 100644 index 0000000000000..bbea8eabc915c --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication-master.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 diff --git a/mysql-test/suite/sql_sequence/replication-slave.opt b/mysql-test/suite/sql_sequence/replication-slave.opt new file mode 100644 index 0000000000000..a4e068e4b43bf --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication-slave.opt @@ -0,0 +1 @@ +--binlog_format=row --query_cache_type=1 --read_only=true diff --git a/mysql-test/suite/sql_sequence/replication.result b/mysql-test/suite/sql_sequence/replication.result new file mode 100644 index 0000000000000..eed4c130a9bd1 --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication.result @@ -0,0 +1,1030 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; +set global read_only=on; +########################################### +master and slave sync sequence. +########################################### +use s_db; +create sequence s1; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +show create table s1; +Table Create Table +s1 CREATE SEQUENCE `s1` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +use s_db; +drop sequence s1; +########################################### +not support create table engine=sequence. +########################################### +create table t(id int)engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +create table t(id int)engine=innodb; +alter table t engine=sequence; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop table t; +########################################### +not support alter sequence table. +########################################### +create sequence s2; +alter table s2 add id int; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +alter table s2 add index ind_x(start); +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +drop sequence s2; +########################################### +support create sequence +########################################### +create table t_1(id int); +show create sequence t_1; +ERROR HY000: 's_db.t_1' is not SEQUENCE +drop table t_1; +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +insert into s2 values(0, 0, 1, 10, 1, 2, 1, 1, 0); +commit; +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 9 1 10 1 2 1 1 0 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 10 1 2 1 1 1 +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 10 1 2 1 1 1 +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 1 +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 1 10 1 2 1 1 1 +drop sequence s2; +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`rrval` bigint(21) NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +CREATE SEQUENCE `s2` ( +`currval` bigint(21) NOT NULL COMMENT 'current value', +`nextval` bigint(21) NOT NULL COMMENT 'next value', +`minvalue` bigint(21) NOT NULL COMMENT 'min value', +`maxvalue` bigint(21) NOT NULL COMMENT 'max value', +`start` bigint(21) NOT NULL COMMENT 'start value', +`increment` bigint(21) NOT NULL COMMENT 'increment value', +`cache` bigint(21) NOT NULL COMMENT 'cache size', +`cycle` bigint(21) NOT NULL COMMENT 'cycle state', +`round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=myisam DEFAULT CHARSET=latin1; +show create sequence s2; +Table Create Table +s2 CREATE SEQUENCE `s2` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +drop sequence s2; +########################################### +select sequence syntax test +########################################### +create sequence s2; +create table t2 (id int); +select * from s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select * from t2; +id +insert into t2 select nextval for s2; +commit; +select * for s2; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 9223372036854775807 1 1 10000 0 0 +select * for t2; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use +select * from s2, t2; +currval nextval minvalue maxvalue start increment cache cycle round id +0 10002 1 9223372036854775807 1 1 10000 0 0 1 +select * for s2, t2; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' t2' at line 1 +drop sequence s2; +drop table t2; +########################################### +support rename, not support truncate +########################################### +create sequence s2; +alter table s2 rename to s2_1; +ERROR HY000: Table storage engine 'sequence' does not support the create option 'SEQUENCE' +rename table s2 to s2_1; +select * for s2_1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +truncate table s2_1; +ERROR HY000: Table storage engine for 's2_1' doesn't have this option +rename table s2_1 to s2; +drop sequence s2; +########################################### +not support create temproary sequence. +########################################### +create temporary sequence s2; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sequence s2' at line 1 +########################################### +all invalid sequence value +########################################### +use s_db; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +cycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +cache 10000 +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 1 +maxvalue 100000 +increment by 1 +nocache +nocycle; +drop sequence s2; +create sequence s2 start with 1 +minvalue 5 +maxvalue 100000 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 5 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 1 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +create sequence s2 start with 1 +minvalue 5 +maxvalue 4 +increment by 0 +nocache +nocycle; +ERROR HY000: Sequence 's_db.s2' structure or number is invalid. +########################################### +global read lock prevent query sequence +########################################### +use s_db; +create sequence s_db.s1; +flush table with read lock; +select * for s1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +drop sequence s_db.s1; +########################################### +session setting +########################################### +use s_db; +create sequence s1; +set session sequence_read_skip_cache=true; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +0 +set session sequence_read_skip_cache=false; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 1 9223372036854775807 1 1 10000 0 0 +select nextval for s1; +nextval +4 +drop sequence s1; +########################################### +query cache test +########################################### +use s_db; +show global variables like 'query_cache_type'; +Variable_name Value +query_cache_type ON +show status like 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +show status like 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 1 +########################################### +priv test +########################################### +create sequence s_db.s1; +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +create sequence s_db.s2; +drop sequence s_db.s2; +select * for s_db.s1; +ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' +create sequence s_db.s2; +ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' +drop sequence s_db.s1; +########################################### +run out sequence value +########################################### +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +insert into t select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' has been run out. +commit; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +select * from t; +id +1111 +1 +2 +3 +4 +5 +use s_db; +drop sequence s_t; +drop table t; +########################################### +read_only prevent query sequence +########################################### +create sequence s_db.s1; +show global variables like 'read_only'; +Variable_name Value +read_only OFF +select * for s_db.s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 10000 0 0 +show global variables like 'read_only'; +Variable_name Value +read_only ON +select * for s_db.s1; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement +drop sequence s_db.s1; +########################################### +update based table +########################################### +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle; +use s_db; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 0 1 20 1 1 5 1 0 +select nextval for s_t; +nextval +1 +select nextval from s_t; +nextval +7 +------------------------------------------ +master update nextval; +------------------------------------------ +select nextval for s_t; +nextval +2 +update s_t set nextval= 11; +commit; +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +------------------------------------------ +show slave nextval; +------------------------------------------ +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +set session sequence_read_skip_cache=off; +select * for s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 11 1 20 1 1 5 1 0 +select * from s_t; +currval nextval minvalue maxvalue start increment cache cycle round +0 17 1 20 1 1 5 1 0 +------------------------------------------ +update into invalid sequence +------------------------------------------ +select nextval for s_t; +nextval +12 +update s_t set nextval= 11,start=10, minvalue=11; +commit; +create table t_1(id int); +insert into t_1 value(1111); +select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +insert into t_1 select nextval for s_t; +ERROR HY000: Sequence 's_db.s_t' structure or number is invalid. +commit; +select * from t_1; +id +1111 +------------------------------------------ +delete sequence row +------------------------------------------ +delete from s_t; +commit; +select nextval for s_t; +nextval +drop sequence s_t; +drop table t_1; +########################################### +test transaction context (innodb) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +select * from t_1; +id +1111 +1 +2 +2222 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +test transaction context (myisam) +########################################### +------------------------------------------ +transaction table and sequence +normal transaction commit +------------------------------------------ +use s_db; +set session sequence_read_skip_cache=off; +create sequence s_1 cache 5; +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 values(2222); +commit; +select * from t_1; +id +1111 +1 +2 +2222 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +------------------------------------------ +normal transaction rollback +------------------------------------------ +begin; +insert into t_1 values(3333); +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +insert into t_1 select nextval for s_1; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +select nextval for s_1; +nextval +11 +set session sequence_read_skip_cache=off; +use s_db; +select * from t_1; +id +1111 +1 +2 +2222 +3333 +3 +4 +5 +6 +7 +8 +9 +10 +use s_db; +drop sequence s_1; +drop table t_1; +########################################### +close binlog +########################################### +use s_db; +create sequence s1 cache 2; +select nextval for s1; +nextval +1 +select nextval for s1; +nextval +2 +select nextval for s1; +nextval +3 +select nextval for s1; +nextval +4 +commit; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +------------------------------------------ +close session binlog. +------------------------------------------ +set session sql_log_bin=off; +select nextval for s1; +nextval +5 +select nextval for s1; +nextval +6 +select nextval for s1; +nextval +7 +select nextval for s1; +nextval +8 +set session sql_log_bin=on; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 10 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 1 9223372036854775807 1 1 2 0 0 +use s_db; +drop sequence s1; +########################################### +statement binlog +########################################### +------------------------------------------ +set binlog_format=statement +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format STATEMENT +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +------------------------------------------ +set binlog_format=mixed +------------------------------------------ +set session sequence_read_skip_cache=off; +set session binlog_format=mixed; +show session variables like '%binlog_format%'; +Variable_name Value +binlog_format MIXED +create sequence s1 cache 2; +select nextval for s1; +ERROR HY000: Sequence requires binlog_format= row +set session binlog_format=row; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +use s_db; +select * from s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 4 1 9223372036854775807 1 1 2 0 0 +set session sequence_read_skip_cache=off; +use s_db; +drop sequence s1; +########################################### +test savepoint +########################################### +set session sequence_read_skip_cache=off; +set session binlog_format=row; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 select nextval for s1; +insert into t1 values(2222); +select * from t1; +id +1111 +1 +2 +3 +2222 +rollback to sp1; +select * from t1; +id +1111 +select nextval for s1; +nextval +4 +commit; +drop sequence s1; +drop table t1; +########################################### +create as +########################################### +set session sequence_read_skip_cache=off; +create sequence s1 cache 2; +create table t as select * for s1; +select * from t; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 9223372036854775807 1 1 2 0 0 +drop sequence s1; +drop table t; +########################################### +test proc +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create table t(id int)engine=innodb; +create procedure p1() +begin +create sequence s1 cache 2; +end// +create procedure p2() +begin +insert into t select nextval for s1; +commit; +end// +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); +select * from t; +id +1 +2 +3 +4 +use s_db; +select * from t; +id +1 +2 +3 +4 +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; +########################################### +test trigger +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; +CREATE TRIGGER tri_1 +before INSERT ON t2 FOR EACH ROW +BEGIN +INSERT INTO t1 select nextval for s1; +END// +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +select * from t2; +id +1111 +1111 +1111 +1111 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t2; +id +select * from t1; +id +select nextval for s1; +nextval +5 +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; +########################################### +test function +########################################### +set session sequence_read_skip_cache=off; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +CREATE function f1() returns int +BEGIN +INSERT INTO t1 select nextval for s1; +return (1); +END// +begin; +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select f1(); +f1() +1 +select * from t1; +id +1 +2 +3 +4 +rollback; +select * from t1; +id +select nextval for s1; +nextval +5 +drop function f1; +drop table t1; +drop sequence s1; +########################################### +test value boundary +########################################### +use s_db; +------------------------------------------ +round increment by round +------------------------------------------ +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 1 cycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 5 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 6 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 7 2 7 5 1 1 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 2 7 5 1 1 1 1 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 3 2 7 5 1 1 1 1 +drop sequence s1; +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 10 nocycle; +select nextval, round for s1; +nextval round +5 0 +select nextval, round for s1; +nextval round +6 0 +select nextval, round for s1; +nextval round +7 0 +select nextval, round for s1; +ERROR HY000: Sequence 's_db.s1' has been run out. +drop sequence s1; +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 nocache cycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 3 2 3 0 1 0 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 1 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 2 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 3 +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 1 1 3 2 3 0 1 4 +drop sequence s1; +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 cache 2 nocycle; +select * for s1; +currval nextval minvalue maxvalue start increment cache cycle round +0 2 1 3 2 3 2 0 0 +select * for s1; +ERROR HY000: Sequence 's_db.s1' has been run out. +drop sequence s1; +------------------------------------------ +beyond ulonglong maxvalue +------------------------------------------ +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 1 cycle; +select nextval, round for s1; +nextval round +9223372036854775805 0 +select nextval, round for s1; +nextval round +9223372036854775806 0 +select nextval, round for s1; +nextval round +9223372036854775807 0 +select nextval, round for s1; +nextval round +9223372036854775804 1 +select nextval, round for s1; +nextval round +9223372036854775805 1 +select nextval, round for s1; +nextval round +9223372036854775806 1 +select nextval, round for s1; +nextval round +9223372036854775807 1 +select nextval, round for s1; +nextval round +9223372036854775804 2 +select nextval, round for s1; +nextval round +9223372036854775805 2 +select nextval, round for s1; +nextval round +9223372036854775806 2 +drop sequence s1; +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775807 cache 10 cycle; +select nextval, round for s1; +nextval round +9223372036854775805 0 +select nextval, round for s1; +nextval round +9223372036854775806 0 +select nextval, round for s1; +nextval round +9223372036854775807 0 +select nextval, round for s1; +nextval round +9223372036854775804 1 +select nextval, round for s1; +nextval round +9223372036854775805 1 +select nextval, round for s1; +nextval round +9223372036854775806 1 +select nextval, round for s1; +nextval round +9223372036854775807 1 +select nextval, round for s1; +nextval round +9223372036854775804 2 +select nextval, round for s1; +nextval round +9223372036854775805 2 +select nextval, round for s1; +nextval round +9223372036854775806 2 +drop sequence s1; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; +include/rpl_end.inc diff --git a/mysql-test/suite/sql_sequence/replication.test b/mysql-test/suite/sql_sequence/replication.test new file mode 100644 index 0000000000000..bf7b6bbdbf768 --- /dev/null +++ b/mysql-test/suite/sql_sequence/replication.test @@ -0,0 +1,881 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/have_innodb.inc + +# +# This test is originally sequence.test from Jianwei modified for MariaDB +# To test basic sequence functionallity together with replication +# + +connection master; +create database s_db; +grant all on s_db.* to normal_1@'%' identified by 'pass'; +grant all on test.* to normal_2@'%' identified by 'pass'; +grant all on s_db.* to normal_3@'%' identified by 'pass'; +grant all on test.* to normal_4@'%' identified by 'pass'; + +--sync_slave_with_master + +connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT); +connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT); + +connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT); +connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT); + +connection slave; +set global read_only=on; + +--echo ########################################### +--echo master and slave sync sequence. +--echo ########################################### +connection master; +use s_db; + +create sequence s1; +show create table s1; + +--sync_slave_with_master +connection slave; +use s_db; +show create table s1; + +connection master; +use s_db; + +drop sequence s1; + +--echo ########################################### +--echo not support create table engine=sequence. +--echo ########################################### +connection master; + +--error ER_UNKNOWN_STORAGE_ENGINE +create table t(id int)engine=sequence; + +create table t(id int)engine=innodb; + +--error ER_UNKNOWN_STORAGE_ENGINE +alter table t engine=sequence; + +drop table t; +--echo ########################################### +--echo not support alter sequence table. +--echo ########################################### +connection master; + +create sequence s2; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add id int; + +--error ER_SEQUENCE_INVALID_TABLE_STRUCTURE +alter table s2 add index ind_x(start); +drop sequence s2; + +--echo ########################################### +--echo support create sequence +--echo ########################################### +connection master; + +create table t_1(id int); +--error ER_WRONG_OBJECT +show create sequence t_1; + +drop table t_1; + +--error ER_PARSE_ERROR +CREATE SEQUENCE `s2` ( + `currval` bigint(21) NOT NULL COMMENT 'current value', + `nextval` bigint(21) NOT NULL COMMENT 'next value', + `minvalue` bigint(21) NOT NULL COMMENT 'min value', + `maxvalue` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` bigint(21) NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB sequence=1; + +CREATE TABLE `s2` ( + `next_value` bigint(21) NOT NULL COMMENT 'next value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=InnoDB sequence=1; + +insert into s2 values(0, 1, 10, 1, 2, 1, 1, 0); +commit; +--error ER_PARSE_ERROR +select * for s2; +select NEXT VALUE for s2; +select NEXT VALUE for s2; +select NEXT VALUE for s2; +select * from s2; +commit; + +connection master; +--sync_slave_with_master +select * from s2; + +connection slave; +select * from s2; + + +connection master; +drop sequence s2; + +CREATE TABLE `s2` ( + `next_value` bigint(21) NOT NULL COMMENT 'next value', + `min_value` bigint(21) NOT NULL COMMENT 'min value', + `max_value` bigint(21) NOT NULL COMMENT 'max value', + `start` bigint(21) NOT NULL COMMENT 'start value', + `increment` bigint(21) NOT NULL COMMENT 'increment value', + `cache` bigint(21) NOT NULL COMMENT 'cache size', + `cycle` tinyint(1) unsigned NOT NULL COMMENT 'cycle state', + `round` bigint(21) NOT NULL COMMENT 'already how many round' +) ENGINE=myisam DEFAULT CHARSET=latin1 sequence=1; + +show create sequence s2; +drop sequence s2; + +--echo ########################################### +--echo select sequence syntax test +--echo ########################################### +connection master; +create sequence s2; +create table t2 (id int); + +select * from s2; +select * from t2; +insert into t2 select next value for s2; +commit; + +select NEXT VALUE for s2; +--error ER_NOT_SEQUENCE +select NEXT VALUE for t2; + +select * from s2, t2; + +--error ER_PARSE_ERROR +select * for s2; +--error ER_PARSE_ERROR +select * for s2, t2; + +connection master; +drop sequence s2; +drop table t2; + +--echo ########################################### +--echo support rename, not support truncate +--echo ########################################### +connection master; + +create sequence s2; + +alter table s2 rename to s2_1; +rename table s2_1 to s2_2; +show create sequence s2_2; +select * from s2_2; + +--error ER_ILLEGAL_HA +truncate table s2_2; +rename table s2_2 to s2; +drop sequence s2; + +--echo ########################################### +--echo all invalid sequence value +--echo ########################################### + +connection master; +use s_db; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + cycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + cache 10000 + nocycle; +drop sequence s2; +create sequence s2 start with 1 + minvalue 1 + maxvalue 100000 + increment by 1 + nocache + nocycle; +drop sequence s2; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 100000 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 5 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 1 + nocache + nocycle; + +--error ER_SEQUENCE_INVALID_DATA +create sequence s2 start with 1 + minvalue 5 + maxvalue 4 + increment by 0 + nocache + nocycle; + +--echo ########################################### +--echo global read lock prevent query sequence +--echo ########################################### +connection master; +use s_db; +create sequence s_db.s1; +flush table with read lock; +--error ER_CANT_UPDATE_WITH_READLOCK +select NEXT VALUE for s1; +unlock tables; +drop sequence s_db.s1; + +--echo ########################################### +--echo query cache test +--echo ########################################### +connection master; +use s_db; +flush status; +show global variables like 'query_cache_type'; + +show status like 'Qcache_hits'; +show status like 'Qcache_inserts'; + +--echo ########################################### +--echo priv test +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +select NEXT VALUE for s_db.s1; +create sequence s_db.s2; +drop sequence s_db.s2; + + +connection m_normal_2; +--error ER_TABLEACCESS_DENIED_ERROR +select NEXT VALUE for s_db.s1; +--error ER_TABLEACCESS_DENIED_ERROR +create sequence s_db.s2; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo run out sequence value +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 cache 2 maxvalue 5; +create table t(id int); +insert into t values(1111); +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +--error ER_SEQUENCE_RUN_OUT +insert into t select next value for s_t; +commit; +select * from t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t; + +connection m_normal_1; +use s_db; +drop sequence s_t; +drop table t; + +--echo ########################################### +--echo read_only prevent query sequence +--echo ########################################### +connection m_normal_1; +create sequence s_db.s1; +show global variables like 'read_only'; +select * from s_db.s1; + +connection s_normal_3; +show global variables like 'read_only'; +--error ER_OPTION_PREVENTS_STATEMENT +select next value for s_db.s1; + +connection m_normal_1; +drop sequence s_db.s1; + +--echo ########################################### +--echo update based table +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s_t start with 1 minvalue 1 maxvalue 20 increment by 1 cache 5 cycle engine=innodb; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from s_t; + + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select next_value from s_t; + +--echo ------------------------------------------ +--echo master update nextval; +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +update s_t set next_value= 11; +commit; + +select * from s_t; +SELECT NEXT VALUE for s_t; + +connection master; +--sync_slave_with_master + +--echo ------------------------------------------ +--echo show slave nextval; +--echo ------------------------------------------ +connection s_normal_3; +select * from s_t; + +connection m_normal_1; +select next value for s_t; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +select * from s_t; + + +--echo ------------------------------------------ +--echo update into invalid sequence +--echo ------------------------------------------ +connection m_normal_1; +select next value for s_t; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set min_value=11, max_value=9; +select * from s_t; +--error ER_SEQUENCE_INVALID_DATA +update s_t set next_value= 12, start=10, min_value=11, max_value=20; +select * from s_t; + +--echo ------------------------------------------ +--echo delete sequence row +--echo ------------------------------------------ +connection m_normal_1; +--error ER_ILLEGAL_HA +delete from s_t; +commit; + +select next value for s_t; + +connection m_normal_1; +drop sequence s_t; + +--echo ########################################### +--echo test transaction context (innodb) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5 engine=innodb; + +create table t_1(id int)engine=innodb; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; +select * from s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select * from s_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo test transaction context (myisam) +--echo ########################################### + +--echo ------------------------------------------ +--echo transaction table and sequence +--echo normal transaction commit +--echo ------------------------------------------ +connection m_normal_1; +use s_db; +create sequence s_1 cache 5; + +create table t_1(id int)engine=myisam; +begin; +insert into t_1 values(1111); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 values(2222); +commit; + +select * from t_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +--echo ------------------------------------------ +--echo normal transaction rollback +--echo ------------------------------------------ +connection m_normal_1; +begin; +insert into t_1 values(3333); +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; +insert into t_1 select next value for s_1; + +select * from t_1; +rollback; + +select * from t_1; +select next value for s_1; + +connection master; +--sync_slave_with_master + +connection s_normal_3; +use s_db; +select * from t_1; + +connection m_normal_1; +use s_db; +drop sequence s_1; +drop table t_1; + +--echo ########################################### +--echo close binlog +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +commit; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +--echo ------------------------------------------ +--echo close session binlog. +--echo ------------------------------------------ +connection master; +set session sql_log_bin=off; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; + +set session sql_log_bin=on; +select * from s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ########################################### +--echo statement binlog +--echo ########################################### +--echo ------------------------------------------ +--echo set binlog_format=statement +--echo ------------------------------------------ +connection master; +set session binlog_format=statement; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +--error ER_BINLOG_STMT_MODE_AND_ROW_ENGINE +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; + +--echo ------------------------------------------ +--echo set binlog_format=mixed +--echo ------------------------------------------ +connection master; +set session binlog_format=mixed; +show session variables like '%binlog_format%'; +create sequence s1 cache 2; +select next value for s1; + +set session binlog_format=row; +select next value for s1; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from s1; + +connection m_normal_1; +use s_db; +drop sequence s1; +--echo ########################################### +--echo test savepoint +--echo ########################################### +connection master; +--sync_slave_with_master +set session binlog_format=row; + +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +begin; +insert into t1 values(1111); +savepoint sp1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; +insert into t1 select next value for s1; + +insert into t1 values(2222); + +select * from t1; +rollback to sp1; +select * from t1; +select next value for s1; + +commit; + +drop sequence s1; +drop table t1; + +connection master; +--sync_slave_with_master + +--echo ########################################### +--echo create as +--echo ########################################### +connection m_normal_1; + +create sequence s1 cache 2; +create table t as select next value for s1; +select * from t; + +drop sequence s1; +drop table t; + +connection master; +--sync_slave_with_master + +--echo ########################################### +--echo test proc +--echo ########################################### +connection m_normal_1; +use s_db; +create table t(id int)engine=innodb; + +delimiter //; + +create procedure p1() +begin + create sequence s1 cache 2; +end// + +create procedure p2() +begin + insert into t select next value for s1; + commit; +end// + +delimiter ;// + +call p1(); +call p2(); +call p2(); +call p2(); +call p2(); + +select * from t; + +connection master; +--sync_slave_with_master + +connection slave; +use s_db; +select * from t; + +connection m_normal_1; +drop table t; +drop sequence s1; +drop procedure p1; +drop procedure p2; + +--echo ########################################### +--echo test trigger +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; +create table t2(id int)engine=innodb; + +delimiter //; +CREATE TRIGGER tri_1 + before INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO t1 select next value for s1; +END// +delimiter ;// + +begin; +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); +insert into t2 values(1111); + +select * from t2; +select * from t1; +rollback; +select * from t2; +select * from t1; + +select next value for s1; + + +drop trigger tri_1; +drop table t1; +drop table t2; +drop sequence s1; + +--echo ########################################### +--echo test function +--echo ########################################### +connection m_normal_1; +use s_db; +create sequence s1 cache 2; +create table t1(id int)engine=innodb; + +delimiter //; +CREATE function f1() returns int +BEGIN + INSERT INTO t1 select next value for s1; + return (1); +END// +delimiter ;// + +begin; +select f1(); +select f1(); +select f1(); +select f1(); + +select * from t1; +rollback; +select * from t1; + +select next value for s1; + +drop function f1; +drop table t1; +drop sequence s1; + +--echo ########################################### +--echo test value boundary +--echo ########################################### +connection m_normal_1; +use s_db; + +--echo ------------------------------------------ +--echo round increment by round +--echo ------------------------------------------ +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 1 cycle; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +select next value for s1; +drop sequence s1; + +create sequence s1 start with 5 minvalue 2 maxvalue 7 cache 10 nocycle; +select next value for s1; +select next value for s1; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +select * from s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 nocache cycle; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +select next value for s1; +select * from s1; +drop sequence s1; + +create sequence s1 start with 2 minvalue 1 maxvalue 3 increment by 3 cache 2 nocycle; +select next value for s1; +--error ER_SEQUENCE_RUN_OUT +select next value for s1; +drop sequence s1; + +--echo ------------------------------------------ +--echo beyond ulonglong maxvalue +--echo ------------------------------------------ +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775806 cache 1 cycle; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +drop sequence s1; + +create sequence s1 start with 9223372036854775805 minvalue 9223372036854775804 maxvalue 9223372036854775806 cache 10 cycle; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +select next value for s1, round from s1; +drop sequence s1; + +connection master; +use s_db; +drop database s_db; +drop user normal_1@'%'; +drop user normal_2@'%'; +drop user normal_3@'%'; +drop user normal_4@'%'; + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 052d30563b283..f93f340b4fc7e 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3063,9 +3063,9 @@ READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME PERFORMANCE_SCHEMA_MAX_STATEMENT_CLASSES SESSION_VALUE NULL -GLOBAL_VALUE 187 +GLOBAL_VALUE 189 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 187 +DEFAULT_VALUE 189 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT Maximum number of statement instruments. diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 42a813b07824b..c3a25c6bcced5 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -25,7 +25,7 @@ eval CREATE FUNCTION myfunc_nonexist RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval CREATE FUNCTION sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; +eval CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB eval CREATE FUNCTION lookup RETURNS STRING SONAME "$UDF_EXAMPLE_SO"; --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB @@ -238,7 +238,7 @@ DROP FUNCTION myfunc_double; --error ER_SP_DOES_NOT_EXIST DROP FUNCTION myfunc_nonexist; DROP FUNCTION myfunc_int; -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP FUNCTION lookup; DROP FUNCTION reverse_lookup; DROP FUNCTION avgcost; @@ -401,19 +401,19 @@ DROP TABLE const_len_bug; # --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval CREATE FUNCTION sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; +eval CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; CREATE TABLE t1 (a INT); CREATE TABLE t2 (a INT PRIMARY KEY); INSERT INTO t1 VALUES (4),(3),(2),(1); INSERT INTO t2 SELECT * FROM t1; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq ASC; -SELECT sequence() AS seq, a FROM t1 ORDER BY seq DESC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq ASC; +SELECT udf_sequence() AS seq, a FROM t1 ORDER BY seq DESC; -SELECT * FROM t1 WHERE a = sequence(); -SELECT * FROM t2 WHERE a = sequence(); +SELECT * FROM t1 WHERE a = udf_sequence(); +SELECT * FROM t2 WHERE a = udf_sequence(); -DROP FUNCTION sequence; +DROP FUNCTION udf_sequence; DROP TABLE t1,t2; # diff --git a/mysql-test/t/udf_notembedded.test b/mysql-test/t/udf_notembedded.test index bf54af7256c24..d66586040059b 100644 --- a/mysql-test/t/udf_notembedded.test +++ b/mysql-test/t/udf_notembedded.test @@ -5,10 +5,10 @@ # MDEV-8644 Using a UDF in a virtual column causes a crash when stopping the server # --replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB -eval create function sequence returns integer soname "$UDF_EXAMPLE_SO"; -create table t1 (n int key not null auto_increment, msg int as (sequence()) virtual); +eval create function udf_sequence returns integer soname "$UDF_EXAMPLE_SO"; +create table t1 (n int key not null auto_increment, msg int as (udf_sequence()) virtual); select * from t1; source include/restart_mysqld.inc; drop table t1; -drop function sequence; +drop function udf_sequence; diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c index 9916650308a25..b8d9442fbc99a 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 964cbd6894c51..1dfa313a70cee 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 99562faa0777e..e6d86860afdd7 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 103a33214aee8..1093f2cdd09cf 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -18,6 +18,7 @@ #include "sql_priv.h" #include "sql_class.h" #include "sql_table.h" +#include "ha_sequence.h" static int read_string(File file, uchar**to, size_t length) { @@ -46,33 +47,40 @@ static int read_string(File file, uchar**to, size_t length) engine_name is a LEX_STRING, where engine_name->str must point to a buffer of at least NAME_CHAR_LEN+1 bytes. - @retval FRMTYPE_ERROR error - @retval FRMTYPE_TABLE table - @retval FRMTYPE_VIEW view + @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise + + @retval TABLE_TYPE_UNKNOWN error + @retval TABLE_TYPE_TABLE table + @retval TABLE_TYPE_SEQUENCE sequence table + @retval TABLE_TYPE_VIEW view */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) +Table_type dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name, + bool *is_sequence) { File file; - uchar header[10]; //"TYPE=VIEW\n" it is 10 characters + uchar header[40]; //"TYPE=VIEW\n" it is 10 characters size_t error; - frm_type_enum type= FRMTYPE_ERROR; + Table_type type= TABLE_TYPE_UNKNOWN; uchar dbt; DBUG_ENTER("dd_frm_type"); - if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0) - DBUG_RETURN(FRMTYPE_ERROR); + *is_sequence= 0; + + if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) + < 0) + DBUG_RETURN(TABLE_TYPE_UNKNOWN); error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)); if (error) goto err; - if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header))) + if (!strncmp((char*) header, "TYPE=VIEW\n", 10)) { - type= FRMTYPE_VIEW; + type= TABLE_TYPE_VIEW; goto err; } - type= FRMTYPE_TABLE; + type= TABLE_TYPE_NORMAL; if (!is_binary_frm_header(header) || !engine_name) goto err; @@ -91,6 +99,9 @@ frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) } } + if (((header[39] >> 4) & 3) == HA_CHOICE_YES) + *is_sequence= 1; + /* read the true engine name */ { MY_STAT state; diff --git a/sql/datadict.h b/sql/datadict.h index 9b180a882f9b1..46dac394f0742 100644 --- a/sql/datadict.h +++ b/sql/datadict.h @@ -1,6 +1,7 @@ #ifndef DATADICT_INCLUDED #define DATADICT_INCLUDED -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, Oracle and/or its affiliates. + Copyright (c) 2017 MariaDB corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,11 +22,12 @@ Data dictionary API. */ -enum frm_type_enum +enum Table_type { - FRMTYPE_ERROR= 0, - FRMTYPE_TABLE, - FRMTYPE_VIEW + TABLE_TYPE_UNKNOWN, + TABLE_TYPE_NORMAL, /* Normal table */ + TABLE_TYPE_SEQUENCE, + TABLE_TYPE_VIEW }; /* @@ -35,11 +37,14 @@ enum frm_type_enum Prefer to use ha_table_exists() instead. To check whether it's an frm of a view, use dd_frm_is_view(). */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name); + +enum Table_type dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name, + bool *is_sequence); static inline bool dd_frm_is_view(THD *thd, char *path) { - return dd_frm_type(thd, path, NULL) == FRMTYPE_VIEW; + bool not_used2; + return dd_frm_type(thd, path, NULL, ¬_used2) == TABLE_TYPE_VIEW; } bool dd_recreate_table(THD *thd, const char *db, const char *table_name, diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7f156c80205e4..b27de9dad4d0b 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 70cd37607835a..70ec4ae8edbb4 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -429,6 +429,7 @@ class ha_partition :public handler 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 0000000000000..f3c4d8961a89e --- /dev/null +++ b/sql/ha_sequence.cc @@ -0,0 +1,417 @@ +/* + Copyright (c) 2017, Aliyun and/or its affiliates. + Copyright (c) 2017, MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "sql_list.h" +#include "table.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_plugin.h" +#include "mysql/plugin.h" +#include "sql_priv.h" +#include "sql_parse.h" +#include "sql_table.h" +#include "sql_update.h" +#include "sql_base.h" +#include "log_event.h" + +/* + Table flags we should inherit and disable from the original engine. + We add HA_STATS_RECORDS_IS_EXACT as ha_sequence::info() will ensure + that records is always 1 +*/ + +#define SEQUENCE_ENABLED_TABLE_FLAGS (HA_STATS_RECORDS_IS_EXACT | \ + HA_PERSISTENT_TABLE) +#define SEQUENCE_DISABLED_TABLE_FLAGS (HA_CAN_SQL_HANDLER | \ + HA_CAN_INSERT_DELAYED | \ + HA_BINLOG_STMT_CAPABLE) +handlerton *sql_sequence_hton; + +/* + Create a sequence handler +*/ + +ha_sequence::ha_sequence(handlerton *hton, TABLE_SHARE *share) + :handler(hton, share), sequence_locked(0) +{ + sequence= share->sequence; + DBUG_ASSERT(share->sequence); +} + +/** + Destructor method must remove the underlying handler +*/ +ha_sequence::~ha_sequence() +{ + delete file; +} + +/** + Sequence table open method + + @param name Path to file (dbname and tablename) + @param mode mode + @param flags Flags how to open file + + RETURN VALUES + @retval 0 Success + @retval != 0 Failure +*/ + +int ha_sequence::open(const char *name, int mode, uint flags) +{ + int error; + DBUG_ENTER("ha_sequence::open"); + DBUG_ASSERT(table->s == table_share && file); + + file->table= table; + if (!(error= file->open(name, mode, flags))) + { + /* + Copy values set by handler::open() in the underlying handler + Reuse original storage engine data for duplicate key reference + It would be easier to do this if we would have another handler + call: fixup_after_open()... + */ + ref= file->ref; + ref_length= file->ref_length; + dup_ref= file->dup_ref; + + /* + ha_open() sets the following for us. We have to set this for the + underlying handler + */ + file->cached_table_flags= file->table_flags(); + + file->reset_statistics(); + internal_tmp_table= file->internal_tmp_table= + MY_TEST(flags & HA_OPEN_INTERNAL_TABLE); + reset_statistics(); + + /* Don't try to read the inital row the call is part of create code */ + if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR))) + { + if ((error= table->s->sequence->read_initial_values(table))) + file->ha_close(); + } + } + DBUG_RETURN(error); +} + +/* + Clone the sequence. Needed if table is used by range optimization + (Very, very unlikely) +*/ + +handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root) +{ + ha_sequence *new_handler; + DBUG_ENTER("ha_sequence::clone"); + if (!(new_handler= new (mem_root) ha_sequence(ht, table_share))) + DBUG_RETURN(NULL); + + /* + Allocate new_handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + goto err; + + if (new_handler->ha_open(table, name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL)) + goto err; + + /* Reuse original storage engine data for duplicate key reference */ + new_handler->ref= file->ref; + new_handler->ref_length= file->ref_length; + new_handler->dup_ref= file->dup_ref; + + DBUG_RETURN((handler*) new_handler); + +err: + delete new_handler; + DBUG_RETURN(NULL); +} + + +/* + Map the create table to the original storage engine +*/ + +int ha_sequence::create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) +{ + DBUG_ASSERT(create_info->sequence); + /* Sequence tables has one and only one row */ + create_info->max_rows= create_info->min_rows= 1; + return (file->create(name, form, create_info)); +} + +/** + Sequence write row method. + + A sequence table has only one row. Any inserts in the table + will update this row. + + @retval 0 Success + @retval != 0 Failure + + NOTES: + sequence_locked is set if we are called from SEQUENCE::next_value + In this case the mutex is already locked and we should not update + the sequence with 'buf' as the sequence object is already up to date. +*/ + +int ha_sequence::write_row(uchar *buf) +{ + int error; + DBUG_ENTER("ha_sequence::write_row"); + DBUG_ASSERT(table->record[0] == buf); + + row_already_logged= 0; + if (!sequence->initialized) + { + /* This calls is from ha_open() as part of create table */ + DBUG_RETURN(file->write_row(buf)); + } + + /* + User tries to write a row + - Check that row is an accurate object + - Update the first row in the table + */ + + sequence_definition tmp_seq; + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust()) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + + /* + Lock sequence to ensure that no one can come in between + while sequence, table and binary log are updated. + */ + if (!sequence_locked) // If not from next_value() + sequence->lock(); + + if (!(error= file->update_first_row(buf))) + { + Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; + if (!sequence_locked) + sequence->copy(&tmp_seq); + rows_changed++; + /* We have to do the logging while we hold the sequence mutex */ + error= binlog_log_row(table, 0, buf, log_func); + row_already_logged= 1; + } + + sequence->all_values_used= 0; + if (!sequence_locked) + sequence->unlock(); + DBUG_RETURN(error); +} + + +int ha_sequence::update_row(const uchar *old_data, uchar *new_data) +{ + int error; + sequence_definition tmp_seq; + DBUG_ENTER("ha_sequence::update_row"); + DBUG_ASSERT(new_data == table->record[0]); + + row_already_logged= 0; + + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust()) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + + /* + Lock sequence to ensure that no one can come in between + while sequence, table and binary log is updated. + */ + sequence->lock(); + if (!(error= file->update_row(old_data, new_data))) + { + sequence->copy(&tmp_seq); + rows_changed++; + /* We have to do the logging while we hold the sequence mutex */ + error= binlog_log_row(table, old_data, new_data, + Update_rows_log_event::binlog_row_logging_function); + row_already_logged= 1; + } + sequence->all_values_used= 0; + sequence->unlock(); + DBUG_RETURN(error); +} + + +/* + Inherit the sequence base table flags. +*/ + +handler::Table_flags ha_sequence::table_flags() const +{ + DBUG_ENTER("ha_sequence::table_flags"); + DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) | + SEQUENCE_ENABLED_TABLE_FLAGS); +} + + +int ha_sequence::info(uint flag) +{ + DBUG_ENTER("ha_sequence::info"); + file->info(flag); + /* Inform optimizer that we have always only one record */ + stats= file->stats; + stats.records= 1; + DBUG_RETURN(false); +} + +int ha_sequence::external_lock(THD *thd, int lock_type) +{ + int error= file->external_lock(thd, lock_type); + + /* + Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in + handler.cc when we later call it with file->ha_..() + */ + file->m_lock_type= lock_type; + return error; +} + +/* + Squence engine error deal method +*/ + +void ha_sequence::print_error(int error, myf errflag) +{ + char *sequence_db= table_share->db.str; + char *sequence_name= table_share->table_name.str; + DBUG_ENTER("ha_sequence::print_error"); + + switch (error) { + case HA_ERR_SEQUENCE_INVALID_DATA: + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db, + sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_SEQUENCE_RUN_OUT: + { + my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_WRONG_COMMAND: + my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", table_share->db.str, + table_share->table_name.str); + DBUG_VOID_RETURN; + } + file->print_error(error, errflag); + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Sequence plugin interface +*****************************************************************************/ + +/* + Create an new handler +*/ + +static handler *sequence_create_handler(handlerton *hton, + TABLE_SHARE *share, + MEM_ROOT *mem_root) +{ + DBUG_ENTER("sequence_create_handler"); + DBUG_RETURN(new (mem_root) ha_sequence(hton, share)); +} + + +/* + Sequence engine end. + + SYNOPSIS + sequence_end() + p handlerton. + type panic type. + RETURN VALUES + 0 Success + !=0 Failure +*/ +static int sequence_end(handlerton* hton, + ha_panic_function type __attribute__((unused))) +{ + DBUG_ENTER("sequence_end"); + DBUG_RETURN(0); +} + + +/* + Sequence engine init. + + SYNOPSIS + sequence_initialize() + + @param p handlerton. + + retval 0 Success + retval !=0 Failure +*/ + +static int sequence_initialize(void *p) +{ + handlerton *local_sequence_hton= (handlerton *)p; + DBUG_ENTER("sequence_initialize"); + + local_sequence_hton->state= SHOW_OPTION_YES; + local_sequence_hton->db_type= DB_TYPE_SEQUENCE; + local_sequence_hton->create= sequence_create_handler; + local_sequence_hton->panic= sequence_end; + local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE | + HTON_HIDDEN | + HTON_TEMPORARY_NOT_SUPPORTED | + HTON_ALTER_NOT_SUPPORTED | + HTON_NO_PARTITION); + DBUG_RETURN(0); +} + + +static struct st_mysql_storage_engine sequence_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +maria_declare_plugin(sql_sequence) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &sequence_storage_engine, + "SQL_SEQUENCE", + "jianwei.zhao @ Aliyun & Monty @ MariaDB corp", + "Sequence Storage Engine for CREATE SEQUENCE", + PLUGIN_LICENSE_GPL, + sequence_initialize, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100, /* 1.0 */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */ +} +maria_declare_plugin_end; diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h new file mode 100644 index 0000000000000..aeb7ff3fd58d9 --- /dev/null +++ b/sql/ha_sequence.h @@ -0,0 +1,138 @@ +#ifndef HA_SEQUENCE_INCLUDED +#define HA_SEQUENCE_INCLUDED +/* + Copyright (c) 2017 Aliyun and/or its affiliates. + Copyright (c) 2017 MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_sequence.h" +#include "table.h" +#include "handler.h" + +extern handlerton *sql_sequence_hton; + +/* + Sequence engine handler. + + The sequence engine is a logic engine. It doesn't store any data. + All the sequence data stored into the base table which must support + non rollback writes (HA_CAN_TABLES_WITHOUT_ROLLBACK) + + The sequence data (SEQUENCE class) is stored in TABLE_SHARE->sequence + + TABLE RULES: + 1. When table is created, one row is automaticlly inserted into + the table. The table will always have one and only one row. + 2. Any inserts or updates to the table will be validated. + 3. Inserts will overwrite the original row. + 4. DELETE and TRUNCATE will not affect the table. + Instead a warning will be given. + 5. Cache will be reset for any updates. + + CACHE RULES: + SEQUENCE class is used to cache values that sequence defined. + 1. If hit cache, we can query back the sequence nextval directly + instead of reading the underlying table. + + 2. When run out of values, the sequence engine will reserve new values + in update the base table. + + 3. The cache is invalidated if any update on based table. +*/ + +class ha_sequence :public handler +{ +private: + handler *file; + SEQUENCE *sequence; /* From table_share->sequence */ + +public: + ha_sequence(handlerton *hton, TABLE_SHARE *share); + ~ha_sequence(); + + /* virtual function that are re-implemented for sequence */ + int open(const char *name, int mode, uint test_if_locked); + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); + handler *clone(const char *name, MEM_ROOT *mem_root); + int write_row(uchar *buf); + int update_row(const uchar *old_data, uchar *new_data); + Table_flags table_flags() const; + /* One can't delete from sequence engine */ + int delete_row(const uchar *buf) + { return HA_ERR_WRONG_COMMAND; } + /* One can't delete from sequence engine */ + int truncate() + { return HA_ERR_WRONG_COMMAND; } + /* Can't use query cache */ + uint8 table_cache_type() + { return HA_CACHE_TBL_NOCACHE; } + void print_error(int error, myf errflag); + int info(uint); + LEX_STRING *engine_name() { return hton_name(file->ht); } + int external_lock(THD *thd, int lock_type); + + /* Functions that are directly mapped to the underlying handler */ + int rnd_init(bool scan) + { return file->rnd_init(scan); } + int rnd_next(uchar *buf) + { return file->rnd_next(buf); } + int rnd_end() + { return file->rnd_end(); } + int rnd_pos(uchar *buf, uchar *pos) + { return file->rnd_pos(buf, pos); } + void position(const uchar *record) + { return file->position(record); } + const char *table_type() const + { return file->table_type(); } + ulong index_flags(uint inx, uint part, bool all_parts) const + { return file->index_flags(inx, part, all_parts); } + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type) + { return file->store_lock(thd, to, lock_type); } + int close(void) + { return file->close(); } + const char **bas_ext() const + { return file->bas_ext(); } + int delete_table(const char*name) + { return file->delete_table(name); } + int rename_table(const char *from, const char *to) + { return file->rename_table(from, to); } + void unbind_psi() + { return file->unbind_psi(); } + void rebind_psi() + { return file->rebind_psi(); } + + bool auto_repair(int error) const + { return file->auto_repair(error); } + int repair(THD* thd, HA_CHECK_OPT* check_opt) + { return file->repair(thd, check_opt); } + bool check_and_repair(THD *thd) + { return file->check_and_repair(thd); } + bool is_crashed() const + { return file->is_crashed(); } + + /* New methods */ + void register_original_handler(handler *file_arg) + { + file= file_arg; + init(); /* Update cached_table_flags */ + } + + /* To inform handler that sequence is already locked by called */ + bool sequence_locked; +}; +#endif diff --git a/sql/handler.cc b/sql/handler.cc index a6016646d3c8b..5d0ec99e97819 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -41,6 +41,7 @@ #include #include "debug_sync.h" // DEBUG_SYNC #include "sql_audit.h" +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -291,7 +292,6 @@ handler *get_ha_partition(partition_info *part_info) } #endif - static const char **handler_errmsgs; C_MODE_START @@ -618,7 +618,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin) /* This is entirely for legacy. We will create a new "disk based" hton and a "memory" hton which will be configurable longterm. We should be able to - remove partition and myisammrg. + remove partition. */ switch (hton->db_type) { case DB_TYPE_HEAP: @@ -630,6 +630,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin) case DB_TYPE_PARTITION_DB: partition_hton= hton; break; + case DB_TYPE_SEQUENCE: + sql_sequence_hton= hton; + break; default: break; }; @@ -2426,6 +2429,11 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) return NULL; } +LEX_STRING *handler::engine_name() +{ + return hton_name(ht); +} + double handler::keyread_time(uint index, uint ranges, ha_rows rows) { @@ -2549,6 +2557,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, } reset_statistics(); internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE); + DBUG_RETURN(error); } @@ -2797,10 +2806,11 @@ int handler::ha_rnd_init_with_error(bool scan) /** - Read first row (only) from a table. + Read first row (only) from a table. Used for reading tables with + only one row, either based on table statistics or if table is a SEQUENCE. - This is never called for InnoDB tables, as these table types - has the HA_STATS_RECORDS_IS_EXACT set. + This is never called for normal InnoDB tables, as these table types + does not have HA_STATS_RECORDS_IS_EXACT set. */ int handler::read_first_row(uchar * buf, uint primary_key) { @@ -3992,7 +4002,7 @@ void handler::mark_trx_read_write_internal() */ if (ha_info->is_started()) { - DBUG_ASSERT(has_transactions()); + DBUG_ASSERT(has_transaction_manager()); /* table_share can be NULL in ha_delete_table(). See implementation of standalone function ha_delete_table() in sql_base.cc. @@ -5033,22 +5043,28 @@ class Table_exists_error_handler : public Internal_error_handler loaded, frm is invalid), the return value will be true, but *hton will be NULL. */ + bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton) + handlerton **hton, bool *is_sequence) { handlerton *dummy; + bool dummy2; DBUG_ENTER("ha_table_exists"); if (hton) *hton= 0; else if (engines_with_discover) hton= &dummy; + if (!is_sequence) + is_sequence= &dummy2; + *is_sequence= 0; TDC_element *element= tdc_lock_share(thd, db, table_name); if (element && element != MY_ERRPTR) { if (hton) *hton= element->share->db_type(); + *is_sequence= element->share->table_type == TABLE_TYPE_SEQUENCE; tdc_unlock_share(element); DBUG_RETURN(TRUE); } @@ -5066,7 +5082,7 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, char engine_buf[NAME_CHAR_LEN + 1]; LEX_STRING engine= { engine_buf, 0 }; - if (dd_frm_type(thd, path, &engine) != FRMTYPE_VIEW) + if (dd_frm_type(thd, path, &engine, is_sequence) != TABLE_TYPE_VIEW) { plugin_ref p= plugin_lock_by_name(thd, &engine, MYSQL_STORAGE_ENGINE_PLUGIN); *hton= p ? plugin_hton(p) : NULL; @@ -5089,7 +5105,6 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, DBUG_RETURN(TRUE); } - if (need_full_discover_for_existence) { TABLE_LIST table; @@ -5778,8 +5793,6 @@ static int write_locked_table_maps(THD *thd) } -typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); - static int check_wsrep_max_ws_rows(); static int binlog_log_row_internal(TABLE* table, @@ -5820,10 +5833,10 @@ static int binlog_log_row_internal(TABLE* table, return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } -static inline int binlog_log_row(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func) { if (!table->file->check_table_binlog_row_based(1)) return 0; @@ -5975,7 +5988,7 @@ int handler::ha_write_row(uchar *buf) { error= write_row(buf); }) MYSQL_INSERT_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, 0, buf, log_func); @@ -6007,7 +6020,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) { error= update_row(old_data, new_data);}) MYSQL_UPDATE_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, old_data, new_data, log_func); @@ -6015,6 +6028,28 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) return error; } +/* + Update first row. Only used by sequence tables +*/ + +int handler::update_first_row(uchar *new_data) +{ + int error; + if (!(error= ha_rnd_init(1))) + { + int end_error; + if (!(error= ha_rnd_next(table->record[1]))) + error= update_row(table->record[1], new_data); + end_error= ha_rnd_end(); + if (!error) + error= end_error; + /* Logging would be wrong if update_row works but ha_rnd_end fails */ + DBUG_ASSERT(!end_error || error != 0); + } + return error; +} + + int handler::ha_delete_row(const uchar *buf) { int error; diff --git a/sql/handler.h b/sql/handler.h index 3b8829a4b83c1..236e39f5dde99 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,9 +42,11 @@ #include #include #include +#include "sql_sequence.h" class Alter_info; class Virtual_column_info; +class sequence_definition; // the following is for checking tables @@ -261,6 +263,24 @@ enum enum_alter_inplace_result { */ #define HA_CONCURRENT_OPTIMIZE (1ULL << 46) +/* + If the storage engine support tables that will not roll back on commit + In addition the table should not lock rows and support READ and WRITE + UNCOMMITTED. + This is useful for implementing things like SEQUENCE but can also in + the future be useful to do logging that should never roll back. +*/ +#define HA_CAN_TABLES_WITHOUT_ROLLBACK (1ULL << 47) + +/* + Mainly for usage by SEQUENCE engine. Setting this flag means + that the table will never roll back and that all operations + for this table should stored in the non transactional log + space that will always be written, even on rollback. +*/ + +#define HA_PERSISTENT_TABLE (1ULL << 48) + /* Set of all binlog flags. Currently only contain the capabilities flags. @@ -380,6 +400,7 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_TMP_TABLE 1U #define HA_CREATE_TMP_ALTER 8U +#define HA_LEX_CREATE_SEQUENCE 16U #define HA_MAX_REC_LENGTH 65535 @@ -434,7 +455,8 @@ enum legacy_db_type DB_TYPE_PERFORMANCE_SCHEMA=28, DB_TYPE_ARIA=42, DB_TYPE_TOKUDB=43, - DB_TYPE_FIRST_DYNAMIC=44, + DB_TYPE_SEQUENCE=44, + DB_TYPE_FIRST_DYNAMIC=45, DB_TYPE_DEFAULT=127 // Must be last }; /* @@ -522,6 +544,8 @@ given at all. */ */ #define HA_CREATE_USED_STATS_SAMPLE_PAGES (1UL << 24) +/* Create a sequence */ +#define HA_CREATE_USED_SEQUENCE (1UL << 25) /* This is master database for most of system tables. However there @@ -1684,6 +1708,7 @@ struct Table_scope_and_contents_source_st engine_option_value *option_list; ///< list of table create options enum_stats_auto_recalc stats_auto_recalc; bool varchar; ///< 1 if table has a VARCHAR + bool sequence; // If SEQUENCE=1 was used List *check_constraint_list; @@ -1697,6 +1722,7 @@ struct Table_scope_and_contents_source_st TABLE_LIST *pos_in_locked_tables; MDL_ticket *mdl_ticket; bool table_was_deleted; + sequence_definition *seq_create_info; void init() { @@ -2646,6 +2672,8 @@ class handler :public Sql_alloc bool mark_trx_read_write_done; /* mark_trx_read_write was called */ bool check_table_binlog_row_based_done; /* check_table_binlog.. was called */ bool check_table_binlog_row_based_result; /* cached check_table_binlog... */ + /* Set to 1 if handler logged last insert/update/delete operation */ + bool row_already_logged; /* TRUE <=> the engine guarantees that returned records are within the range being scanned. @@ -2748,6 +2776,7 @@ class handler :public Sql_alloc mark_trx_read_write_done(0), check_table_binlog_row_based_done(0), check_table_binlog_row_based_result(0), + row_already_logged(0), in_range_check_pushed_down(FALSE), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), keyread(MAX_KEY), @@ -2985,8 +3014,24 @@ class handler :public Sql_alloc virtual double keyread_time(uint index, uint ranges, ha_rows rows); virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } + + /* + True if changes to the table is persistent (no rollback) + This is manly used to decide how to log changes to the table in + the binary log. + */ bool has_transactions() - { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; } + { + return ((ha_table_flags() & (HA_NO_TRANSACTIONS | HA_PERSISTENT_TABLE)) + == 0); + } + /* + True if the underlaying table doesn't support transactions + */ + bool has_transaction_manager() + { + return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0); + } /** This method is used to analyse the error to see whether the error @@ -3905,7 +3950,7 @@ class handler :public Sql_alloc return 0; } - LEX_STRING *engine_name() { return hton_name(ht); } + virtual LEX_STRING *engine_name(); TABLE* get_table() { return table; } TABLE_SHARE* get_table_share() { return table_share; } @@ -3997,6 +4042,12 @@ class handler :public Sql_alloc return HA_ERR_WRONG_COMMAND; } + /* + Optimized function for updating the first row. Only used by sequence + tables + */ + virtual int update_first_row(uchar *new_data); + virtual int delete_row(const uchar *buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; @@ -4059,6 +4110,7 @@ class handler :public Sql_alloc enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } friend class ha_partition; + friend class ha_sequence; public: /** This method is similar to update_row, however the handler doesn't need @@ -4289,7 +4341,7 @@ int ha_discover_table(THD *thd, TABLE_SHARE *share); int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, Discovered_table_list *result, bool reusable); bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton= 0); + handlerton **hton= 0, bool *is_sequence= 0); #endif /* key cache */ @@ -4347,6 +4399,11 @@ inline const char *table_case_name(HA_CREATE_INFO *info, const char *name) return ((lower_case_table_names == 2 && info->alias) ? info->alias : name); } +typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func); #define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \ { \ diff --git a/sql/item_func.cc b/sql/item_func.cc index 7eeb9547c89fe..8b52ed3c613ab 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -53,6 +53,7 @@ #include "sp.h" #include "set_var.h" #include "debug_sync.h" +#include "sql_base.h" #ifdef NO_EMBEDDED_ACCESS_CHECKS #define sp_restore_security_context(A,B) while (0) {} @@ -6939,3 +6940,147 @@ longlong Item_func_cursor_rowcount::val_int() sp_cursor *c= get_open_cursor_or_error(); return !(null_value= !c) ? c->row_count() : 0; } + +/***************************************************************************** + SEQUENCE functions +*****************************************************************************/ + +longlong Item_func_nextval::val_int() +{ + longlong value; + int error; + const char *key; + TABLE *table= table_list->table; + uint length= get_table_def_key(table_list, &key); + THD *thd= table->in_use; + SEQUENCE_LAST_VALUE *entry; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ASSERT(table && table->s->sequence); + DBUG_ENTER("Item_func_nextval::val_int"); + + if (table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + if (!(key= (char*) my_memdup(key, length, MYF(MY_WME))) || + !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length))) + { + /* EOM, error given */ + my_free((char*) key); + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + if (my_hash_insert(&thd->sequences, (uchar*) entry)) + { + /* EOM, error given */ + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + } + entry->null_value= null_value= 0; + value= table->s->sequence->next_value(table,0, &error); + entry->value= value; + entry->set_version(table); + + if (error) // Warning already printed + entry->null_value= null_value= 1; // For not strict mode + DBUG_RETURN(value); +} + + +/* Print for nextval and lastval */ + +void Item_func_nextval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= table_list->db, *t_name= table_list->table_name; + bool use_db_name= d_name && d_name[0]; + THD *thd= current_thd; + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, d_name, (uint)strlen(d_name)); + str->append('.'); + } + append_identifier(thd, str, t_name, (uint) strlen(t_name)); + str->append(')'); +} + + +/* Return last used value for sequence or NULL if sequence hasn't been used */ + +longlong Item_func_lastval::val_int() +{ + const char *key; + SEQUENCE_LAST_VALUE *entry; + uint length= get_table_def_key(table_list, &key); + THD *thd= table_list->table->in_use; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ENTER("Item_func_lastval::val_int"); + + if (table_list->table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + /* Sequence not used */ + null_value= 1; + DBUG_RETURN(0); + } + if (entry->check_version(table_list->table)) + { + /* Table droped and re-created, remove current version */ + my_hash_delete(&thd->sequences, (uchar*) entry); + null_value= 1; + DBUG_RETURN(0); + } + + null_value= entry->null_value; + DBUG_RETURN(entry->value); +} diff --git a/sql/item_func.h b/sql/item_func.h index 9d948a0334312..0b398adb9371c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2769,6 +2769,48 @@ class Item_func_last_value :public Item_func }; +/* 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(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(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 9918683e60b70..e67b207a75d87 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -160,6 +160,7 @@ static SYMBOL symbols[] = { { "CURRENT_USER", SYM(CURRENT_USER)}, { "CURSOR", SYM(CURSOR_SYM)}, { "CURSOR_NAME", SYM(CURSOR_NAME_SYM)}, + { "CYCLE", SYM(CYCLE_SYM)}, { "DATA", SYM(DATA_SYM)}, { "DATABASE", SYM(DATABASE)}, { "DATABASES", SYM(DATABASES)}, @@ -287,6 +288,7 @@ static SYMBOL symbols[] = { { "IMPORT", SYM(IMPORT)}, { "INTERSECT", SYM(INTERSECT_SYM)}, { "IN", SYM(IN_SYM)}, + { "INCREMENT", SYM(INCREMENT_SYM)}, { "INDEX", SYM(INDEX_SYM)}, { "INDEXES", SYM(INDEXES)}, { "INFILE", SYM(INFILE)}, @@ -324,6 +326,7 @@ static SYMBOL symbols[] = { { "LANGUAGE", SYM(LANGUAGE_SYM)}, { "LAST", SYM(LAST_SYM)}, { "LAST_VALUE", SYM(LAST_VALUE)}, + { "LASTVAL", SYM(LASTVAL_SYM)}, { "LEADING", SYM(LEADING)}, { "LEAVE", SYM(LEAVE_SYM)}, { "LEAVES", SYM(LEAVES)}, @@ -379,7 +382,7 @@ static SYMBOL symbols[] = { { "MAX_STATEMENT_TIME", SYM(MAX_STATEMENT_TIME_SYM)}, { "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)}, { "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)}, - { "MAXVALUE", SYM(MAX_VALUE_SYM)}, + { "MAXVALUE", SYM(MAXVALUE_SYM)}, { "MEDIUM", SYM(MEDIUM_SYM)}, { "MEDIUMBLOB", SYM(MEDIUMBLOB)}, { "MEDIUMINT", SYM(MEDIUMINT)}, @@ -393,6 +396,7 @@ static SYMBOL symbols[] = { { "MINUTE", SYM(MINUTE_SYM)}, { "MINUTE_MICROSECOND", SYM(MINUTE_MICROSECOND_SYM)}, { "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM)}, + { "MINVALUE", SYM(MINVALUE_SYM)}, { "MIN_ROWS", SYM(MIN_ROWS)}, { "MOD", SYM(MOD_SYM)}, { "MODE", SYM(MODE_SYM)}, @@ -412,7 +416,12 @@ static SYMBOL symbols[] = { { "NCHAR", SYM(NCHAR_SYM)}, { "NEW", SYM(NEW_SYM)}, { "NEXT", SYM(NEXT_SYM)}, + { "NEXTVAL", SYM(NEXTVAL_SYM)}, { "NO", SYM(NO_SYM)}, + { "NOMAXVALUE", SYM(NOMAXVALUE_SYM)}, + { "NOMINVALUE", SYM(NOMINVALUE_SYM)}, + { "NOCACHE", SYM(NOCACHE_SYM)}, + { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, @@ -466,6 +475,7 @@ static SYMBOL symbols[] = { { "PREPARE", SYM(PREPARE_SYM)}, { "PRESERVE", SYM(PRESERVE_SYM)}, { "PREV", SYM(PREV_SYM)}, + { "PREVIOUS", SYM(PREVIOUS_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, { "PROCEDURE", SYM(PROCEDURE_SYM)}, @@ -546,6 +556,7 @@ static SYMBOL symbols[] = { { "SELECT", SYM(SELECT_SYM)}, { "SENSITIVE", SYM(SENSITIVE_SYM)}, { "SEPARATOR", SYM(SEPARATOR_SYM)}, + { "SEQUENCE", SYM(SEQUENCE_SYM)}, { "SERIAL", SYM(SERIAL_SYM)}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM)}, { "SESSION", SYM(SESSION_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index a51c34365fa87..12de6ae061639 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -261,7 +261,7 @@ static void track_table_access(THD *thd, TABLE **tables, size_t count) if (t) tst->add_trx_state(thd, t->reginfo.lock_type, - t->file->has_transactions()); + t->file->has_transaction_manager()); } } } @@ -439,10 +439,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) This will work even if get_lock_data fails (next unlock will free all) */ -void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count) +void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag) { MYSQL_LOCK *sql_lock= - get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD); + get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag); if (sql_lock) mysql_unlock_tables(thd, sql_lock, 0); } @@ -539,7 +539,7 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) DBUG_ASSERT(table->lock_position == i); /* Unlock the table. */ - mysql_unlock_some_tables(thd, &table, /* table count */ 1); + mysql_unlock_some_tables(thd, &table, /* table count */ 1, 0); /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; @@ -733,6 +733,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) @param flags One of: - GET_LOCK_UNLOCK : If we should send TL_IGNORE to store lock - GET_LOCK_STORE_LOCKS : Store lock info in TABLE + - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock) */ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) @@ -750,7 +751,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) TABLE *t= table_ptr[i]; if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE && - t->s->tmp_table != INTERNAL_TMP_TABLE) + t->s->tmp_table != INTERNAL_TMP_TABLE && + (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0)) { lock_count+= t->file->lock_count(); table_count++; @@ -781,7 +783,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) THR_LOCK_DATA **locks_start; table= table_ptr[i]; if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE || - table->s->tmp_table == INTERNAL_TMP_TABLE) + table->s->tmp_table == INTERNAL_TMP_TABLE || + ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence)) continue; lock_type= table->reginfo.lock_type; DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); diff --git a/sql/lock.h b/sql/lock.h index 341d7a20f9f0b..35cb3043d5706 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 a6f2de2c6c576..c7cbff9c61598 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4075,10 +4075,12 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If we are using CREATE ... SELECT or if we are a slave executing BEGIN...COMMIT (generated by CREATE...SELECT) we @@ -5318,7 +5320,8 @@ START SLAVE; . Query: '%s'", expected_error, thd->query()); has already been dropped. To ignore such irrelevant "table does not exist errors", we silently clear the error if TEMPORARY was used. */ - if (thd->lex->sql_command == SQLCOM_DROP_TABLE && + if ((thd->lex->sql_command == SQLCOM_DROP_TABLE || + thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) && thd->lex->tmp_table() && thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR && !expected_error) @@ -11322,7 +11325,8 @@ Annotate_rows_log_event::Annotate_rows_log_event(THD *thd, bool direct) : Log_event(thd, 0, using_trans), m_save_thd_query_txt(0), - m_save_thd_query_len(0) + m_save_thd_query_len(0), + m_used_query_txt(0) { m_query_txt= thd->query(); m_query_len= thd->query_length(); @@ -11336,7 +11340,8 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, const Format_description_log_event *desc) : Log_event(buf, desc), m_save_thd_query_txt(0), - m_save_thd_query_len(0) + m_save_thd_query_len(0), + m_used_query_txt(0) { m_query_len= event_len - desc->common_header_len; m_query_txt= (char*) buf + desc->common_header_len; @@ -11344,10 +11349,14 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, Annotate_rows_log_event::~Annotate_rows_log_event() { + DBUG_ENTER("Annotate_rows_log_event::~Annotate_rows_log_event"); #ifndef MYSQL_CLIENT if (m_save_thd_query_txt) thd->set_query(m_save_thd_query_txt, m_save_thd_query_len); + else if (m_used_query_txt) + thd->reset_query(); #endif + DBUG_VOID_RETURN; } int Annotate_rows_log_event::get_data_size() @@ -11431,6 +11440,7 @@ int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi) { m_save_thd_query_txt= thd->query(); m_save_thd_query_len= thd->query_length(); + m_used_query_txt= 1; thd->set_query(m_query_txt, m_query_len); return 0; } @@ -11982,7 +11992,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id); table_list->updating= 1; - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id)); diff --git a/sql/log_event.h b/sql/log_event.h index 2bc0a85854283..e45151c85642e 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3908,6 +3908,7 @@ class Annotate_rows_log_event: public Log_event uint m_query_len; char *m_save_thd_query_txt; uint m_save_thd_query_len; + bool m_used_query_txt; }; /** diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c655ffc72d19e..c497a41673e96 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -919,7 +919,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_PARTITION_LOCK_auto_inc; PSI_mutex_key key_RELAYLOG_LOCK_index; PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, - key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry; + key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry, + key_LOCK_SEQUENCE; PSI_mutex_key key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, @@ -1004,6 +1005,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_slave_state, "LOCK_slave_state", 0}, { &key_LOCK_start_thread, "LOCK_start_thread", PSI_FLAG_GLOBAL}, { &key_LOCK_binlog_state, "LOCK_binlog_state", 0}, + { &key_LOCK_SEQUENCE, "SQUENCE::LOCK_SEQUENCE", 0}, { &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0}, { &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0}, { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0} @@ -3784,6 +3786,7 @@ SHOW_VAR com_status_vars[]= { {"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)}, {"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)}, {"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)}, + {"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)}, {"create_server", STMT_STATUS(SQLCOM_CREATE_SERVER)}, {"create_table", STMT_STATUS(SQLCOM_CREATE_TABLE)}, {"create_temporary_table", COM_STATUS(com_create_tmp_table)}, @@ -3802,6 +3805,7 @@ SHOW_VAR com_status_vars[]= { {"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)}, {"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)}, {"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)}, + {"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)}, {"drop_table", STMT_STATUS(SQLCOM_DROP_TABLE)}, {"drop_temporary_table", COM_STATUS(com_drop_tmp_table)}, {"drop_trigger", STMT_STATUS(SQLCOM_DROP_TRIGGER)}, diff --git a/sql/mysqld.h b/sql/mysqld.h index 8b88c2d4ee9db..9f9e9a3ae660c 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 385a69adee38a..562d0acdd19a3 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7466,3 +7466,21 @@ ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD eng "Row variable '%-.192s' does not have a field '%-.192s'" ER_END_IDENTIFIER_DOES_NOT_MATCH eng "END identifier '%-.192s' does not match '%-.192s'" +ER_SEQUENCE_RUN_OUT + eng "Sequence '%-.64s.%-.64s' has run out" +ER_SEQUENCE_INVALID_DATA + eng "Sequence '%-.64s.%-.64s' values are conflicting" +ER_SEQUENCE_INVALID_TABLE_STRUCTURE + eng "Sequence '%-.64s.%-.64s' table structure is invalid (%s)" +ER_SEQUENCE_ACCESS_ERROR + eng "Sequence '%-.64s.%-.64s' access error" +ER_SEQUENCE_BINLOG_FORMAT + eng "Sequences requires binlog_format mixed or row" +ER_NOT_SEQUENCE 42S02 + eng "'%-.64s.%-.64s' is not a SEQUENCE" +ER_NOT_SEQUENCE2 42S02 + eng "'%-.192s' is not a SEQUENCE" +ER_UNKNOWN_SEQUENCES 42S02 + eng "Unknown SEQUENCE: '%-.300s'" +ER_UNKNOWN_VIEW 42S02 + eng "Unknown VIEW: '%-.300s'" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a9c89aef0a874..aeaca82649f7c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -234,12 +234,14 @@ sp_get_flags_for_command(LEX *lex) flags= sp_head::CONTAINS_DYNAMIC_SQL; break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: if (lex->tmp_table()) flags= 0; else flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: if (lex->tmp_table()) flags= 0; else @@ -4407,7 +4409,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { SP_TABLE *tab; - if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE && + if ((lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE || + lex_for_tmp_check->sql_command == SQLCOM_DROP_SEQUENCE) && lex_for_tmp_check->tmp_table()) return TRUE; @@ -4471,7 +4474,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE)))) return FALSE; - if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE && + if ((lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE || + lex_for_tmp_check->sql_command == SQLCOM_CREATE_SEQUENCE) && lex_for_tmp_check->query_tables == table && lex_for_tmp_check->tmp_table()) { diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5e3311d502a2a..09781f4bbe39a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6438,7 +6438,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { if (!(rights & CREATE_ACL)) { - if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0)) + if (!ha_table_exists(thd, table_list->db, table_list->table_name)) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(TRUE); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 95c2163f04388..f9452077d36f5 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 5a51e2b34afa0..5f9c6ceabc740 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1934,6 +1934,11 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) DBUG_RETURN(true); } #endif + if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + DBUG_RETURN(true); + } table->init(thd, table_list); @@ -3650,8 +3655,9 @@ lock_table_names(THD *thd, const DDL_options_st &options, DBUG_RETURN(FALSE); /* Check if CREATE TABLE without REPLACE was used */ - create_table= thd->lex->sql_command == SQLCOM_CREATE_TABLE && - !options.or_replace(); + create_table= ((thd->lex->sql_command == SQLCOM_CREATE_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) && + !options.or_replace()); if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) { @@ -4518,7 +4524,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, /* Set requested lock type. */ table_l->lock_type= lock_type; /* Allow to open real tables only. */ - table_l->required_type= FRMTYPE_TABLE; + table_l->required_type= TABLE_TYPE_NORMAL; /* Open the table. */ if (open_and_lock_tables(thd, table_l, FALSE, flags, @@ -4574,7 +4580,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, THD_STAGE_INFO(thd, stage_opening_tables); thd->current_tablenr= 0; /* open_ltable can be used only for BASIC TABLEs */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; /* This function can't properly handle requests for such metadata locks. */ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE); @@ -5602,6 +5608,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, strcmp(db_name, table_list->db))))) DBUG_RETURN(0); + /* + Don't allow usage of fields in sequence table that is opened as part of + NEXT VALUE for sequence_name + */ + if (table_list->sequence) + DBUG_RETURN(0); + *actual_table= NULL; if (table_list->field_translation) diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index a40a064dc4bbd..6a491a3e7bb98 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 #include diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 93bc600deb9e2..abd680d749f9e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -101,6 +101,23 @@ extern "C" void free_user_var(user_var_entry *entry) my_free(entry); } +/* Functions for last-value-from-sequence hash */ + +extern "C" uchar *get_sequence_last_key(SEQUENCE_LAST_VALUE *entry, + size_t *length, + my_bool not_used + __attribute__((unused))) +{ + *length= entry->length; + return (uchar*) entry->key; +} + +extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry) +{ + delete entry; +} + + bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && @@ -879,6 +896,9 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_proc_cache= NULL; sp_func_cache= NULL; @@ -1428,6 +1448,9 @@ void THD::change_user(void) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, 0); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); } @@ -1484,6 +1507,7 @@ void THD::cleanup(void) #endif /* defined(ENABLED_DEBUG_SYNC) */ my_hash_free(&user_vars); + my_hash_free(&sequences); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); auto_inc_intervals_forced.empty(); @@ -5699,7 +5723,7 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg) int THD::decide_logging_format(TABLE_LIST *tables) { DBUG_ENTER("THD::decide_logging_format"); - DBUG_PRINT("info", ("Query: %s", query())); + DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query())); DBUG_PRINT("info", ("variables.binlog_format: %lu", variables.binlog_format)); DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x", @@ -5866,7 +5890,9 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (prev_write_table && prev_write_table->file->ht != table->table->file->ht) multi_write_engine= TRUE; - if (table->table->s->non_determinstic_insert) + if (table->table->s->non_determinstic_insert && + lex->sql_command != SQLCOM_CREATE_SEQUENCE && + lex->sql_command != SQLCOM_CREATE_TABLE) has_write_tables_with_unsafe_statements= true; trans= table->table->file->has_transactions(); diff --git a/sql/sql_class.h b/sql/sql_class.h index 974596910b233..52d2d12ae43f4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -699,6 +699,7 @@ typedef struct system_variables ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; + my_bool sequence_read_skip_cache; ulong threadpool_priority; } SV; @@ -2215,6 +2216,8 @@ class THD :public Statement, chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ HASH ull_hash; + /* Hash of used seqeunces (for PREVIOUS value) */ + HASH sequences; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif @@ -2419,6 +2422,8 @@ class THD :public Statement, uint binlog_table_maps; public: void issue_unsafe_warnings(); + void reset_unsafe_warnings() + { binlog_unsafe_warning_flags= 0; } uint get_binlog_table_maps() const { return binlog_table_maps; @@ -5689,6 +5694,14 @@ class select_dumpvar :public select_result_interceptor { 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 e33f8e443dc34..875dcf0e57594 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -96,6 +96,8 @@ enum enum_sql_command { SQLCOM_ALTER_USER, SQLCOM_SHOW_CREATE_USER, SQLCOM_EXECUTE_IMMEDIATE, + SQLCOM_CREATE_SEQUENCE, + SQLCOM_DROP_SEQUENCE, /* When a command is added here, be sure it's also added in mysqld.cc diff --git a/sql/sql_const.h b/sql/sql_const.h index c8e60305eab3c..2fa4b01b0a537 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -114,6 +114,7 @@ #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 +#define SEQUENCES_HASH_SIZE 16 #define TABLE_OPEN_CACHE_MIN 400 #define TABLE_OPEN_CACHE_DEFAULT 2000 #define TABLE_DEF_CACHE_DEFAULT 400 diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 6b29d3454325c..6054c45b1c3b2 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -902,7 +902,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, true, false))) + mysql_rm_table_no_locks(thd, tables, true, false, true, false, true, + false))) { /* We temporarily disable the binary log while dropping the objects diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4a27244f5b9eb..d4673605e13ee 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -286,7 +286,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* for now HANDLER can be used only for real TABLES */ - tables->required_type= FRMTYPE_TABLE; + tables->required_type= TABLE_TYPE_NORMAL; /* We use open_tables() here, rather than, say, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8e5dfb4f69cf9..f97732860ad7e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4120,7 +4120,7 @@ static TABLE *create_table_from_items(THD *thd, if (!mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, alter_info, NULL, - select_field_count)) + select_field_count, create_table)) { DEBUG_SYNC(thd,"create_table_select_before_open"); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 38a6e84f509a2..e474a2cb313b3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -750,7 +750,7 @@ void LEX::start(THD *thd_arg) in_sum_func= NULL; used_tables= 0; - only_view= FALSE; + table_type= TABLE_TYPE_UNKNOWN; reset_slave_info.all= false; limit_rows_examined= 0; limit_rows_examined_cnt= ULONGLONG_MAX; @@ -3379,6 +3379,7 @@ void LEX::set_trg_event_type_for_tables() REPLACE SELECT is handled later in this method. */ case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: new_trg_event_map|= static_cast (1 << static_cast(TRG_EVENT_INSERT)); break; @@ -6394,6 +6395,39 @@ Item *LEX::create_item_ident(THD *thd, sp_variable *spv; if (spcont && (spv= spcont->find_variable(a, false))) return create_item_spvar_row_field(thd, a, b, spv, pos_in_q, length_in_q); + + if ((thd->variables.sql_mode & MODE_ORACLE) && b.length == 7) + { + if (!my_strnncoll(system_charset_info, + (const uchar *) b.str, 7, + (const uchar *) "NEXTVAL", 7)) + { + TABLE_LIST *table; + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(a)) || + !(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + return NULL; + return new (thd->mem_root) Item_func_nextval(thd, table); + } + else if (!my_strnncoll(system_charset_info, + (const uchar *) b.str, 7, + (const uchar *) "CURRVAL", 7)) + { + TABLE_LIST *table; + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(a)) || + !(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + return NULL; + return new (thd->mem_root) Item_func_lastval(thd, table); + } + } + return create_item_ident_nospvar(thd, a, b); } @@ -6921,3 +6955,21 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } return 0; } + + +bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) +{ + uint offset; + sp_instr_cfetch *i; + + if (!spcont->find_cursor(name, &offset, false)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name.str); + return true; + } + i= new (thd->mem_root) + sp_instr_cfetch(sphead->instructions(), spcont, offset); + if (i == NULL || sphead->add_instr(i)) + return true; + return false; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6211330c2c28a..e131241ea5290 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -249,6 +249,7 @@ enum enum_drop_mode #define TL_OPTION_FORCE_INDEX 2 #define TL_OPTION_IGNORE_LEAVES 4 #define TL_OPTION_ALIAS 8 +#define TL_OPTION_SEQUENCE 16 typedef List List_item; typedef Mem_root_array Group_list_ptrs; @@ -2632,6 +2633,7 @@ struct LEX: public Query_tables_list */ LEX_USER *definer; + Table_type table_type; /* Used for SHOW CREATE */ List ref_list; List users_list; List columns; @@ -2804,7 +2806,6 @@ struct LEX: public Query_tables_list Event_parse_data *event_parse_data; - bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* field_list was created for view and should be removed before PS/SP rexecuton @@ -3533,6 +3534,8 @@ struct LEX: public Query_tables_list 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 efcde7950eb9c..161e1086bba4a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -101,6 +101,7 @@ #include "set_var.h" #include "log_slow.h" #include "sql_bootstrap.h" +#include "sql_sequence.h" #include "my_json_writer.h" @@ -448,6 +449,7 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: skip= (lex->tmp_table() || (thd->variables.option_bits & OPTION_GTID_BEGIN)); break; @@ -456,6 +458,7 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) skip= (lex->tmp_table()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If CREATE TABLE of non-temporary table and the table is not part if a BEGIN GTID ... COMMIT group, do a implicit commit. @@ -542,19 +545,25 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | + CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]= (CF_CHANGES_DATA | + CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS | + CF_SCHEMA_CHANGE); sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; - sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -721,6 +730,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* We don't want to replicate DROP for temp tables in row format */ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* One can change replication mode with SET */ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; @@ -763,6 +773,7 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES; @@ -795,7 +806,9 @@ void init_update_queries(void) have to be closed before temporary tables are pre-opened. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE; @@ -813,8 +826,10 @@ void init_update_queries(void) even temporary table DDL should be disallowed. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS; @@ -1407,7 +1422,7 @@ bool do_command(THD *thd) This is a helper function to mysql_execute_command. - @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere. + @note SQLCOM_MULTI_UPDATE is an exception and dealt with elsewhere. @see mysql_execute_command @returns Status code @@ -1425,13 +1440,12 @@ static my_bool deny_updates_if_read_only_option(THD *thd, LEX *lex= thd->lex; - const my_bool user_is_super= - ((ulong)(thd->security_ctx->master_access & SUPER_ACL) == - (ulong)SUPER_ACL); - - if (user_is_super) + /* Super user is allowed to do changes */ + if (((ulong)(thd->security_ctx->master_access & SUPER_ACL) == + (ulong)SUPER_ACL)) DBUG_RETURN(FALSE); + /* Check if command doesn't update anything */ if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA)) DBUG_RETURN(FALSE); @@ -1439,29 +1453,17 @@ static my_bool deny_updates_if_read_only_option(THD *thd, if (lex->sql_command == SQLCOM_UPDATE_MULTI) DBUG_RETURN(FALSE); - const my_bool create_temp_tables= - (lex->sql_command == SQLCOM_CREATE_TABLE) && lex->tmp_table(); - - const my_bool drop_temp_tables= - (lex->sql_command == SQLCOM_DROP_TABLE) && lex->tmp_table(); - - const my_bool update_real_tables= - some_non_temp_table_to_be_updated(thd, all_tables) && - !(create_temp_tables || drop_temp_tables); - - - const my_bool create_or_drop_databases= - (lex->sql_command == SQLCOM_CREATE_DB) || - (lex->sql_command == SQLCOM_DROP_DB); + /* Check if we created and dropped temporary tables */ + if ((sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE) && + lex->tmp_table()) + DBUG_RETURN(FALSE); - if (update_real_tables || create_or_drop_databases) - { - /* - An attempt was made to modify one or more non-temporary tables. - */ - DBUG_RETURN(TRUE); - } + /* Check if we created or dropped databases */ + if ((sql_command_flags[lex->sql_command] & CF_DB_CHANGE)) + DBUG_RETURN(TRUE); + if (some_non_temp_table_to_be_updated(thd, all_tables)) + DBUG_RETURN(TRUE); /* Assuming that only temporary tables are modified. */ DBUG_RETURN(FALSE); @@ -3192,7 +3194,8 @@ mysql_execute_command(THD *thd) */ if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) && !(lex->sql_command == SQLCOM_SET_OPTION) && - !(lex->sql_command == SQLCOM_DROP_TABLE && + !((lex->sql_command == SQLCOM_DROP_TABLE || + lex->sql_command == SQLCOM_DROP_SEQUENCE) && lex->tmp_table() && lex->if_exists()) && all_tables_not_ok(thd, all_tables)) { @@ -3817,6 +3820,7 @@ mysql_execute_command(THD *thd) res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX); break; } + case SQLCOM_CREATE_SEQUENCE: case SQLCOM_CREATE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4074,6 +4078,7 @@ mysql_execute_command(THD *thd) /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); } + if (!res) { /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4282,9 +4287,9 @@ mysql_execute_command(THD *thd) */ DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s", - lex->only_view, + lex->table_type == TABLE_TYPE_VIEW, first_table->db, first_table->table_name)); - if (lex->only_view) + if (lex->table_type == TABLE_TYPE_VIEW) { if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) { @@ -4298,7 +4303,6 @@ mysql_execute_command(THD *thd) /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->open_type= OT_BASE_ONLY; - } else { @@ -4806,6 +4810,7 @@ mysql_execute_command(THD *thd) } break; } + case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4816,7 +4821,7 @@ mysql_execute_command(THD *thd) } else { - status_var_decrement(thd->status_var.com_stat[SQLCOM_DROP_TABLE]); + status_var_decrement(thd->status_var.com_stat[lex->sql_command]); status_var_increment(thd->status_var.com_drop_tmp_table); /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4846,10 +4851,13 @@ mysql_execute_command(THD *thd) lex->create_info.set(DDL_options_st::OPT_IF_EXISTS); /* DDL and binlog write order are protected by metadata locks. */ - res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); + res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(), + lex->table_type == TABLE_TYPE_SEQUENCE); - /* when dropping temporary tables if @@session_track_state_change is ON then - send the boolean tracker in the OK packet */ + /* + When dropping temporary tables if @@session_track_state_change is ON + then send the boolean tracker in the OK packet + */ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); @@ -8095,6 +8103,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES); + ptr->sequence= MY_TEST(table_options & TL_OPTION_SEQUENCE); ptr->derived= table->sel; if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) { @@ -8135,8 +8144,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->cacheable_table= !table->is_derived_table(); ptr->index_hints= index_hints_arg; ptr->option= option ? option->str : 0; - /* check that used name is unique */ - if (lock_type != TL_IGNORE) + /* check that used name is unique. Sequences are ignored */ + if (lock_type != TL_IGNORE && !ptr->sequence) { TABLE_LIST *first_table= table_list.first; if (lex->sql_command == SQLCOM_CREATE_VIEW) @@ -8146,7 +8155,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, tables=tables->next_local) { if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && - !strcmp(ptr->db, tables->db)) + !strcmp(ptr->db, tables->db) && ! tables->sequence) { my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ @@ -8154,7 +8163,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } /* Store the table reference preceding the current one. */ - if (table_list.elements > 0) + if (table_list.elements > 0 && !ptr->sequence) { /* table_list.next points to the last inserted TABLE_LIST->next_local' @@ -8179,8 +8188,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, Notice that as a side effect here we set the next_local field of the previous table reference to 'ptr'. Here we also add one element to the list 'table_list'. + We don't store sequences into the local list to hide them from INSERT + and SELECT. */ - table_list.link_in_list(ptr, &ptr->next_local); + if (!ptr->sequence) + table_list.link_in_list(ptr, &ptr->next_local); ptr->next_name_resolution_table= NULL; #ifdef WITH_PARTITION_STORAGE_ENGINE ptr->partition_names= partition_names; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 101ea3fd3c7a1..fe779cf01b49d 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1651,9 +1651,10 @@ int plugin_init(int *argc, char **argv, int flags) build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0); char engine_name_buf[NAME_CHAR_LEN + 1]; LEX_STRING maybe_myisam= { engine_name_buf, 0 }; - frm_type_enum frm_type= dd_frm_type(NULL, path, &maybe_myisam); + bool is_sequence; + Table_type frm_type= dd_frm_type(NULL, path, &maybe_myisam, &is_sequence); /* if mysql.plugin table is MyISAM - load it right away */ - if (frm_type == FRMTYPE_TABLE && !strcasecmp(maybe_myisam.str, "MyISAM")) + if (frm_type == TABLE_TYPE_NORMAL && !strcasecmp(maybe_myisam.str, "MyISAM")) { plugin_load(&tmp_root); flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4b2d8b5fb3679..3aa6f531e0168 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 5ca19e34bac6b..e6cfd923d05f5 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 0000000000000..d021ee63ebfdd --- /dev/null +++ b/sql/sql_sequence.cc @@ -0,0 +1,670 @@ +/* + Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_class.h" +#include "sql_list.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_base.h" +#include "transaction.h" +#include "lock.h" + +struct Field_definition +{ + const char *field_name; + uint length; + enum enum_field_types sql_type; + LEX_STRING comment; + ulong flags; +}; + +/* + Structure for all SEQUENCE tables + + Note that the first field is named "next_val" to all us to have + NEXTVAL a reserved word that will on access be changed to + NEXTVAL(sequence_table). For this to work, the table can't have + a column named NEXTVAL. +*/ + +#define FL (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG) + +static Field_definition sequence_structure[]= +{ + {"next_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("next not cached value")}, + FL}, + {"min_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("min value")}, FL}, + {"max_value", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("max value")}, FL}, + {"start", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("start value")}, FL}, + {"increment", 21, MYSQL_TYPE_LONGLONG, + {C_STRING_WITH_LEN("increment value")}, FL}, + {"cache", 21, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("cache size")}, FL}, + {"cycle", 1, MYSQL_TYPE_TINY, {C_STRING_WITH_LEN("cycle state")}, + FL | UNSIGNED_FLAG }, + {"round", 21, MYSQL_TYPE_LONGLONG, + {C_STRING_WITH_LEN("How many cycles has been done")}, FL}, + {NULL, 0, MYSQL_TYPE_LONGLONG, {C_STRING_WITH_LEN("")}, 0} +}; + +#undef FL + + +#define MAX_AUTO_INCREMENT_VALUE 65535 + +/* + Check whether sequence values are valid. + Sets default values for fields that are not used, according to Oracle spec. + + Note that reserved_until is not checked as it's ok that it's outside of + the range (to indicate that sequence us used up). + + RETURN VALUES + false valid + true invalid +*/ + +bool sequence_definition::check_and_adjust() +{ + longlong max_increment; + DBUG_ENTER("sequence_definition::check"); + + /* + If min_value is not set, set it to LONGLONG_MIN or 1, depending on + increment + */ + if (!(used_fields & seq_field_used_min_value)) + min_value= increment < 0 ? LONGLONG_MIN+1 : 1; + + /* + If min_value is not set, set it to LONGLONG_MAX or -1, depending on + increment + */ + if (!(used_fields & seq_field_used_max_value)) + max_value= increment < 0 ? -1 : LONGLONG_MAX-1; + + if (!(used_fields & seq_field_used_start)) + { + /* Use min_value or max_value for start depending on increment */ + start= increment < 0 ? max_value : min_value; + } + + /* To ensure that cache * increment will never overflow */ + max_increment= increment ? labs(increment) : MAX_AUTO_INCREMENT_VALUE; + + if (max_value >= start && + max_value > min_value && + start >= min_value && + max_value != LONGLONG_MAX && + min_value != LONGLONG_MIN && + cache < (LONGLONG_MAX - max_increment) / max_increment) + DBUG_RETURN(FALSE); + DBUG_RETURN(TRUE); +} + + +/* + Read sequence values from a table +*/ + +void sequence_definition::read_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + reserved_until= table->field[0]->val_int(); + min_value= table->field[1]->val_int(); + max_value= table->field[2]->val_int(); + start= table->field[3]->val_int(); + increment= table->field[4]->val_int(); + cache= table->field[5]->val_int(); + cycle= table->field[6]->val_int(); + round= table->field[7]->val_int(); + dbug_tmp_restore_column_map(table->read_set, old_map); + used_fields= ~(uint) 0; + print_dbug(); +} + + +/* + Store sequence into a table row +*/ + +void sequence_definition::store_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + + /* zero possible delete markers & null bits */ + memcpy(table->record[0], table->s->default_values, table->s->null_bytes); + table->field[0]->store(reserved_until, 0); + table->field[1]->store(min_value, 0); + table->field[2]->store(max_value, 0); + table->field[3]->store(start, 0); + table->field[4]->store(increment, 0); + table->field[5]->store(cache, 0); + table->field[6]->store((longlong) cycle != 0, 0); + table->field[7]->store((longlong) round, 1); + + dbug_tmp_restore_column_map(table->write_set, old_map); + print_dbug(); +} + + +/* + Check the sequence fields through seq_fields when create sequence.qq + + RETURN VALUES + false Success + true Failure +*/ + +bool check_sequence_fields(LEX *lex, List *fields) +{ + Create_field *field; + List_iterator_fast it(*fields); + uint field_count; + uint field_no; + const char *reason; + DBUG_ENTER("check_sequence_fields"); + + field_count= fields->elements; + if (field_count != array_elements(sequence_structure)-1) + { + reason= "Wrong number of columns"; + goto err; + } + if (lex->alter_info.key_list.elements > 0) + { + reason= "Sequence tables cannot have any keys"; + goto err; + } + + for (field_no= 0; (field= it++); field_no++) + { + Field_definition *field_def= &sequence_structure[field_no]; + if (my_strcasecmp(system_charset_info, field_def->field_name, + field->field_name) || + field->flags != field_def->flags || + field->sql_type != field_def->sql_type) + { + reason= field->field_name; + goto err; + } + } + DBUG_RETURN(FALSE); + +err: + my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name, reason); + DBUG_RETURN(TRUE); +} + + +/* + Create the fields for a SEQUENCE TABLE + + RETURN VALUES + false Success + true Failure (out of memory) +*/ + +bool prepare_sequence_fields(THD *thd, List *fields) +{ + Field_definition *field_info; + DBUG_ENTER("prepare_sequence_fields"); + + for (field_info= sequence_structure; field_info->field_name ; field_info++) + { + Create_field *new_field; + if (unlikely(!(new_field= new Create_field()))) + DBUG_RETURN(TRUE); /* purify inspected */ + + new_field->field_name= field_info->field_name; + new_field->sql_type= field_info->sql_type; + new_field->length= field_info->length; + new_field->char_length= field_info->length; + new_field->comment= field_info->comment; + new_field->flags= field_info->flags; + if (unlikely(fields->push_back(new_field))) + DBUG_RETURN(TRUE); /* purify inspected */ + } + DBUG_RETURN(FALSE); +} + +/* + Initialize the sequence table record as part of CREATE SEQUENCE + + Store one row with sequence information. + + RETURN VALUES + false Success + true Failure. Error reported. + + NOTES + This function is called as part of CREATE SEQUENCE. When called + there are now active transactions and no open tables. + There is also a MDL lock on the table. +*/ + +bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list) +{ + int error; + TABLE *table; + TABLE_LIST::enum_open_strategy save_open_strategy; + sequence_definition *seq= lex->create_info.seq_create_info; + bool temporary_table= table_list->table != 0; + MY_BITMAP *save_write_set; + DBUG_ENTER("sequence_insert"); + + /* If not temporary table */ + if (!temporary_table) + { + /* Table was locked as part of create table. Free it but keep MDL locks */ + close_thread_tables(thd); + table_list->lock_type= TL_WRITE_DEFAULT; + table_list->updating= 1; + /* + The FOR CREATE flag is needed to ensure that ha_open() doesn't try to + read the not yet existing row in the sequence table + */ + thd->open_options|= HA_OPEN_FOR_CREATE; + save_open_strategy= table_list->open_strategy; + table_list->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + table_list->open_type= OT_BASE_ONLY; + error= open_and_lock_tables(thd, table_list, FALSE, + MYSQL_LOCK_IGNORE_TIMEOUT | + MYSQL_OPEN_HAS_MDL_LOCK); + table_list->open_strategy= save_open_strategy; + thd->open_options&= ~HA_OPEN_FOR_CREATE; + if (error) + DBUG_RETURN(TRUE); /* purify inspected */ + } + + table= table_list->table; + + /* + seq is 0 if sequence was created with CREATE TABLE instead of + CREATE SEQUENCE + */ + if (!seq) + { + if (!(seq= new (thd->mem_root) sequence_definition)) + DBUG_RETURN(TRUE); // EOM + } + + seq->reserved_until= seq->start; + seq->store_fields(table); + /* Store the sequence values in table share */ + table->s->sequence->copy(seq); + + /* + Sequence values will be replicated as a statement + like 'create sequence'. So disable binary log temporarily + */ + tmp_disable_binlog(thd); + save_write_set= table->write_set; + table->write_set= &table->s->all_set; + error= table->file->ha_write_row(table->record[0]); + reenable_binlog(thd); + table->write_set= save_write_set; + + if (error) + table->file->print_error(error, MYF(0)); + else + { + /* + Sequence structure is up to date and table has one row, + sequence is now usable + */ + table->s->sequence->initialized= 1; + } + + trans_commit_stmt(thd); + trans_commit_implicit(thd); + if (!temporary_table) + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + DBUG_RETURN(error); +} + + +/* Create a SQUENCE object */ + +SEQUENCE::SEQUENCE() :initialized(0), all_values_used(0), table(0) +{ + mysql_mutex_init(key_LOCK_SEQUENCE, &mutex, MY_MUTEX_INIT_SLOW); +} + +SEQUENCE::~SEQUENCE() +{ + mysql_mutex_destroy(&mutex); +} + + +/** + Read values from the sequence tables to table_share->sequence. + This is called from ha_open() when the table is not yet locked +*/ + +int SEQUENCE::read_initial_values(TABLE *table_arg) +{ + int error= 0; + enum thr_lock_type save_lock_type; + MDL_request mdl_request; // Empty constructor! + DBUG_ENTER("SEQUENCE::read_initial_values"); + + if (likely(initialized)) + DBUG_RETURN(0); + table= table_arg; + mysql_mutex_lock(&mutex); + if (unlikely(!initialized)) + { + MYSQL_LOCK *lock; + bool mdl_lock_used= 0; + THD *thd= table->in_use; + bool has_active_transaction= !thd->transaction.stmt.is_empty(); + /* + There is already a mdl_ticket for this table. However, for list_fields + the MDL lock is of type MDL_SHARED_HIGH_PRIO which is not usable + for doing a able lock. Get a proper read lock to solve this. + */ + if (table->mdl_ticket == 0) + { + MDL_request_list mdl_requests; + mdl_lock_used= 1; + /* + This happens if first request is SHOW CREATE TABLE or LIST FIELDS + where we don't have a mdl lock on the table + */ + + mdl_request.init(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_SHARED_READ, MDL_EXPLICIT); + mdl_requests.push_front(&mdl_request); + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + save_lock_type= table->reginfo.lock_type; + table->reginfo.lock_type= TL_READ; + if (!(lock= mysql_lock_tables(thd, &table, 1, + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY))) + { + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + if (!(error= read_stored_values())) + initialized= 1; + mysql_unlock_tables(thd, lock, 0); + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + + /* Reset value to default */ + table->reginfo.lock_type= save_lock_type; + /* + Doing mysql_lock_tables() may have started a read only transaction. + If that happend, it's better that we commit it now, as a lot of + code assumes that there is no active stmt transaction directly after + open_tables() + */ + if (!has_active_transaction && !thd->transaction.stmt.is_empty()) + trans_commit_stmt(thd); + } + mysql_mutex_unlock(&mutex); + DBUG_RETURN(error); +} + +/* + Read data from sequence table and update values + Done when table is opened +*/ + +int SEQUENCE::read_stored_values() +{ + int error; + my_bitmap_map *save_read_set; + DBUG_ENTER("SEQUENCE::read_stored_values"); + mysql_mutex_assert_owner(&mutex); + + save_read_set= tmp_use_all_columns(table, table->read_set); + error= table->file->ha_read_first_row(table->record[0], MAX_KEY); + tmp_restore_column_map(table->read_set, save_read_set); + + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + read_fields(table); + adjust_values(); + + all_values_used= 0; + DBUG_RETURN(0); +} + + +/* + Adjust values after reading a the stored state +*/ + +void SEQUENCE::adjust_values() +{ + offset= 0; + next_free_value= reserved_until; + if (!(real_increment= increment)) + { + longlong off, to_add; + /* Use auto_increment_increment and auto_increment_offset */ + + if ((real_increment= global_system_variables.auto_increment_increment) + != 1) + offset= global_system_variables.auto_increment_offset; + + /* + Ensure that next_free_value has the right offset, so that we + can generate a serie by just adding real_increment. + */ + off= next_free_value % real_increment; + if (off < 0) + off+= real_increment; + to_add= (real_increment + offset - off) % real_increment; + + /* + Check if add will make next_free_value bigger than max_value, + taken into account that next_free_value or max_value addition + may overflow + */ + if (next_free_value > max_value - to_add || + next_free_value + to_add > max_value) + next_free_value= max_value+1; + else + { + next_free_value+= to_add; + DBUG_ASSERT(next_free_value % real_increment == offset && + next_free_value >= reserved_until); + } + } +} + + +/** + Get next value for sequence + + @param in table Sequence table + @param in second_round + 1 if recursive call (out of values once) + @param out error Set this to <> 0 in case of error + push_warning_printf(WARN_LEVEL_WARN) has been called + + +  @retval 0 Next number or error. Check error variable + # Next sequence number + + NOTES: + Return next_free_value and increment next_free_value to next allowed + value or reserved_value if out of range + if next_free_value >= reserved_value reserve a new range by writing + a record to the sequence table. + + The state of the variables: + next_free_value contains next value to use. It may be + bigger than max_value or less than min_value if end of sequence. + reserved_until contains the last value written to the file. All + values up to this one can be used. + If next_free_value >= reserved_until we have to reserve new + values from the sequence. +*/ + +longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error) +{ + longlong res_value, org_reserved_until, add_to; + bool out_of_values; + MY_BITMAP *save_rpl_write_set, *save_write_set; + DBUG_ENTER("SEQUENCE::next_value"); + + *error= 0; + if (!second_round) + lock(); + + res_value= next_free_value; + + /* Increment next_free_value */ + if (real_increment > 0) + { + if (next_free_value + real_increment > max_value || + next_free_value > max_value - real_increment) + next_free_value= max_value + 1; + else + next_free_value+= real_increment; + } + else + { + if (next_free_value + real_increment < min_value || + next_free_value < min_value - real_increment) + next_free_value= min_value - 1; + else + next_free_value+= real_increment; + } + + if ((real_increment > 0 && res_value < reserved_until) || + (real_increment < 0 && res_value > reserved_until)) + { + unlock(); + DBUG_RETURN(res_value); + } + + if (all_values_used) + goto err; + + org_reserved_until= reserved_until; + + /* + Out of cached values, reserve 'cache' new ones + The cache value is checked on insert so the following can't + overflow + */ + add_to= cache ? real_increment * cache : 1; + out_of_values= 0; + + if (real_increment > 0) + { + if (reserved_until + add_to > max_value || + reserved_until > max_value - add_to) + { + reserved_until= max_value + 1; + out_of_values= res_value >= reserved_until; + } + else + reserved_until+= add_to; + } + else + { + if (reserved_until + add_to < min_value || + reserved_until < min_value - add_to) + { + reserved_until= min_value - 1; + out_of_values= res_value <= reserved_until; + } + else + reserved_until+= add_to; + } + if (out_of_values) + { + if (!cycle || second_round) + goto err; + round++; + reserved_until= real_increment >0 ? min_value : max_value; + adjust_values(); // Fix next_free_value + /* + We have to do everything again to ensure that the given range was + not empty, which could happen if increment == 0 + */ + DBUG_RETURN(next_value(table, 1, error)); + } + + /* Log a full insert (ok as table is small) */ + save_rpl_write_set= table->rpl_write_set; + + /* Update table */ + save_write_set= table->write_set; + table->rpl_write_set= table->write_set= &table->s->all_set; + store_fields(table); + /* Tell ha_sequence::write_row that we already hold the mutex */ + ((ha_sequence*) table->file)->sequence_locked= 1; + if ((*error= table->file->ha_write_row(table->record[0]))) + { + table->file->print_error(*error, MYF(0)); + /* Restore original range */ + reserved_until= org_reserved_until; + next_free_value= res_value; + } + ((ha_sequence*) table->file)->sequence_locked= 0; + table->rpl_write_set= save_rpl_write_set; + table->write_set= save_write_set; + + unlock(); + DBUG_RETURN(res_value); + +err: + unlock(); + my_error(ER_SEQUENCE_RUN_OUT, MYF(0), table->s->db.str, + table->s->table_name.str); + *error= ER_SEQUENCE_RUN_OUT; + all_values_used= 1; + DBUG_RETURN(0); +} + + +/* + The following functions is to detect if a table has been dropped + and re-created since last call to PREVIOUS VALUE. + + This is needed as we don't delete dropped sequences from THD->sequence + for DROP TABLE. +*/ + +bool SEQUENCE_LAST_VALUE::check_version(TABLE *table) +{ + DBUG_ASSERT(table->s->tabledef_version.length == MY_UUID_SIZE); + return memcmp(table->s->tabledef_version.str, table_version, + MY_UUID_SIZE) != 0; +} + +void SEQUENCE_LAST_VALUE::set_version(TABLE *table) +{ + memcpy(table_version, table->s->tabledef_version.str, MY_UUID_SIZE); +} diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h new file mode 100644 index 0000000000000..f5534d55d9bd5 --- /dev/null +++ b/sql/sql_sequence.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2017, MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SQL_SEQUENCE_INCLUDED +#define SQL_SEQUENCE_INCLUDED + +#define seq_field_used_min_value 1 +#define seq_field_used_max_value 2 +#define seq_field_used_start 4 + +/** + sequence_definition is used when defining a sequence as part of create +*/ + +class sequence_definition :public Sql_alloc +{ +public: + sequence_definition(): + min_value(1), max_value(LONGLONG_MAX-1), start(1), increment(1), + cache(1000), round(0), cycle(0), used_fields(0) + {} + longlong reserved_until; + longlong min_value; + longlong max_value; + longlong start; + longlong increment; + longlong cache; + ulonglong round; + bool cycle; + uint used_fields; // Which fields where used in CREATE + + bool check_and_adjust(); + void store_fields(TABLE *table); + void read_fields(TABLE *table); + void print_dbug() + { + DBUG_PRINT("sequence", ("reserved: %lld start: %lld increment: %lld min_value: %lld max_value: %lld cache: %lld round: %lld", + reserved_until, start, increment, min_value, + max_value, cache, round)); + } +}; + +/** + SEQUENCE is in charge of managing the sequence values. + It's also responsible to generate new values and updating the sequence + table (engine=SQL_SEQUENCE) trough it's specialized handler interface. + + If increment is 0 then the sequence will be be using + auto_increment_increment and auto_increment_offset variables, just like + AUTO_INCREMENT is using. +*/ + +class SEQUENCE :public sequence_definition +{ +public: + SEQUENCE(); + ~SEQUENCE(); + int read_initial_values(TABLE *table); + int read_stored_values(); + void lock() + { + mysql_mutex_lock(&mutex); + } + void unlock() + { + mysql_mutex_unlock(&mutex); + } + /* This must be called after sequence data has been updated */ + void adjust_values(); + void copy(sequence_definition *seq) + { + sequence_definition::operator= (*seq); + adjust_values(); + } + longlong next_value(TABLE *table, bool second_round, int *error); + + bool initialized; // If row has been read + bool all_values_used; +private: + TABLE *table; + mysql_mutex_t mutex; + longlong next_free_value; + /* + The following values are the values from sequence_definition + merged with global auto_increment_offset and auto_increment_increment + */ + longlong real_increment; + longlong offset; +}; + + +/** + Class to cache last value of NEXT VALUE from the sequence +*/ + +class SEQUENCE_LAST_VALUE +{ +public: + SEQUENCE_LAST_VALUE(uchar *key_arg, uint length_arg) + :key(key_arg), length(length_arg) + {} + ~SEQUENCE_LAST_VALUE() + { my_free((void*) key); } + /* Returns 1 if table hasn't been dropped or re-created */ + bool check_version(TABLE *table); + void set_version(TABLE *table); + + const uchar *key; + uint length; + bool null_value; + longlong value; + uchar table_version[MY_UUID_SIZE]; +}; + + +class Create_field; +extern bool prepare_sequence_fields(THD *thd, List *fields); +extern bool check_sequence_fields(LEX *lex, List *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 4fb1b2e8c84db..01f474a73fb36 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -58,10 +58,11 @@ #include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH #include "debug_sync.h" #include "keycaches.h" - +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif + enum enum_i_s_events_fields { ISE_EVENT_CATALOG= 0, @@ -130,6 +131,8 @@ static void get_cs_converted_string_value(THD *thd, #endif static int show_create_view(THD *thd, TABLE_LIST *table, String *buff); +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet); static const LEX_STRING *view_algorithm(TABLE_LIST *table); @@ -1169,12 +1172,19 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, } /* TODO: add environment variables show when it become possible */ - if (thd->lex->only_view && !table_list->view) + if (thd->lex->table_type == TABLE_TYPE_VIEW && !table_list->view) { my_error(ER_WRONG_OBJECT, MYF(0), table_list->db, table_list->table_name, "VIEW"); goto exit; } + else if (thd->lex->table_type == TABLE_TYPE_SEQUENCE && + table_list->table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_WRONG_OBJECT, MYF(0), + table_list->db, table_list->table_name, "SEQUENCE"); + goto exit; + } buffer->length(0); @@ -1183,6 +1193,8 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, if ((table_list->view ? show_create_view(thd, table_list, buffer) : + thd->lex->table_type == TABLE_TYPE_SEQUENCE ? + show_create_sequence(thd, table_list, buffer) : show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) goto exit; @@ -1761,6 +1773,179 @@ static void append_create_options(THD *thd, String *packet, packet->append(STRING_WITH_LEN(" */")); } +/** + Add table options to end of CREATE statement + + @param schema_table 1 if schema table + @param sequence 1 if sequence. If sequence, we flush out options + not relevant for sequences. +*/ + +static void add_table_options(THD *thd, TABLE *table, + Table_specification_st *create_info_arg, + bool schema_table, bool sequence, + String *packet) +{ + sql_mode_t sql_mode= thd->variables.sql_mode; + TABLE_SHARE *share= table->s; + handlerton *hton; + HA_CREATE_INFO create_info; + bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && + !create_info_arg); + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + hton= table->part_info->default_engine_type; + else +#endif + hton= table->file->ht; + + bzero((char*) &create_info, sizeof(create_info)); + /* Allow update_create_info to update row type, page checksums and options */ + create_info.row_type= share->row_type; + create_info.page_checksum= share->page_checksum; + create_info.options= share->db_create_options; + table->file->update_create_info(&create_info); + + /* + IF check_create_info + THEN add ENGINE only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) + { + LEX_STRING *engine_name= table->file->engine_name(); + + if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + packet->append(STRING_WITH_LEN(" TYPE=")); + else + packet->append(STRING_WITH_LEN(" ENGINE=")); + + packet->append(engine_name->str, engine_name->length); + } + + if (sequence) + goto end_options; + + /* + Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, + and NEXT_ID > 1 (the default). We must not print the clause + for engines that do not support this as it would break the + import of dumps, but as of this writing, the test for whether + AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... + is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) + Because of that, we do not explicitly test for the feature, + but may extrapolate its existence from that of an AUTO_INCREMENT column. + */ + + if (create_info.auto_increment_value > 1) + { + packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); + packet->append_ulonglong(create_info.auto_increment_value); + } + + if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + share->table_type != TABLE_TYPE_SEQUENCE) + { + /* + IF check_create_info + THEN add DEFAULT CHARSET only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) + { + packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); + packet->append(share->table_charset->csname); + if (!(share->table_charset->state & MY_CS_PRIMARY)) + { + packet->append(STRING_WITH_LEN(" COLLATE=")); + packet->append(table->s->table_charset->name); + } + } + } + + if (share->min_rows) + { + packet->append(STRING_WITH_LEN(" MIN_ROWS=")); + packet->append_ulonglong(share->min_rows); + } + + if (share->max_rows && !schema_table && !sequence) + { + packet->append(STRING_WITH_LEN(" MAX_ROWS=")); + packet->append_ulonglong(share->max_rows); + } + + if (share->avg_row_length) + { + packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); + packet->append_ulonglong(share->avg_row_length); + } + + if (create_info.options & HA_OPTION_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); + if (create_info.options & HA_OPTION_NO_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); + if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); + if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); + if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); + else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); + if (share->stats_sample_pages != 0) + { + packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); + packet->append_ulonglong(share->stats_sample_pages); + } + + /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ + if (create_info.options & HA_OPTION_CHECKSUM) + packet->append(STRING_WITH_LEN(" CHECKSUM=1")); + if (create_info.page_checksum != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); + packet->append(ha_choice_values[create_info.page_checksum], 1); + } + if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) + packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); + if (create_info.row_type != ROW_TYPE_DEFAULT) + { + packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); + packet->append(ha_row_type[(uint) create_info.row_type]); + } + if (share->transactional != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); + packet->append(ha_choice_values[(uint) share->transactional], 1); + } + if (share->table_type == TABLE_TYPE_SEQUENCE) + packet->append(STRING_WITH_LEN(" SEQUENCE=1")); + if (table->s->key_block_size) + { + packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); + packet->append_ulonglong(table->s->key_block_size); + } + table->file->append_create_info(packet); + +end_options: + if (share->comment.length) + { + packet->append(STRING_WITH_LEN(" COMMENT=")); + append_unescaped(packet, share->comment.str, share->comment.length); + } + if (share->connect_string.length) + { + packet->append(STRING_WITH_LEN(" CONNECTION=")); + append_unescaped(packet, share->connect_string.str, share->connect_string.length); + } + append_create_options(thd, packet, share->option_list, check_options, + hton->table_options); + append_directory(thd, packet, "DATA", create_info.data_file_name); + append_directory(thd, packet, "INDEX", create_info.index_file_name); +} + /* Build a CREATE TABLE statement for a table. @@ -1790,7 +1975,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, enum_with_db_name with_db_name) { List field_list; - char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH]; + char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH]; const char *alias; String type; String def_value; @@ -1798,9 +1983,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, uint primary_key; KEY *key_info; TABLE *table= table_list->table; - handler *file= table->file; TABLE_SHARE *share= table->s; - HA_CREATE_INFO create_info; sql_mode_t sql_mode= thd->variables.sql_mode; bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | MODE_DB2 | @@ -1811,8 +1994,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, !foreign_db_mode; bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && !create_info_arg; - handlerton *hton; my_bitmap_map *old_map; + handlerton *hton; int error= 0; DBUG_ENTER("show_create_table"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -1822,7 +2005,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton= table->part_info->default_engine_type; else #endif - hton= file->ht; + hton= table->file->ht; restore_record(table, s->default_values); // Get empty record @@ -1971,12 +2154,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } key_info= table->key_info; - bzero((char*) &create_info, sizeof(create_info)); - /* Allow update_create_info to update row type, page checksums and options */ - create_info.row_type= share->row_type; - create_info.page_checksum= share->page_checksum; - create_info.options= share->db_create_options; - file->update_create_info(&create_info); primary_key= share->primary_key; for (uint i=0 ; i < share->keys ; i++,key_info++) @@ -2043,10 +2220,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, to the CREATE TABLE statement */ - if ((for_str= file->get_foreign_key_create_info())) + if ((for_str= table->file->get_foreign_key_create_info())) { packet->append(for_str, strlen(for_str)); - file->free_foreign_key_create_info(for_str); + table->file->free_foreign_key_create_info(for_str); } /* Add table level check constraints */ @@ -2073,146 +2250,9 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (show_table_options) - { - /* - IF check_create_info - THEN add ENGINE only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) - { - if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) - packet->append(STRING_WITH_LEN(" TYPE=")); - else - packet->append(STRING_WITH_LEN(" ENGINE=")); - packet->append(hton_name(hton)); - } - - /* - Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, - and NEXT_ID > 1 (the default). We must not print the clause - for engines that do not support this as it would break the - import of dumps, but as of this writing, the test for whether - AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... - is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) - Because of that, we do not explicitly test for the feature, - but may extrapolate its existence from that of an AUTO_INCREMENT column. - */ - - if (create_info.auto_increment_value > 1) - { - char *end; - packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); - end= longlong10_to_str(create_info.auto_increment_value, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) - { - /* - IF check_create_info - THEN add DEFAULT CHARSET only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) - { - packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); - packet->append(share->table_charset->csname); - if (!(share->table_charset->state & MY_CS_PRIMARY)) - { - packet->append(STRING_WITH_LEN(" COLLATE=")); - packet->append(table->s->table_charset->name); - } - } - } - - if (share->min_rows) - { - char *end; - packet->append(STRING_WITH_LEN(" MIN_ROWS=")); - end= longlong10_to_str(share->min_rows, buff, 10); - packet->append(buff, (uint) (end- buff)); - } - - if (share->max_rows && !table_list->schema_table) - { - char *end; - packet->append(STRING_WITH_LEN(" MAX_ROWS=")); - end= longlong10_to_str(share->max_rows, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->avg_row_length) - { - char *end; - packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); - end= longlong10_to_str(share->avg_row_length, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (create_info.options & HA_OPTION_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); - if (create_info.options & HA_OPTION_NO_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); - if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); - if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); - if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); - else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); - if (share->stats_sample_pages != 0) - { - char *end; - packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); - end= longlong10_to_str(share->stats_sample_pages, buff, 10); - packet->append(buff, (uint) (end - buff)); - } + add_table_options(thd, table, create_info_arg, + table_list->schema_table != 0, 0, packet); - /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ - if (create_info.options & HA_OPTION_CHECKSUM) - packet->append(STRING_WITH_LEN(" CHECKSUM=1")); - if (create_info.page_checksum != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); - packet->append(ha_choice_values[create_info.page_checksum], 1); - } - if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) - packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); - if (create_info.row_type != ROW_TYPE_DEFAULT) - { - packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); - packet->append(ha_row_type[(uint) create_info.row_type]); - } - if (share->transactional != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); - packet->append(ha_choice_values[(uint) share->transactional], 1); - } - if (table->s->key_block_size) - { - char *end; - packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); - end= longlong10_to_str(table->s->key_block_size, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - table->file->append_create_info(packet); - if (share->comment.length) - { - packet->append(STRING_WITH_LEN(" COMMENT=")); - append_unescaped(packet, share->comment.str, share->comment.length); - } - if (share->connect_string.length) - { - packet->append(STRING_WITH_LEN(" CONNECTION=")); - append_unescaped(packet, share->connect_string.str, share->connect_string.length); - } - append_create_options(thd, packet, share->option_list, check_options, - hton->table_options); - append_directory(thd, packet, "DATA", create_info.data_file_name); - append_directory(thd, packet, "INDEX", create_info.index_file_name); - } #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && @@ -2423,6 +2463,55 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) } +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet) +{ + TABLE *table= table_list->table; + SEQUENCE *seq= table->s->sequence; + LEX_STRING alias; + sql_mode_t sql_mode= thd->variables.sql_mode; + bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | + MODE_MSSQL | MODE_DB2 | + MODE_MAXDB | MODE_ANSI); + bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) && + !foreign_db_mode; + + if (lower_case_table_names == 2) + { + alias.str= table->alias.c_ptr(); + alias.length= table->alias.length(); + } + else + alias= table->s->table_name; + + packet->append(STRING_WITH_LEN("CREATE SEQUENCE ")); + append_identifier(thd, packet, alias.str, alias.length); + packet->append(STRING_WITH_LEN(" start with ")); + packet->append_longlong(seq->start); + packet->append(STRING_WITH_LEN(" minvalue ")); + packet->append_longlong(seq->min_value); + packet->append(STRING_WITH_LEN(" maxvalue ")); + packet->append_longlong(seq->max_value); + packet->append(STRING_WITH_LEN(" increment by ")); + packet->append_longlong(seq->increment); + if (seq->cache) + { + packet->append(STRING_WITH_LEN(" cache ")); + packet->append_longlong(seq->cache); + } + else + packet->append(STRING_WITH_LEN(" nocache")); + if (seq->cycle) + packet->append(STRING_WITH_LEN(" cycle")); + else + packet->append(STRING_WITH_LEN(" nocycle")); + + if (show_table_options) + add_table_options(thd, table, 0, 0, 1, packet); + return 0; +} + + /**************************************************************************** Return info about all processes returns for each thread: thread id, user, host, db, command, info @@ -4143,7 +4232,8 @@ static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl, char path[FN_REFLEN]; build_table_filename(path, sizeof(path) - 1, db->str, table->str, reg_ext, 0); - if (dd_frm_type(thd, path, &engine_name) == FRMTYPE_TABLE) + bool is_sequence; + if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL) tl->option= engine_name.str; } } @@ -4360,10 +4450,14 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, { CHARSET_INFO *cs= system_charset_info; handlerton *hton; - if (ha_table_exists(thd, db_name->str, table_name->str, &hton)) + bool is_sequence; + if (ha_table_exists(thd, db_name->str, table_name->str, &hton, + &is_sequence)) { if (hton == view_pseudo_hton) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); + else if (is_sequence) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); } @@ -5041,7 +5135,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, String str(option_buff,sizeof(option_buff), system_charset_info); TABLE *show_table= tables->table; TABLE_SHARE *share= show_table->s; - handler *file= show_table->file; + handler *file= show_table->db_stat ? show_table->file : 0; handlerton *tmp_db_type= share->db_type(); #ifdef WITH_PARTITION_STORAGE_ENGINE bool is_partitioned= FALSE; @@ -5051,6 +5145,8 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else if (share->tmp_table) table->field[3]->store(STRING_WITH_LEN("LOCAL TEMPORARY"), cs); + else if (share->table_type == TABLE_TYPE_SEQUENCE) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index b12f0332035a5..4d577fffb3e4a 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 0a930554d8a00..3d7703b9f6150 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -473,6 +473,7 @@ class String 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 213b6eff6d2ba..5bd423b6ac789 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 @@ -1992,6 +1992,8 @@ int write_bin_log(THD *thd, bool clear_error, thd Thread handle tables List of tables to delete if_exists If 1, don't give error if one table doesn't exists + drop_temporary 1 if DROP TEMPORARY + drop_seqeunce 1 if DROP SEQUENCE NOTES Will delete all tables that can be deleted and give a compact error @@ -2008,8 +2010,8 @@ int write_bin_log(THD *thd, bool clear_error, */ -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary) +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence) { bool error; Drop_table_error_handler err_handler; @@ -2086,7 +2088,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, - false, false, false); + false, drop_sequence, false, false); thd->pop_internal_handler(); if (error) @@ -2175,6 +2177,7 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool dont_log_query, bool dont_free_locks) { @@ -2189,7 +2192,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; - bool was_view= 0; + bool was_view= 0, was_table, is_sequence; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2231,17 +2234,19 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, */ if (!dont_log_query) { + const char *object_to_drop= (drop_sequence) ? "SEQUENCE" : "TABLE"; + if (!drop_temporary) { const char *comment_start; uint32 comment_len; built_query.set_charset(thd->charset()); + built_query.append("DROP TABLE "); if (if_exists) - built_query.append("DROP TABLE IF EXISTS "); - else - built_query.append("DROP TABLE "); + built_query.append("IF EXISTS "); + /* Preserve comment in original query */ if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start))) { built_query.append(comment_start, comment_len); @@ -2249,21 +2254,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } } + built_trans_tmp_query.set_charset(system_charset_info); + built_trans_tmp_query.append("DROP TEMPORARY "); + built_trans_tmp_query.append(object_to_drop); + built_trans_tmp_query.append(' '); if (thd->is_current_stmt_binlog_format_row() || if_exists) { is_drop_tmp_if_exists_added= true; - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - } - else - { - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE "); + built_trans_tmp_query.append("IF EXISTS "); } + built_non_trans_tmp_query.set_charset(system_charset_info); + built_non_trans_tmp_query.copy(built_trans_tmp_query); } for (table= tables; table; table= table->next_local) @@ -2288,7 +2289,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) || + (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE)) error= 1; else { @@ -2396,26 +2398,31 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; if (drop_temporary || - (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) || - (!drop_view && (was_view= (table_type == view_pseudo_hton)))) + (ha_table_exists(thd, db, alias, &table_type, &is_sequence) == 0 && + table_type == 0) || + (!drop_view && (was_view= (table_type == view_pseudo_hton))) || + (drop_sequence && !is_sequence)) { /* One of the following cases happened: . "DROP TEMPORARY" but a temporary table was not found. . "DROP" but table was not found . "DROP TABLE" statement, but it's a view. + . "DROP SEQUENCE", but it's not a sequence */ + was_table= drop_sequence && table_type; if (if_exists) { char buff[FN_REFLEN]; + int err= (drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR); String tbl_name(buff, sizeof(buff), system_charset_info); tbl_name.length(0); tbl_name.append(db); tbl_name.append('.'); tbl_name.append(table->table_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_BAD_TABLE_ERROR, - ER_THD(thd, ER_BAD_TABLE_ERROR), + err, ER_THD(thd, err), tbl_name.c_ptr_safe()); } else @@ -2536,8 +2543,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, DBUG_ASSERT(errors); if (errors == 1 && was_view) my_error(ER_IT_IS_A_VIEW, MYF(0), wrong_tables.c_ptr_safe()); + else if (errors == 1 && drop_sequence && was_table) + my_error(ER_NOT_SEQUENCE2, MYF(0), wrong_tables.c_ptr_safe()); else if (errors > 1 || !thd->is_error()) - my_error(ER_BAD_TABLE_ERROR, MYF(0), wrong_tables.c_ptr_safe()); + my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR), + MYF(0), wrong_tables.c_ptr_safe()); error= 1; } @@ -3181,11 +3192,26 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); - select_field_pos= alter_info->create_list.elements - select_field_count; null_fields=blob_columns=0; create_info->varchar= 0; max_key_length= file->max_key_length(); + /* Handle creation of sequences */ + if (create_info->sequence) + { + if (!(file->ha_table_flags() & HA_CAN_TABLES_WITHOUT_ROLLBACK)) + { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), file->engine_name()->str, + "SEQUENCE"); + DBUG_RETURN(TRUE); + } + + /* The user specified fields: check that structure is ok */ + if (check_sequence_fields(thd->lex, &alter_info->create_list)) + DBUG_RETURN(TRUE); + } + + select_field_pos= alter_info->create_list.elements - select_field_count; for (field_no=0; (sql_field=it++) ; field_no++) { CHARSET_INFO *save_cs; @@ -4655,7 +4681,7 @@ int create_table_impl(THD *thd, */ (void) trans_rollback_stmt(thd); /* Remove normal table without logging. Keep tables locked */ - if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1)) + if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 0, 1, 1)) goto err; /* @@ -4802,7 +4828,7 @@ int create_table_impl(THD *thd, } } #endif - + error= 0; err: THD_STAGE_INFO(thd, stage_after_create); @@ -4825,10 +4851,11 @@ int create_table_impl(THD *thd, */ int mysql_create_table_no_lock(THD *thd, - const char *db, const char *table_name, - Table_specification_st *create_info, - Alter_info *alter_info, bool *is_trans, - int create_table_mode) + const char *db, const char *table_name, + Table_specification_st *create_info, + Alter_info *alter_info, bool *is_trans, + int create_table_mode, + TABLE_LIST *table_list) { KEY *not_used_1; uint not_used_2; @@ -4857,6 +4884,17 @@ int mysql_create_table_no_lock(THD *thd, alter_info, create_table_mode, is_trans, ¬_used_1, ¬_used_2, &frm); my_free(const_cast(frm.str)); + + if (!res && create_info->sequence) + { + /* Set create_info.table if temporary table */ + if (create_info->tmp_table()) + table_list->table= create_info->table; + else + table_list->table= 0; + res= sequence_insert(thd, thd->lex, table_list); + } + return res; } @@ -4918,7 +4956,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, promote_first_timestamp_column(&alter_info->create_list); if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, - &is_trans, create_table_mode) > 0) + &is_trans, create_table_mode, + create_table) > 0) { result= 1; goto err; @@ -5305,7 +5344,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, res= ((create_res= mysql_create_table_no_lock(thd, table->db, table->table_name, &local_create_info, &local_alter_info, - &is_trans, C_ORDINARY_CREATE)) > 0); + &is_trans, C_ORDINARY_CREATE, + table)) > 0); /* Remember to log if we deleted something */ do_logging= thd->log_current_statement; if (res) @@ -5542,7 +5582,7 @@ int mysql_discard_or_import_tablespace(THD *thd, table_list->mdl_request.set_type(MDL_EXCLUSIVE); table_list->lock_type= TL_WRITE; /* Do not open views. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; if (open_and_lock_tables(thd, table_list, FALSE, 0, &alter_prelocking_strategy)) @@ -7442,6 +7482,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!(used_fields & HA_CREATE_USED_CONNECTION)) create_info->connect_string= table->s->connect_string; + if (!(used_fields & HA_CREATE_USED_SEQUENCE)) + create_info->sequence= table->s->table_type == TABLE_TYPE_SEQUENCE; + restore_record(table, s->default_values); // Empty record for DEFAULT if ((create_info->fields_option_struct= (ha_field_option_struct**) @@ -8480,7 +8523,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Note that RENAME TABLE the only ALTER clause which is supported for views has been already processed. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; Alter_table_prelocking_strategy alter_prelocking_strategy; @@ -8587,7 +8630,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. */ - if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0)) + if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name)) { /* Table will be closed in do_command() */ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); @@ -9930,7 +9973,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, table->next_global= NULL; table->lock_type= TL_READ; /* Allow to open real tables only. */ - table->required_type= FRMTYPE_TABLE; + table->required_type= TABLE_TYPE_NORMAL; if (thd->open_temporary_tables(table) || open_and_lock_tables(thd, table, FALSE, 0)) diff --git a/sql/sql_table.h b/sql/sql_table.h index 5b2c9f32443b0..0ab6dcaa3ef81 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -196,7 +196,7 @@ int mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode); + int create_table_mode, TABLE_LIST *table); handler *mysql_create_frm_image(THD *thd, const char *db, const char *table_name, @@ -239,10 +239,11 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list); bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary); +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence); int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool log_query, bool dont_free_locks); bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, const char *table_name, size_t table_name_length, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ca9b143178508..ce80d53feb21a 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 c08c75f771abd..daa295d768e02 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -216,7 +216,7 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, or writing into the table. Yet, to open a write cursor we need a thr_lock lock. Allow to open base tables only. */ - table_ref->required_type= FRMTYPE_TABLE; + table_ref->required_type= TABLE_TYPE_NORMAL; /* Ignore pending FLUSH TABLES since we don't want to release the MDL lock taken above and otherwise there is no way to @@ -248,8 +248,8 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, table_ref->table->file->print_error(error, MYF(0)); /* If truncate method is not implemented then we don't binlog the - statement. If truncation has failed in a transactional engine then also we - donot binlog the statment. Only in non transactional engine we binlog + statement. If truncation has failed in a transactional engine then also + we don't binlog the statment. Only in non transactional engine we binlog inspite of errors. */ if (error == HA_ERR_WRONG_COMMAND || @@ -311,14 +311,17 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, } else { + handlerton *hton; + bool is_sequence; + /* Acquire an exclusive lock. */ DBUG_ASSERT(table_ref->next_global == NULL); if (lock_table_names(thd, table_ref, NULL, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); - handlerton *hton; - if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, &hton) || + if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, + &hton, &is_sequence) || hton == view_pseudo_hton) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name); @@ -337,7 +340,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, *hton_can_recreate= false; } else - *hton_can_recreate= hton->flags & HTON_CAN_RECREATE; + *hton_can_recreate= !is_sequence && hton->flags & HTON_CAN_RECREATE; } /* diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 90da7034ae316..09b1fbf65d03e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -683,8 +683,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, buff.append(views->source.str, views->source.length); int errcode= query_error_code(thd, TRUE); + /* + Don't log any unsafe warnings for CREATE VIEW as it's safely replicated + with statement based replication + */ + thd->reset_unsafe_warnings(); if (thd->binlog_query(THD::STMT_QUERY_TYPE, - buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode)) + buff.ptr(), buff.length(), FALSE, FALSE, FALSE, + errcode)) res= TRUE; } @@ -1015,7 +1021,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); - if (ha_table_exists(thd, view->db, view->table_name, NULL)) + if (ha_table_exists(thd, view->db, view->table_name)) { if (lex->create_info.if_not_exists()) { @@ -1153,7 +1159,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); - if (table->required_type == FRMTYPE_TABLE) + if (table->required_type == TABLE_TYPE_NORMAL) { my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "BASE TABLE"); @@ -1541,7 +1547,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) { - tbl->lock_type= table->lock_type; + /* We have to keep the lock type for sequence tables */ + if (!tbl->sequence) + tbl->lock_type= table->lock_type; tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ? MDL_SHARED_WRITE : MDL_SHARED_READ); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 869323d03cc1a..e06993bf9ca24 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -66,6 +66,7 @@ #include "rpl_mi.h" #include "lex_token.h" #include "sql_lex.h" +#include "sql_sequence.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -858,10 +859,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 102 shift/reduce conflicts. + Currently there are 103 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 102 +%expect 103 /* Comments for TOKENS. @@ -995,6 +996,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ %token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM %token DATABASE %token DATABASES %token DATAFILE_SYM @@ -1133,6 +1135,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IGNORE_SERVER_IDS_SYM %token IMMEDIATE_SYM /* SQL-2003-R */ %token IMPORT +%token INCREMENT_SYM %token INDEXES %token INDEX_SYM %token INFILE @@ -1165,6 +1168,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LANGUAGE_SYM /* SQL-2003-R */ %token LAST_SYM /* SQL-2003-N */ %token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ %token LE /* OPERATOR */ %token LEADING /* SQL-2003-R */ %token LEAVES @@ -1223,7 +1227,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_UPDATES_PER_HOUR %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MAXVALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -1236,6 +1240,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM %token MIN_ROWS %token MIN_SYM /* SQL-2003-N */ %token MODE_SYM @@ -1259,6 +1264,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NEG %token NEW_SYM /* SQL-2003-R */ %token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM %token NODEGROUP_SYM %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM @@ -1266,6 +1274,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM @@ -1323,6 +1333,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM +%token PREVIOUS_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ %token PROCEDURE_SYM /* SQL-2003-R */ @@ -1403,6 +1414,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SELECT_SYM /* SQL-2003-R */ %token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM +%token SEQUENCE_SYM %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ @@ -1682,6 +1694,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type ulonglong_num real_ulonglong_num size_number +%type + longlong_num + %type choice %type @@ -2450,6 +2465,64 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_lex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust()) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident opt_key_algorithm_clause ON table_ident @@ -2527,6 +2600,75 @@ create: { } ; +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | MAXVALUE_SYM opt_equal longlong_num + + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + } + ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3741,24 +3883,26 @@ sp_proc_stmt_open: } ; -sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor($3, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); - i= new (thd->mem_root) - sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_add_cfetch(thd, $2)) MYSQL_YYABORT; } - sp_fetch_list - {} + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3779,12 +3923,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -4605,7 +4743,7 @@ create_body: if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ - src_table->required_type= FRMTYPE_TABLE; + src_table->required_type= TABLE_TYPE_NORMAL; } ; @@ -5055,7 +5193,7 @@ opt_part_values: ; part_func_max: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; @@ -5169,7 +5307,7 @@ part_value_item_list: ; part_value_expr_item: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; if (part_info->part_type == LIST_PARTITION) @@ -5694,6 +5832,11 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } ; default_charset: @@ -6920,7 +7063,7 @@ alter: ALTER { Lex->name= null_lex_str; - Lex->only_view= FALSE; + Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; Lex->duplicates= DUP_ERROR; Lex->select_lex.init_order(); @@ -7796,7 +7939,9 @@ opt_checksum_type: repair_table_or_view: table_or_tables table_list opt_mi_repair_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type ; repair: @@ -7961,7 +8106,9 @@ binlog_base64_event: check_view_or_table: table_or_tables table_list opt_mi_check_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type ; check: CHECK_SYM @@ -9204,6 +9351,50 @@ column_default_non_parenthesized_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } ; simple_expr: @@ -11693,8 +11884,6 @@ delete_limit_clause: int_num: NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - ; ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } @@ -11713,6 +11902,13 @@ real_ulong_num: | dec_num_error { MYSQL_YYABORT; } ; +longlong_num: + NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + ulonglong_num: NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } @@ -12027,6 +12223,17 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12245,6 +12452,16 @@ opt_equal: | equal {} ; +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + no_braces: '(' { @@ -12791,7 +13008,15 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; - lex->only_view= 1; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; } | MASTER_SYM STATUS_SYM { @@ -13083,8 +13308,10 @@ opt_flush_lock: for (; tables; tables= tables->next_global) { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; } } ; @@ -14377,6 +14604,7 @@ keyword_sp: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} | DATETIME {} @@ -14434,6 +14662,7 @@ keyword_sp: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14449,6 +14678,7 @@ keyword_sp: | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} @@ -14493,6 +14723,7 @@ keyword_sp: | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} @@ -14508,7 +14739,12 @@ keyword_sp: | NATIONAL_SYM {} | NCHAR_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} @@ -14535,6 +14771,7 @@ keyword_sp: | POLYGON {} | PRESERVE_SYM {} | PREV_SYM {} + | PREVIOUS_SYM {} | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} @@ -14579,6 +14816,7 @@ keyword_sp: | SCHEDULE_SYM {} | SCHEMA_NAME_SYM {} | SECOND_SYM {} + | SEQUENCE_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 791e7a386c9ae..4718a29062735 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -66,6 +66,7 @@ #include "rpl_mi.h" #include "lex_token.h" #include "sql_lex.h" +#include "sql_sequence.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -273,10 +274,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 103 shift/reduce conflicts. + Currently there are 104 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 103 +%expect 104 /* Comments for TOKENS. @@ -410,6 +411,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ %token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM %token DATABASE %token DATABASES %token DATAFILE_SYM @@ -548,6 +550,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IGNORE_SERVER_IDS_SYM %token IMMEDIATE_SYM /* SQL-2003-R */ %token IMPORT +%token INCREMENT_SYM %token INDEXES %token INDEX_SYM %token INFILE @@ -580,6 +583,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LANGUAGE_SYM /* SQL-2003-R */ %token LAST_SYM /* SQL-2003-N */ %token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ %token LE /* OPERATOR */ %token LEADING /* SQL-2003-R */ %token LEAVES @@ -638,7 +642,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_UPDATES_PER_HOUR %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MAXVALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -651,6 +655,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM %token MIN_ROWS %token MIN_SYM /* SQL-2003-N */ %token MODE_SYM @@ -674,6 +679,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NEG %token NEW_SYM /* SQL-2003-R */ %token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM %token NODEGROUP_SYM %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM @@ -681,6 +689,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM @@ -738,6 +748,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM +%token PREVIOUS_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ %token PROCEDURE_SYM /* SQL-2003-R */ @@ -818,6 +829,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SELECT_SYM /* SQL-2003-R */ %token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM +%token SEQUENCE_SYM %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ @@ -1111,6 +1123,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type ulonglong_num real_ulonglong_num size_number +%type + longlong_num + %type choice %type @@ -1904,6 +1919,64 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_lex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust()) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident opt_key_algorithm_clause ON table_ident @@ -1981,6 +2054,75 @@ create: { } ; +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + } + | MAXVALUE_SYM opt_equal longlong_num + + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + } + ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -3425,24 +3567,26 @@ sp_proc_stmt_open: } ; -sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor($3, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); - i= new (thd->mem_root) - sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_add_cfetch(thd, $2)) MYSQL_YYABORT; } - sp_fetch_list - {} + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, $4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3463,12 +3607,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -4490,7 +4628,7 @@ create_body: if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ - src_table->required_type= FRMTYPE_TABLE; + src_table->required_type= TABLE_TYPE_NORMAL; } ; @@ -4940,7 +5078,7 @@ opt_part_values: ; part_func_max: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; @@ -5054,7 +5192,7 @@ part_value_item_list: ; part_value_expr_item: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; if (part_info->part_type == LIST_PARTITION) @@ -5579,6 +5717,11 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } ; default_charset: @@ -6939,7 +7082,7 @@ alter: ALTER { Lex->name= null_lex_str; - Lex->only_view= FALSE; + Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; Lex->duplicates= DUP_ERROR; Lex->select_lex.init_order(); @@ -7815,7 +7958,9 @@ opt_checksum_type: repair_table_or_view: table_or_tables table_list opt_mi_repair_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type ; repair: @@ -7980,7 +8125,9 @@ binlog_base64_event: check_view_or_table: table_or_tables table_list opt_mi_check_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type ; check: CHECK_SYM @@ -9292,6 +9439,50 @@ column_default_non_parenthesized_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_nextval(thd, table))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $4, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + TABLE_LIST *table; + if (!(table= Select->add_table_to_list(thd, $3, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_WRITE))) + MYSQL_YYABORT; + if (!($$= new (thd->mem_root) Item_func_lastval(thd, table))) + MYSQL_YYABORT; + } ; simple_expr: @@ -11811,8 +12002,6 @@ delete_limit_clause: int_num: NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - ; ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } @@ -11831,6 +12020,13 @@ real_ulong_num: | dec_num_error { MYSQL_YYABORT; } ; +longlong_num: + NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + ulonglong_num: NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } @@ -12145,6 +12341,17 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12363,6 +12570,16 @@ opt_equal: | equal {} ; +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + no_braces: '(' { @@ -12916,7 +13133,15 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; - lex->only_view= 1; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; } | MASTER_SYM STATUS_SYM { @@ -13208,8 +13433,10 @@ opt_flush_lock: for (; tables; tables= tables->next_global) { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; } } ; @@ -14650,6 +14877,7 @@ keyword_sp_not_data_type: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} | DAY_SYM {} @@ -14698,6 +14926,7 @@ keyword_sp_not_data_type: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14713,6 +14942,7 @@ keyword_sp_not_data_type: | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} @@ -14755,6 +14985,7 @@ keyword_sp_not_data_type: | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} @@ -14765,7 +14996,12 @@ keyword_sp_not_data_type: | NAME_SYM {} | NAMES_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} @@ -14788,6 +15024,7 @@ keyword_sp_not_data_type: | PLUGINS_SYM {} | PRESERVE_SYM {} | PREV_SYM {} + | PREVIOUS_SYM {} | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} @@ -14828,6 +15065,7 @@ keyword_sp_not_data_type: | SCHEDULE_SYM {} | SCHEMA_NAME_SYM {} | SECOND_SYM {} + | SEQUENCE_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} | SIMPLE_SYM {} diff --git a/sql/table.cc b/sql/table.cc index ee592f120d3e9..fae17fd8f20b5 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -42,6 +42,7 @@ #include "sql_view.h" #include "rpl_filter.h" #include "sql_cte.h" +#include "ha_sequence.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -431,6 +432,7 @@ void TABLE_SHARE::destroy() ha_share= NULL; // Safety } + delete sequence; free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; stats_cb.stats_is_read= FALSE; @@ -1327,6 +1329,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->db_create_options= db_create_options= uint2korr(frm_image+30); share->db_options_in_use= share->db_create_options; share->mysql_version= uint4korr(frm_image+51); + share->table_type= TABLE_TYPE_NORMAL; share->null_field_first= 0; if (!frm_image[32]) // New frm file in 3.23 { @@ -1340,6 +1343,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX); share->page_checksum= (ha_choice) enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX); + if (((ha_choice) enum_value_with_check(thd, share, "sequence", + (frm_image[39] >> 4) & 3, + HA_CHOICE_MAX)) == HA_CHOICE_YES) + { + share->table_type= TABLE_TYPE_SEQUENCE; + share->sequence= new (&share->mem_root) SEQUENCE(); + share->non_determinstic_insert= true; + } share->row_type= (enum row_type) enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); @@ -2534,7 +2545,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, HA_CREATE_INFO *create_info= &lex->create_info; // ... not CREATE TABLE - if (lex->sql_command != SQLCOM_CREATE_TABLE) + if (lex->sql_command != SQLCOM_CREATE_TABLE && + lex->sql_command != SQLCOM_CREATE_SEQUENCE) return 1; // ... create like if (lex->create_info.like()) @@ -2985,6 +2997,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!db_stat); } + if (share->sequence && outparam->file) + { + ha_sequence *file; + /* SEQUENCE table. Create a sequence handler over the original handler */ + if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share, + &outparam->mem_root))) + goto err; + file->register_original_handler(outparam->file); + outparam->file= file; + } + outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock= F_UNLCK; records=0; @@ -3679,7 +3702,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, create_info->default_table_charset->number : 0); fileinfo[38]= (uchar) csid; fileinfo[39]= (uchar) ((uint) create_info->transactional | - ((uint) create_info->page_checksum << 2)); + ((uint) create_info->page_checksum << 2) | + ((create_info->sequence ? HA_CHOICE_YES : 0) << 4)); fileinfo[40]= (uchar) create_info->row_type; /* Bytes 41-46 were for RAID support; now reused for other purposes */ fileinfo[41]= (uchar) (csid >> 8); @@ -3716,6 +3740,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; create_info->option_list= share->option_list; + create_info->sequence= MY_TEST(share->sequence); DBUG_VOID_RETURN; } diff --git a/sql/table.h b/sql/table.h index 4885196a81783..94a0d4834138b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -54,6 +54,7 @@ struct TDC_element; class Virtual_column_info; class Table_triggers_list; class TMP_TABLE_PARAM; +class SEQUENCE; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -642,6 +643,7 @@ struct TABLE_SHARE db_plugin ? plugin_hton(db_plugin) : NULL; } enum row_type row_type; /* How rows are stored */ + enum Table_type table_type; enum tmp_table_type tmp_table; /** Transactional or not. */ @@ -716,6 +718,9 @@ struct TABLE_SHARE */ const File_parser *view_def; + /* For sequence tables, the current sequence state */ + SEQUENCE *sequence; + /* Cache for row-based replication table share checks that does not need to be repeated. Possible values are: -1 when cache value is @@ -2058,8 +2063,8 @@ struct TABLE_LIST bool where_processed; /* TRUE <=> VIEW CHECK OPTION expression has been processed */ bool check_option_processed; - /* FRMTYPE_ERROR if any type is acceptable */ - enum frm_type_enum required_type; + /* TABLE_TYPE_UNKNOWN if any type is acceptable */ + Table_type required_type; handlerton *db_type; /* table_type for handler */ char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ /* @@ -2098,6 +2103,7 @@ struct TABLE_LIST bool merged_for_insert; /* TRUE <=> don't prepare this derived table/view as it should be merged.*/ bool skip_prepare_derived; + bool sequence; /* Part of NEXTVAL/CURVAL/LASTVAL */ /* Items created by create_view_field and collected to change them in case diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index c05fc632a94ff..0aa5396f35bd9 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -73,7 +73,9 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, if ((share= create_temporary_table(hton, frm, path, db, table_name))) { + open_options|= HA_OPEN_FOR_CREATE; table= open_temporary_table(share, table_name, open_in_engine); + open_options&= ~HA_OPEN_FOR_CREATE; /* Failed to open a temporary table instance. As we are not passing @@ -1117,8 +1119,10 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, if (open_table_from_share(this, share, alias, open_in_engine ? (uint)HA_OPEN_KEYFILE : 0, - EXTRA_RECORD, ha_open_options, table, - open_in_engine ? false : true)) + EXTRA_RECORD, + (ha_open_options | + (open_options & HA_OPEN_FOR_CREATE)), + table, open_in_engine ? false : true)) { my_free(table); DBUG_RETURN(NULL); @@ -1126,7 +1130,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, table->reginfo.lock_type= TL_WRITE; /* Simulate locked */ table->grant.privilege= TMP_TABLE_ACLS; - share->tmp_table= (table->file->has_transactions() ? + share->tmp_table= (table->file->has_transaction_manager() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); table->pos_in_table_list= 0; diff --git a/sql/udf_example.c b/sql/udf_example.c index a48801d1c4ac6..6a0d78078a6f2 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -56,7 +56,7 @@ ** ** Function 'myfunc_int' returns summary length of all its arguments. ** -** Function 'sequence' returns an sequence starting from a certain number. +** Function 'udf_sequence' returns an sequence starting from a certain number. ** ** Function 'myfunc_argument_name' returns name of argument. ** @@ -80,7 +80,7 @@ ** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; -** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so"; +** CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "udf_example.so"; ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; @@ -162,9 +162,9 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); - void sequence_deinit(UDF_INIT *initid); -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); + void udf_sequence_deinit(UDF_INIT *initid); +longlong udf_sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); void avgcost_deinit( UDF_INIT* initid ); @@ -642,7 +642,7 @@ my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), or 1 if no arguments have been given */ -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if (args->arg_count > 1) { @@ -659,22 +659,22 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } bzero(initid->ptr,sizeof(longlong)); /* - sequence() is a non-deterministic function : it has different value + udf_sequence() is a non-deterministic function : it has different value even if called with the same arguments. */ initid->const_item=0; return 0; } -void sequence_deinit(UDF_INIT *initid) +void udf_sequence_deinit(UDF_INIT *initid) { if (initid->ptr) free(initid->ptr); } -longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, - char *is_null __attribute__((unused)), - char *error __attribute__((unused))) +longlong udf_sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) { ulonglong val=0; if (args->arg_count) diff --git a/sql/udf_example.def b/sql/udf_example.def index 41150b24e8f58..74230b638bf1b 100644 --- a/sql/udf_example.def +++ b/sql/udf_example.def @@ -14,9 +14,9 @@ EXPORTS myfunc_double myfunc_int_init myfunc_int - sequence_init - sequence_deinit - sequence + udf_sequence_init + udf_sequence_deinit + udf_sequence avgcost_init avgcost_deinit avgcost_reset diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index 79dad57158542..0ccd533eac15b 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 c9dc31105c126..bbeb56d9b2185 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 6aacdd48ad853..bb8f4c6ff41fb 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3200,6 +3200,7 @@ ha_innobase::ha_innobase( */ | HA_CAN_EXPORT | HA_CAN_RTREEKEYS + | HA_CAN_TABLES_WITHOUT_ROLLBACK | HA_CONCURRENT_OPTIMIZE | (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0) ), @@ -17365,6 +17366,8 @@ ha_innobase::store_lock( && lock_type <= TL_WRITE)) || sql_command == SQLCOM_CREATE_INDEX || sql_command == SQLCOM_DROP_INDEX + || sql_command == SQLCOM_CREATE_SEQUENCE + || sql_command == SQLCOM_DROP_SEQUENCE || sql_command == SQLCOM_DELETE)) { ib_senderrf(trx->mysql_thd, @@ -17394,7 +17397,8 @@ ha_innobase::store_lock( } /* Check for DROP TABLE */ - } else if (sql_command == SQLCOM_DROP_TABLE) { + } else if (sql_command == SQLCOM_DROP_TABLE || + sql_command == SQLCOM_DROP_SEQUENCE) { /* MySQL calls this function in DROP TABLE though this table handle may belong to another thd that is running a query. Let @@ -17438,6 +17442,7 @@ ha_innobase::store_lock( && (sql_command == SQLCOM_INSERT_SELECT || sql_command == SQLCOM_REPLACE_SELECT || sql_command == SQLCOM_UPDATE + || sql_command == SQLCOM_CREATE_SEQUENCE || sql_command == SQLCOM_CREATE_TABLE))) { /* If we either have innobase_locks_unsafe_for_binlog diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 557da9ad2bb2a..86bb49c56256a 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 2b5536a9ce5cd..9a0d3ab7b5f91 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -693,7 +693,8 @@ ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg) HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS | HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | - HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR), + HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_REPAIR | + HA_CAN_TABLES_WITHOUT_ROLLBACK), can_enable_indexes(1) {} diff --git a/storage/rocksdb/rdb_datadic.cc b/storage/rocksdb/rdb_datadic.cc index 255a54cbdced3..fd5424c2c230a 100644 --- a/storage/rocksdb/rdb_datadic.cc +++ b/storage/rocksdb/rdb_datadic.cc @@ -2834,15 +2834,15 @@ bool Rdb_validate_tbls::check_frm_file(const std::string &fullpath, */ char eng_type_buf[NAME_CHAR_LEN+1]; LEX_STRING eng_type_str = {eng_type_buf, 0}; - //enum legacy_db_type eng_type; - frm_type_enum type = dd_frm_type(nullptr, fullfilename.c_ptr(), &eng_type_str); - if (type == FRMTYPE_ERROR) { + bool is_sequence; + enum Table_type type = dd_frm_type(nullptr, fullfilename.c_ptr(), &eng_type_str, &is_sequence); + if (type == TABLE_TYPE_UNKNOWN) { sql_print_warning("RocksDB: Failed to open/read .from file: %s", fullfilename.ptr()); return false; } - if (type == FRMTYPE_TABLE) { + if (type == TABLE_TYPE_NORMAL) { /* For a RocksDB table do we have a reference in the data dictionary? */ if (!strncmp(eng_type_str.str, "ROCKSDB", eng_type_str.length)) { /* diff --git a/storage/sequence/sequence.cc b/storage/sequence/sequence.cc index 599374c6f8c71..ad617c79a5a9b 100644 --- a/storage/sequence/sequence.cc +++ b/storage/sequence/sequence.cc @@ -29,7 +29,7 @@ #include #include -handlerton *sequence_hton; +static handlerton *sequence_hton; class Sequence_share : public Handler_share { public: From 470c3fd98d724fccbf96c049f2153bc1f5990915 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 26 Mar 2017 22:30:43 +0300 Subject: [PATCH 3/6] Change error message when using DROP VIEW on a non existing view from "Unknown table" to "Unknown view" --- mysql-test/r/create_drop_binlog.result | 2 +- mysql-test/r/create_drop_view.result | 2 +- mysql-test/r/drop.result | 10 +++++----- mysql-test/r/grant.result | 2 +- mysql-test/r/profiling.result | 2 +- mysql-test/r/sp-group.result | 2 +- mysql-test/r/sp.result | 2 +- mysql-test/r/view.result | 6 +++--- .../suite/funcs_1/r/innodb_views.result | 20 +++++++++---------- .../suite/funcs_1/r/memory_views.result | 20 +++++++++---------- .../suite/funcs_1/views/views_master.inc | 4 ++-- .../suite/rpl/r/rpl_create_drop_view.result | 4 ++-- mysql-test/suite/rpl/r/rpl_drop_view.result | 4 ++-- .../suite/rpl/t/rpl_create_drop_view.test | 2 +- mysql-test/suite/rpl/t/rpl_drop_view.test | 4 ++-- mysql-test/t/drop.test | 2 +- mysql-test/t/view.test | 4 ++-- sql/sql_view.cc | 6 +++--- 18 files changed, 49 insertions(+), 49 deletions(-) diff --git a/mysql-test/r/create_drop_binlog.result b/mysql-test/r/create_drop_binlog.result index 249f99475bebd..d22d8dd5d92c8 100644 --- a/mysql-test/r/create_drop_binlog.result +++ b/mysql-test/r/create_drop_binlog.result @@ -160,7 +160,7 @@ Note 1050 Table 'v1' already exists DROP VIEW IF EXISTS v1; DROP VIEW IF EXISTS v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' SHOW BINLOG EVENTS; Log_name Pos Event_type Server_id End_log_pos Info # # Format_desc 1 # VER diff --git a/mysql-test/r/create_drop_view.result b/mysql-test/r/create_drop_view.result index 0ec337b9b25f8..fc9dca3ea2e07 100644 --- a/mysql-test/r/create_drop_view.result +++ b/mysql-test/r/create_drop_view.result @@ -55,5 +55,5 @@ id DROP VIEW IF EXISTS v1; DROP VIEW IF EXISTS v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' DROP TABLE t1; diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 1a06380e66f21..050e0b9cd2b82 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -198,7 +198,7 @@ ERROR 42S02: Unknown table 'test.table1' DROP TABLE table1,table2; ERROR 42S02: Unknown table 'test.table1,test.table2' DROP VIEW view1,view2,view3,view4; -ERROR 42S02: Unknown table 'test.view1,test.view2,test.view3,test.view4' +ERROR 42S02: Unknown VIEW: 'test.view1,test.view2,test.view3,test.view4' DROP TABLE IF EXISTS table1; Warnings: @@ -209,10 +209,10 @@ Note 1051 Unknown table 'test.table1' Note 1051 Unknown table 'test.table2' DROP VIEW IF EXISTS view1,view2,view3,view4; Warnings: -Note 1051 Unknown table 'test.view1' -Note 1051 Unknown table 'test.view2' -Note 1051 Unknown table 'test.view3' -Note 1051 Unknown table 'test.view4' +Note 4067 Unknown VIEW: 'test.view1' +Note 4067 Unknown VIEW: 'test.view2' +Note 4067 Unknown VIEW: 'test.view3' +Note 4067 Unknown VIEW: 'test.view4' # Test error message when trigger does not find table CREATE TABLE table1(a int); diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index b250137ebf82d..593d998754a2c 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1428,7 +1428,7 @@ Warnings: Note 1305 FUNCTION test.test_function does not exist drop view if exists v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' create table test (col1 varchar(30)); create function test_function() returns varchar(30) begin diff --git a/mysql-test/r/profiling.result b/mysql-test/r/profiling.result index 4c531a8a5f704..e87bfd4905689 100644 --- a/mysql-test/r/profiling.result +++ b/mysql-test/r/profiling.result @@ -415,7 +415,7 @@ select @@profiling; drop table if exists t1, t2, t3; drop view if exists v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' drop function if exists f1; set session profiling = OFF; set global profiling_history_size= @start_value; diff --git a/mysql-test/r/sp-group.result b/mysql-test/r/sp-group.result index 2e30b697103fb..db118496e9a8f 100644 --- a/mysql-test/r/sp-group.result +++ b/mysql-test/r/sp-group.result @@ -3,7 +3,7 @@ Warnings: Note 1051 Unknown table 'test.t1' drop view if exists view_t1; Warnings: -Note 1051 Unknown table 'test.view_t1' +Note 4067 Unknown VIEW: 'test.view_t1' SET sql_mode=ONLY_FULL_GROUP_BY; CREATE TABLE t1 ( pk INT, diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 4b571e526a286..3312a46dc887e 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3211,7 +3211,7 @@ drop procedure bug10961| DROP PROCEDURE IF EXISTS bug6866| DROP VIEW IF EXISTS tv| Warnings: -Note 1051 Unknown table 'test.tv' +Note 4067 Unknown VIEW: 'test.tv' DROP TABLE IF EXISTS tt1,tt2,tt3| Warnings: Note 1051 Unknown table 'test.tt1' diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 20c9a1d2e80bf..9c0732a81b031 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -197,7 +197,7 @@ c d 2 5 3 10 drop view v100; -ERROR 42S02: Unknown table 'test.v100' +ERROR 42S02: Unknown VIEW: 'test.v100' drop view t1; ERROR HY000: 'test.t1' is not VIEW drop table v1; @@ -2894,7 +2894,7 @@ Tables_in_test t1 v1 DROP VIEW v2,v1; -ERROR 42S02: Unknown table 'test.v2' +ERROR 42S02: Unknown VIEW: 'test.v2' SHOW TABLES; Tables_in_test t1 @@ -5222,7 +5222,7 @@ CREATE TABLE t4 (i4 INT); INSERT INTO t4 VALUES (1),(2); DROP VIEW IF EXISTS v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' CREATE VIEW v1 AS select coalesce(j1,i3) AS v1_field1 from t2 join t3 left join t1 on ( i1 = i2 ); CREATE VIEW v2 AS select v1_field1 from t4 join v1; prepare my_stmt from "select v1_field1 from v2"; diff --git a/mysql-test/suite/funcs_1/r/innodb_views.result b/mysql-test/suite/funcs_1/r/innodb_views.result index 0ce0130e727eb..294e0695226d0 100644 --- a/mysql-test/suite/funcs_1/r/innodb_views.result +++ b/mysql-test/suite/funcs_1/r/innodb_views.result @@ -4314,7 +4314,7 @@ CREATE VIEW v2 AS Select * from test.v1; ERROR 42S02: Table 'test.v1' doesn't exist DROP VIEW IF EXISTS v2; Warnings: -Note 1051 Unknown table 'test.v2' +Note 4067 Unknown VIEW: 'test.v2' Testcase 3.3.1.25 -------------------------------------------------------------------------------- @@ -7566,7 +7566,7 @@ Call sp1() ; ERROR 42000: PROCEDURE test.sp1 does not exist Drop view if exists test.v1 ; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' Drop procedure sp1 ; ERROR 42000: PROCEDURE test.sp1 does not exist @@ -21307,12 +21307,12 @@ CREATE TABLE t1 ( f1 VARCHAR(1000) ) ENGINE = innodb ; CREATE VIEW v1 AS SELECT f1 FROM t1; DROP VIEW v1; DROP VIEW v1; -ERROR 42S02: Unknown table 'test.v1' +ERROR 42S02: Unknown VIEW: 'test.v1' CREATE VIEW v1 AS SELECT f1 FROM t1; DROP VIEW IF EXISTS v1; DROP VIEW IF EXISTS v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' Testcase 3.3.1.68 -------------------------------------------------------------------------------- @@ -21324,31 +21324,31 @@ CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top CASCADE ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base CASCADE ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top RESTRICT ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base RESTRICT ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; Testcase 3.3.1.69, 3.3.1.70, 3.3.1.A5 diff --git a/mysql-test/suite/funcs_1/r/memory_views.result b/mysql-test/suite/funcs_1/r/memory_views.result index 0ca6e8d94d88c..87465035980c2 100644 --- a/mysql-test/suite/funcs_1/r/memory_views.result +++ b/mysql-test/suite/funcs_1/r/memory_views.result @@ -4315,7 +4315,7 @@ CREATE VIEW v2 AS Select * from test.v1; ERROR 42S02: Table 'test.v1' doesn't exist DROP VIEW IF EXISTS v2; Warnings: -Note 1051 Unknown table 'test.v2' +Note 4067 Unknown VIEW: 'test.v2' Testcase 3.3.1.25 -------------------------------------------------------------------------------- @@ -7567,7 +7567,7 @@ Call sp1() ; ERROR 42000: PROCEDURE test.sp1 does not exist Drop view if exists test.v1 ; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' Drop procedure sp1 ; ERROR 42000: PROCEDURE test.sp1 does not exist @@ -21309,12 +21309,12 @@ CREATE TABLE t1 ( f1 VARCHAR(1000) ) ENGINE = memory ; CREATE VIEW v1 AS SELECT f1 FROM t1; DROP VIEW v1; DROP VIEW v1; -ERROR 42S02: Unknown table 'test.v1' +ERROR 42S02: Unknown VIEW: 'test.v1' CREATE VIEW v1 AS SELECT f1 FROM t1; DROP VIEW IF EXISTS v1; DROP VIEW IF EXISTS v1; Warnings: -Note 1051 Unknown table 'test.v1' +Note 4067 Unknown VIEW: 'test.v1' Testcase 3.3.1.68 -------------------------------------------------------------------------------- @@ -21326,31 +21326,31 @@ CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top CASCADE ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base CASCADE ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; CREATE VIEW v1_base AS SELECT * FROM t1; CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_top RESTRICT ; DROP VIEW v1_top; -ERROR 42S02: Unknown table 'test.v1_top' +ERROR 42S02: Unknown VIEW: 'test.v1_top' CREATE VIEW v1_top AS SELECT * FROM v1_base; DROP VIEW v1_base RESTRICT ; DROP VIEW v1_base; -ERROR 42S02: Unknown table 'test.v1_base' +ERROR 42S02: Unknown VIEW: 'test.v1_base' DROP VIEW v1_top; Testcase 3.3.1.69, 3.3.1.70, 3.3.1.A5 diff --git a/mysql-test/suite/funcs_1/views/views_master.inc b/mysql-test/suite/funcs_1/views/views_master.inc index 54e7f2af54bae..17f5c1e55299f 100644 --- a/mysql-test/suite/funcs_1/views/views_master.inc +++ b/mysql-test/suite/funcs_1/views/views_master.inc @@ -2745,7 +2745,7 @@ CREATE VIEW v1 AS SELECT f1 FROM t1; # DROP VIEW DROP VIEW v1; ---error ER_BAD_TABLE_ERROR +--error ER_UNKNOWN_VIEW DROP VIEW v1; CREATE VIEW v1 AS SELECT f1 FROM t1; @@ -2792,7 +2792,7 @@ while ($num1) # DROP VIEW v1_top < |RESTRICD|CASCADE> must be successful. eval $aux1 ; # Check, that v1_top really no more exists + cleanup for the second sub test - --error ER_BAD_TABLE_ERROR + --error ER_UNKNOWN_VIEW DROP VIEW v1_top; CREATE VIEW v1_top AS SELECT * FROM v1_base; diff --git a/mysql-test/suite/rpl/r/rpl_create_drop_view.result b/mysql-test/suite/rpl/r/rpl_create_drop_view.result index fef1edb201832..ea1e335404561 100644 --- a/mysql-test/suite/rpl/r/rpl_create_drop_view.result +++ b/mysql-test/suite/rpl/r/rpl_create_drop_view.result @@ -96,10 +96,10 @@ connection master; DROP VIEW v1; DROP TABLE t1; DROP VIEW v1; -ERROR 42S02: Unknown table 'test.v1' +ERROR 42S02: Unknown VIEW: 'test.v1' DROP VIEW IF EXISTS v2; Warnings: -Note 1051 Unknown table 'test.v2' +Note 4067 Unknown VIEW: 'test.v2' # Syncing slave with master connection slave; SELECT * FROM v1; diff --git a/mysql-test/suite/rpl/r/rpl_drop_view.result b/mysql-test/suite/rpl/r/rpl_drop_view.result index f6ced4eb41f56..aa4df701609b2 100644 --- a/mysql-test/suite/rpl/r/rpl_drop_view.result +++ b/mysql-test/suite/rpl/r/rpl_drop_view.result @@ -9,9 +9,9 @@ create view v1 as select * from t1; create view v2 as select * from t2; create view v3 as select * from t3; drop view not_exist_view; -ERROR 42S02: Unknown table 'test.not_exist_view' +ERROR 42S02: Unknown VIEW: 'test.not_exist_view' drop view v1, not_exist_view; -ERROR 42S02: Unknown table 'test.not_exist_view' +ERROR 42S02: Unknown VIEW: 'test.not_exist_view' select * from v1; ERROR 42S02: Table 'test.v1' doesn't exist drop view v2, v3; diff --git a/mysql-test/suite/rpl/t/rpl_create_drop_view.test b/mysql-test/suite/rpl/t/rpl_create_drop_view.test index 9280f35f63836..c26243ab3c699 100644 --- a/mysql-test/suite/rpl/t/rpl_create_drop_view.test +++ b/mysql-test/suite/rpl/t/rpl_create_drop_view.test @@ -41,7 +41,7 @@ connection master; DROP VIEW v1; DROP TABLE t1; ---error ER_BAD_TABLE_ERROR +--error ER_UNKNOWN_VIEW DROP VIEW v1; DROP VIEW IF EXISTS v2; diff --git a/mysql-test/suite/rpl/t/rpl_drop_view.test b/mysql-test/suite/rpl/t/rpl_drop_view.test index 55a0ea104d8d5..1893dd21926fe 100644 --- a/mysql-test/suite/rpl/t/rpl_drop_view.test +++ b/mysql-test/suite/rpl/t/rpl_drop_view.test @@ -13,9 +13,9 @@ create table t3 (c int); create view v1 as select * from t1; create view v2 as select * from t2; create view v3 as select * from t3; ---error 1051 +--error ER_UNKNOWN_VIEW drop view not_exist_view; ---error 1051 +--error ER_UNKNOWN_VIEW drop view v1, not_exist_view; --error 1146 select * from v1; diff --git a/mysql-test/t/drop.test b/mysql-test/t/drop.test index a3e96953bac02..6f506dd7215da 100644 --- a/mysql-test/t/drop.test +++ b/mysql-test/t/drop.test @@ -288,7 +288,7 @@ DROP TABLE t1; DROP TABLE table1; --error ER_BAD_TABLE_ERROR DROP TABLE table1,table2; ---error ER_BAD_TABLE_ERROR +--error ER_UNKNOWN_VIEW DROP VIEW view1,view2,view3,view4; --echo DROP TABLE IF EXISTS table1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index df5c7d3495da8..137902c54f205 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -125,7 +125,7 @@ select * from v1; select * from v2; # try to drop nonexistent VIEW --- error ER_BAD_TABLE_ERROR +--error ER_UNKNOWN_VIEW drop view v100; # try to drop table with DROP VIEW @@ -2782,7 +2782,7 @@ CREATE TABLE t1 (id INT); CREATE VIEW v1 AS SELECT id FROM t1; SHOW TABLES; ---error ER_BAD_TABLE_ERROR +--error ER_UNKNOWN_VIEW DROP VIEW v2,v1; SHOW TABLES; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 09b1fbf65d03e..fb4c2c3d83562 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1805,8 +1805,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) if (thd->lex->if_exists()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_BAD_TABLE_ERROR, - ER_THD(thd, ER_BAD_TABLE_ERROR), + ER_UNKNOWN_VIEW, + ER_THD(thd, ER_UNKNOWN_VIEW), name); continue; } @@ -1848,7 +1848,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) } if (non_existant_views.length()) { - my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr_safe()); + my_error(ER_UNKNOWN_VIEW, MYF(0), non_existant_views.c_ptr_safe()); } something_wrong= error || wrong_object_name || non_existant_views.length(); From 7c767a30a708e58b537568412da581400dddd1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 27 Mar 2017 18:58:16 +0300 Subject: [PATCH 4/6] MDEV-10139 Support for InnoDB SEQUENCE objects We introduce a NO_ROLLBACK flag for InnoDB tables. This flag only works for tables that have a single index. Apart from undo logging, this flag will also prevent locking and the assignment of DB_ROW_ID or DB_TRX_ID, and imply READ UNCOMMITTED isolation. It is assumed that the SQL layer is guaranteeing mutual exclusion. After the initial insert of the single record during CREATE SEQUENCE, InnoDB will be updating the single record in-place. This is crash-safe thanks to the redo log. (That is, after a crash after CREATE SEQUENCE was committed, the effect of sequence operations will be observable fully or not at all.) When it comes to the durability of the updates of SEQUENCE in InnoDB, there is a clear analogy to MDEV-6076 Persistent AUTO_INCREMENT. The updates would be made persistent by the InnoDB redo log flush at transaction commit or rollback (or XA PREPARE), provided that innodb_log_flush_at_trx_commit=1. Similar to AUTO_INCREMENT, it is possible that the update of a SEQUENCE in a middle of transaction becomes durable before the COMMIT/ROLLBACK of the transaction, in case the InnoDB redo log is being flushed as a result of the a commit or rollback of some other transaction, or as a result of a redo log checkpoint that can be initiated at any time by operations that are writing redo log. dict_table_t::no_rollback(): Check if the table does not support rollback. BTR_NO_ROLLBACK: Logging and locking flags for no_rollback() tables. DICT_TF_BITS: Add the NO_ROLLBACK flag. row_ins_step(): Assign 0 to DB_ROW_ID and DB_TRX_ID, and skip any locking for no-rollback tables. There will be only a single row in no-rollback tables (or there must be a proper PRIMARY KEY). row_search_mvcc(): Execute the READ UNCOMMITTED code path for no-rollback tables. ha_innobase::external_lock(), ha_innobase::store_lock(): Block CREATE/DROP SEQUENCE in innodb_read_only mode. This probably has no effect for CREATE SEQUENCE, because already ha_innobase::create() should have been called (and refused) before external_lock() or store_lock() is called. ha_innobase::store_lock(): For CREATE SEQUENCE, do not acquire any InnoDB locks, even though TL_WRITE is being requested. (This is just a performance optimization.) innobase_copy_frm_flags_from_create_info(), row_drop_table_for_mysql(): Disable persistent statistics for no_rollback tables. --- storage/innobase/dict/dict0dict.cc | 1 + storage/innobase/handler/ha_innodb.cc | 49 +++++++++++-------- storage/innobase/include/btr0cur.h | 5 ++ storage/innobase/include/dict0mem.h | 28 ++++++++--- storage/innobase/row/row0ins.cc | 26 ++++++++-- storage/innobase/row/row0mysql.cc | 2 +- storage/innobase/row/row0sel.cc | 10 ++-- storage/innobase/row/row0upd.cc | 6 +-- .../rocksdb/r/tbl_opt_data_index_dir.result | 4 +- 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index d7fcbdf390629..0db194b7e52bd 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -2502,6 +2502,7 @@ dict_index_add_to_cache_w_vcol( ut_d(mem_heap_validate(index->heap)); ut_a(!dict_index_is_clust(index) || UT_LIST_GET_LEN(table->indexes) == 0); + ut_ad(dict_index_is_clust(index) || !table->no_rollback()); if (!dict_index_find_cols(table, index, add_v)) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bb8f4c6ff41fb..927923263985c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3114,7 +3114,8 @@ innobase_copy_frm_flags_from_create_info( ibool ps_on; ibool ps_off; - if (dict_table_is_temporary(innodb_table)) { + if (dict_table_is_temporary(innodb_table) + || innodb_table->no_rollback()) { /* Temp tables do not use persistent stats. */ ps_on = FALSE; ps_off = TRUE; @@ -12909,6 +12910,10 @@ create_table_info_t::innobase_table_flags() default_compression_level : static_cast(options->page_compression_level), 0); + if (m_form->s->table_type == TABLE_TYPE_SEQUENCE) { + m_flags |= 1U << DICT_TF_POS_NO_ROLLBACK; + } + /* Set the flags2 when create table or alter tables */ m_flags2 |= DICT_TF2_FTS_AUX_HEX_NAME; DBUG_EXECUTE_IF("innodb_test_wrong_fts_aux_table_name", @@ -13539,6 +13544,10 @@ ha_innobase::create( trx_t* trx; DBUG_ENTER("ha_innobase::create"); + DBUG_ASSERT(form->s == table_share); + DBUG_ASSERT(table_share->table_type == TABLE_TYPE_SEQUENCE + || table_share->table_type == TABLE_TYPE_NORMAL); + create_table_info_t info(ha_thd(), form, create_info, @@ -16489,24 +16498,23 @@ ha_innobase::external_lock( } /* Check for UPDATEs in read-only mode. */ - if (srv_read_only_mode - && (thd_sql_command(thd) == SQLCOM_UPDATE - || thd_sql_command(thd) == SQLCOM_INSERT - || thd_sql_command(thd) == SQLCOM_REPLACE - || thd_sql_command(thd) == SQLCOM_DROP_TABLE - || thd_sql_command(thd) == SQLCOM_ALTER_TABLE - || thd_sql_command(thd) == SQLCOM_OPTIMIZE - || (thd_sql_command(thd) == SQLCOM_CREATE_TABLE - && lock_type == F_WRLCK) - || thd_sql_command(thd) == SQLCOM_CREATE_INDEX - || thd_sql_command(thd) == SQLCOM_DROP_INDEX - || thd_sql_command(thd) == SQLCOM_DELETE)) { - - if (thd_sql_command(thd) == SQLCOM_CREATE_TABLE) { - ib_senderrf(thd, IB_LOG_LEVEL_WARN, - ER_READ_ONLY_MODE); - DBUG_RETURN(HA_ERR_TABLE_READONLY); - } else { + if (srv_read_only_mode) { + switch (thd_sql_command(thd)) { + case SQLCOM_CREATE_TABLE: + if (lock_type != F_WRLCK) { + break; + } + case SQLCOM_UPDATE: + case SQLCOM_INSERT: + case SQLCOM_REPLACE: + case SQLCOM_DROP_TABLE: + case SQLCOM_ALTER_TABLE: + case SQLCOM_OPTIMIZE: + case SQLCOM_CREATE_INDEX: + case SQLCOM_DROP_INDEX: + case SQLCOM_CREATE_SEQUENCE: + case SQLCOM_DROP_SEQUENCE: + case SQLCOM_DELETE: ib_senderrf(thd, IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); @@ -17433,7 +17441,8 @@ ha_innobase::store_lock( /* Use consistent read for checksum table */ if (sql_command == SQLCOM_CHECKSUM - || (sql_command == SQLCOM_ANALYZE && lock_type == TL_READ) + || sql_command == SQLCOM_CREATE_SEQUENCE + || (sql_command == SQLCOM_ANALYZE && lock_type == TL_READ) || ((srv_locks_unsafe_for_binlog || trx->isolation_level <= TRX_ISO_READ_COMMITTED) && trx->isolation_level != TRX_ISO_SERIALIZABLE diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index fee7f375cb462..e1f5286e12206 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -42,6 +42,11 @@ enum { /** sys fields will be found in the update vector or inserted entry */ BTR_KEEP_SYS_FLAG = 4, + + /** no rollback */ + BTR_NO_ROLLBACK = BTR_NO_UNDO_LOG_FLAG + | BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG, + /** btr_cur_pessimistic_update() must keep cursor position when moving columns to big_rec */ BTR_KEEP_POS_FLAG = 8, diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 0630137bb4fdc..9b87e654b216f 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -147,17 +147,20 @@ Width of the page compression flag #define DICT_TF_WIDTH_PAGE_COMPRESSION 1 #define DICT_TF_WIDTH_PAGE_COMPRESSION_LEVEL 4 +/** +Width of atomic writes flag +DEFAULT=0, ON = 1, OFF = 2 +*/ +#define DICT_TF_WIDTH_ATOMIC_WRITES 2 + /** Width of the page encryption flag */ #define DICT_TF_WIDTH_PAGE_ENCRYPTION 1 #define DICT_TF_WIDTH_PAGE_ENCRYPTION_KEY 8 -/** -Width of atomic writes flag -DEFAULT=0, ON = 1, OFF = 2 -*/ -#define DICT_TF_WIDTH_ATOMIC_WRITES 2 +/** Width of the NO_ROLLBACK flag */ +#define DICT_TF_WIDTH_NO_ROLLBACK 1 /** Width of all the currently known table flags */ #define DICT_TF_BITS (DICT_TF_WIDTH_COMPACT \ @@ -169,7 +172,8 @@ DEFAULT=0, ON = 1, OFF = 2 + DICT_TF_WIDTH_PAGE_COMPRESSION_LEVEL \ + DICT_TF_WIDTH_ATOMIC_WRITES \ + DICT_TF_WIDTH_PAGE_ENCRYPTION \ - + DICT_TF_WIDTH_PAGE_ENCRYPTION_KEY) + + DICT_TF_WIDTH_PAGE_ENCRYPTION_KEY \ + + DICT_TF_WIDTH_NO_ROLLBACK) /** A mask of all the known/used bits in table flags */ #define DICT_TF_BIT_MASK (~(~0U << DICT_TF_BITS)) @@ -203,9 +207,11 @@ DEFAULT=0, ON = 1, OFF = 2 /** Zero relative shift position of the PAGE_ENCRYPTION_KEY field */ #define DICT_TF_POS_PAGE_ENCRYPTION_KEY (DICT_TF_POS_PAGE_ENCRYPTION \ + DICT_TF_WIDTH_PAGE_ENCRYPTION) -#define DICT_TF_POS_UNUSED (DICT_TF_POS_PAGE_ENCRYPTION_KEY \ +/** Zero relative shift position of the NO_ROLLBACK field */ +#define DICT_TF_POS_NO_ROLLBACK (DICT_TF_POS_PAGE_ENCRYPTION_KEY \ + DICT_TF_WIDTH_PAGE_ENCRYPTION_KEY) - +#define DICT_TF_POS_UNUSED (DICT_TF_POS_NO_ROLLBACK \ + + DICT_TF_WIDTH_NO_ROLLBACK) /** Bit mask of the COMPACT field */ #define DICT_TF_MASK_COMPACT \ ((~(~0U << DICT_TF_WIDTH_COMPACT)) \ @@ -1357,6 +1363,12 @@ struct dict_table_t { /** Release the table handle. */ inline void release(); + /** @return whether the table supports transactions */ + bool no_rollback() const + { + return flags & (1U << DICT_TF_POS_NO_ROLLBACK); + } + /** Id of the table. */ table_id_t id; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 9626645ebf28d..f596f3d8f27ec 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -3169,7 +3169,7 @@ row_ins_clust_index_entry( log_free_check(); const ulint flags = dict_table_is_temporary(index->table) ? BTR_NO_LOCKING_FLAG - : 0; + : index->table->no_rollback() ? BTR_NO_ROLLBACK : 0; err = row_ins_clust_index_entry_low( flags, BTR_MODIFY_LEAF, index, n_uniq, entry, @@ -3703,7 +3703,27 @@ row_ins_step( table during the search operation, and there is no need to set it again here. But we must write trx->id to node->trx_id_buf. */ - memset(node->trx_id_buf, 0, DATA_TRX_ID_LEN); + if (node->table->no_rollback()) { + /* No-rollback tables should only be accessed by a + single thread at a time. Concurrency control (mutual + exclusion) must be guaranteed by the SQL layer. */ + DBUG_ASSERT(node->table->n_ref_count == 1); + DBUG_ASSERT(node->ins_type == INS_DIRECT); + /* No-rollback tables can consist only of a single index. */ + DBUG_ASSERT(UT_LIST_GET_LEN(node->entry_list) == 1); + DBUG_ASSERT(UT_LIST_GET_LEN(node->table->indexes) == 1); + /* There should be no possibility for interruption and + restarting here. In theory, we could allow resumption + from the INS_NODE_INSERT_ENTRIES state here. */ + DBUG_ASSERT(node->state == INS_NODE_SET_IX_LOCK); + memset(node->trx_id_buf, 0, DATA_TRX_ID_LEN); + memset(node->row_id_buf, 0, DATA_ROW_ID_LEN); + node->index = dict_table_get_first_index(node->table); + node->entry = UT_LIST_GET_FIRST(node->entry_list); + node->state = INS_NODE_INSERT_ENTRIES; + goto do_insert; + } + trx_write_trx_id(node->trx_id_buf, trx->id); if (node->state == INS_NODE_SET_IX_LOCK) { @@ -3753,7 +3773,7 @@ row_ins_step( return(thr); } - +do_insert: /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ err = row_ins(node, thr); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 8b7c64868b8b0..03d7ac628a781 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3696,7 +3696,7 @@ row_drop_table_for_mysql( RemoteDatafile::delete_link_file(name); } - if (!dict_table_is_temporary(table)) { + if (!dict_table_is_temporary(table) && !table->no_rollback()) { dict_stats_recalc_pool_del(table); dict_stats_defrag_pool_del(table, NULL); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 229bd567c488d..106845f73fa70 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4130,9 +4130,10 @@ row_search_mvcc( ulint direction) { DBUG_ENTER("row_search_mvcc"); + DBUG_ASSERT(prebuilt->index->table == prebuilt->table); dict_index_t* index = prebuilt->index; - ibool comp = dict_table_is_comp(index->table); + ibool comp = dict_table_is_comp(prebuilt->table); const dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; @@ -4514,7 +4515,7 @@ row_search_mvcc( que_thr_move_to_run_state_for_mysql(thr, trx); - clust_index = dict_table_get_first_index(index->table); + clust_index = dict_table_get_first_index(prebuilt->table); /* Do some start-of-statement preparations */ @@ -4543,7 +4544,7 @@ row_search_mvcc( prebuilt->sql_stat_start = FALSE; } else { wait_table_again: - err = lock_table(0, index->table, + err = lock_table(0, prebuilt->table, prebuilt->select_lock_type == LOCK_S ? LOCK_IS : LOCK_IX, thr); @@ -5072,7 +5073,8 @@ row_search_mvcc( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ - if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) { + if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED + || prebuilt->table->no_rollback()) { /* Do nothing: we let a non-locking SELECT read the latest version of the record */ diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 18ea3cf3cf867..92af465aa4999 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -2260,7 +2260,7 @@ row_upd_sec_index_entry( flags = BTR_NO_LOCKING_FLAG; mtr.set_log_mode(MTR_LOG_NO_REDO); } else { - flags = 0; + flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0; } if (!index->is_committed()) { @@ -3046,11 +3046,11 @@ row_upd_clust_step( server or connection lifetime and so REDO information is not needed on restart for recovery. Disable locking as temp-tables are not shared across connection. */ - if (dict_table_is_temporary(index->table)) { + if (dict_table_is_temporary(node->table)) { flags = BTR_NO_LOCKING_FLAG; mtr.set_log_mode(MTR_LOG_NO_REDO); } else { - flags = 0; + flags = node->table->no_rollback() ? BTR_NO_ROLLBACK : 0; } /* If the restoration does not succeed, then the same diff --git a/storage/rocksdb/mysql-test/rocksdb/r/tbl_opt_data_index_dir.result b/storage/rocksdb/mysql-test/rocksdb/r/tbl_opt_data_index_dir.result index d1e445f734c29..37797c7a8da25 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/tbl_opt_data_index_dir.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/tbl_opt_data_index_dir.result @@ -3,14 +3,14 @@ CREATE TABLE t1 (a INT PRIMARY KEY, b CHAR(8)) ENGINE=rocksdb DATA DIRECTORY = ' ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options") show warnings; Level Code Message -Warning 1296 Got error 198 'Specifying DATA DIRECTORY for an individual table is not supported.' from ROCKSDB +Warning 1296 Got error 200 'Specifying DATA DIRECTORY for an individual table is not supported.' from ROCKSDB Error 1005 Can't create table `test`.`t1` (errno: 140 "Wrong create options") Warning 1030 Got error 140 "Wrong create options" from storage engine ROCKSDB CREATE TABLE t1 (a INT PRIMARY KEY, b CHAR(8)) ENGINE=rocksdb INDEX DIRECTORY = '/foo/bar/index'; ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options") show warnings; Level Code Message -Warning 1296 Got error 199 'Specifying INDEX DIRECTORY for an individual table is not supported.' from ROCKSDB +Warning 1296 Got error 201 'Specifying INDEX DIRECTORY for an individual table is not supported.' from ROCKSDB Error 1005 Can't create table `test`.`t1` (errno: 140 "Wrong create options") Warning 1030 Got error 140 "Wrong create options" from storage engine ROCKSDB CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY) ENGINE=rocksdb PARTITION BY RANGE (id) From 1bcfa14e26f96d5438277a87fce63c926e7766a2 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 29 Mar 2017 23:10:42 +0300 Subject: [PATCH 5/6] Simple cleanups - Added file name to error in mysql-test-run - When creating tags, first do it for sql to make it easier to find things in server --- mysql-test/mysql-test-run.pl | 2 +- support-files/build-tags | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 17a2d7db632e2..05169b2353136 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4235,7 +4235,7 @@ ($$) push(@lines, $line); if (scalar(@lines) > 1000000) { $Ferr = undef; - mtr_warning("Too much log from test, bailing out from extracting"); + mtr_warning("Too much log in $error_log, bailing out from extracting"); return (); } } diff --git a/support-files/build-tags b/support-files/build-tags index 03b243ee8cc6e..e0616661a75d2 100755 --- a/support-files/build-tags +++ b/support-files/build-tags @@ -5,8 +5,8 @@ rm -f TAGS if git rev-parse HEAD >/dev/null 2>&1 then cd `git rev-parse --show-toplevel` - echo client storage dbug libmysql sql-common \ - sql extra mysys mysys_ssl strings regex pcre vio include \ + echo sql mysys strings client storage dbug libmysql sql-common \ + extra mysys_ssl strings regex pcre vio include \ tools unittest plugin libmysqld | \ xargs -n1 git ls-files | grep -v '\.jar$' | \ xargs etags -o TAGS --append From 0177a9c74a44d7f3b3a2353f5e024286c81979e1 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 7 Apr 2017 10:08:20 +0300 Subject: [PATCH 6/6] Simple binary cache optimizations - Don't call my_chsize() for small (less than 64K) binary log tmp files - Don't flush cache to disk on reset. --- sql/log.cc | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 91da94af878ec..1f55b283a1307 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -61,6 +61,8 @@ #define MAX_LOG_BUFFER_SIZE 1024 #define MAX_TIME_SIZE 32 #define MY_OFF_T_UNDEF (~(my_off_t)0UL) +/* Truncate cache log files bigger than this */ +#define CACHE_FILE_TRUNC_SIZE 65536 #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -313,24 +315,19 @@ class binlog_cache_data void reset() { - compute_statistics(); - truncate(0); - if(cache_log.file != -1) + bool cache_was_empty= empty(); + bool truncate_file= (cache_log.file != -1 && + my_b_write_tell(&cache_log) > CACHE_FILE_TRUNC_SIZE); + truncate(0,1); // Forget what's in cache + if (!cache_was_empty) + compute_statistics(); + if (truncate_file) my_chsize(cache_log.file, 0, 0, MYF(MY_WME)); changes_to_non_trans_temp_table_flag= FALSE; status= 0; incident= FALSE; before_stmt_pos= MY_OFF_T_UNDEF; - /* - The truncate function calls reinit_io_cache that calls - my_b_flush_io_cache which may increase disk_writes. This breaks - the disk_writes use by the binary log which aims to compute the - ratio between in-memory cache usage and disk cache usage. To - avoid this undesirable behavior, we reset the variable after - truncating the cache. - */ - cache_log.disk_writes= 0; DBUG_ASSERT(empty()); } @@ -437,11 +434,16 @@ class binlog_cache_data */ void compute_statistics() { - if (!empty()) + statistic_increment(*ptr_binlog_cache_use, &LOCK_status); + if (cache_log.disk_writes != 0) { - statistic_increment(*ptr_binlog_cache_use, &LOCK_status); - if (cache_log.disk_writes != 0) - statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status); +#ifdef REAL_STATISTICS + statistic_add(*ptr_binlog_cache_disk_use, + cache_log.disk_writes, &LOCK_status); +#else + statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status); +#endif + cache_log.disk_writes= 0; } } @@ -470,7 +472,7 @@ class binlog_cache_data It truncates the cache to a certain position. This includes deleting the pending event. */ - void truncate(my_off_t pos) + void truncate(my_off_t pos, bool reset_cache=0) { DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos)); if (pending()) @@ -478,7 +480,7 @@ class binlog_cache_data delete pending(); set_pending(0); } - reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0); + reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, reset_cache); cache_log.end_of_file= saved_max_binlog_cache_size; }