summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt3
-rw-r--r--sql/create_options.cc5
-rw-r--r--sql/datadict.cc33
-rw-r--r--sql/datadict.h19
-rw-r--r--sql/ha_partition.cc4
-rw-r--r--sql/ha_partition.h1
-rw-r--r--sql/ha_sequence.cc417
-rw-r--r--sql/ha_sequence.h138
-rw-r--r--sql/handler.cc69
-rw-r--r--sql/handler.h65
-rw-r--r--sql/item_func.cc145
-rw-r--r--sql/item_func.h42
-rw-r--r--sql/lex.h13
-rw-r--r--sql/lock.cc15
-rw-r--r--sql/lock.h3
-rw-r--r--sql/log_event.cc18
-rw-r--r--sql/log_event.h1
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/share/errmsg-utf8.txt18
-rw-r--r--sql/sp_head.cc8
-rw-r--r--sql/sql_acl.cc2
-rw-r--r--sql/sql_admin.cc13
-rw-r--r--sql/sql_base.cc21
-rw-r--r--sql/sql_builtin.cc.in8
-rw-r--r--sql/sql_class.cc30
-rw-r--r--sql/sql_class.h13
-rw-r--r--sql/sql_cmd.h2
-rw-r--r--sql/sql_const.h1
-rw-r--r--sql/sql_db.cc3
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc54
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_parse.cc100
-rw-r--r--sql/sql_plugin.cc5
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_select.cc9
-rw-r--r--sql/sql_sequence.cc670
-rw-r--r--sql/sql_sequence.h133
-rw-r--r--sql/sql_show.cc410
-rw-r--r--sql/sql_string.cc9
-rw-r--r--sql/sql_string.h1
-rw-r--r--sql/sql_table.cc119
-rw-r--r--sql/sql_table.h7
-rw-r--r--sql/sql_trigger.cc2
-rw-r--r--sql/sql_truncate.cc15
-rw-r--r--sql/sql_view.cc16
-rw-r--r--sql/sql_yacc.yy308
-rw-r--r--sql/sql_yacc_ora.yy308
-rw-r--r--sql/table.cc29
-rw-r--r--sql/table.h10
-rw-r--r--sql/temporary_tables.cc10
-rw-r--r--sql/udf_example.c22
-rw-r--r--sql/udf_example.def6
-rw-r--r--sql/wsrep_hton.cc3
-rw-r--r--sql/wsrep_mysqld.cc3
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, &not_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, &not_used_1, &not_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");
}