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