summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_sequence.cc4
-rw-r--r--sql/handler.h1
-rw-r--r--sql/item_func.cc24
-rw-r--r--sql/item_func.h4
-rw-r--r--sql/share/errmsg-utf8.txt8
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_sequence.cc448
-rw-r--r--sql/sql_sequence.h153
-rw-r--r--sql/sql_show.cc84
-rw-r--r--sql/sql_type.cc2
-rw-r--r--sql/sql_yacc.yy267
12 files changed, 791 insertions, 211 deletions
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
index b348e6e7025..8f511ba3a7f 100644
--- a/sql/ha_sequence.cc
+++ b/sql/ha_sequence.cc
@@ -227,7 +227,7 @@ int ha_sequence::write_row(const uchar *buf)
int error= 0;
/* This is called from alter table */
tmp_seq.read_fields(table);
- if (tmp_seq.check_and_adjust(0))
+ if (tmp_seq.check_and_adjust(thd, 0))
DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
sequence->copy(&tmp_seq);
if (likely(!(error= file->write_row(buf))))
@@ -258,7 +258,7 @@ int ha_sequence::write_row(const uchar *buf)
DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT);
tmp_seq.read_fields(table);
- if (tmp_seq.check_and_adjust(0))
+ if (tmp_seq.check_and_adjust(thd, 0))
DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA);
/*
diff --git a/sql/handler.h b/sql/handler.h
index 6b05da2ca98..f573dc835e9 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1055,6 +1055,7 @@ enum enum_schema_tables
SCH_PROCEDURES,
SCH_SCHEMATA,
SCH_SCHEMA_PRIVILEGES,
+ SCH_SEQUENCES,
SCH_SESSION_STATUS,
SCH_SESSION_VARIABLES,
SCH_STATISTICS,
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a07595cbbd8..7d78a33c005 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -7070,7 +7070,9 @@ longlong Item_func_nextval::val_int()
}
entry->null_value= null_value= 0;
value= table->s->sequence->next_value(table, 0, &error);
+ unsigned_flag= table->s->sequence->is_unsigned;
entry->value= value;
+ entry->unsigned_flag= unsigned_flag;
entry->set_version(table);
if (unlikely(error)) // Warning already printed
@@ -7162,6 +7164,7 @@ longlong Item_func_lastval::val_int()
}
null_value= entry->null_value;
+ unsigned_flag= entry->unsigned_flag;
DBUG_RETURN(entry->value);
}
@@ -7192,8 +7195,20 @@ longlong Item_func_setval::val_int()
DBUG_RETURN(0);
}
- value= nextval;
- error= table->s->sequence->set_value(table, nextval, round, is_used);
+ /*
+ Truncate nextval according to the value type of the sequence if
+ out of bounds. If truncation happens i.e. nextval is out of
+ bounds for the value type, return null immediately.
+ */
+ value= table->s->sequence->truncate_value(nextval);
+ if (value != nextval.value())
+ {
+ null_value= 1;
+ value= 0;
+ DBUG_RETURN(0);
+ }
+ unsigned_flag= table->s->sequence->is_unsigned;
+ error= table->s->sequence->set_value(table, value, round, is_used);
if (unlikely(error))
{
null_value= 1;
@@ -7241,7 +7256,10 @@ void Item_func_setval::print(String *str, enum_query_type query_type)
}
append_identifier(thd, str, &t_name);
str->append(',');
- str->append_longlong(nextval);
+ if (nextval.is_unsigned())
+ str->append_ulonglong(nextval.value());
+ else
+ str->append_longlong(nextval.value());
str->append(',');
str->append_longlong(is_used);
str->append(',');
diff --git a/sql/item_func.h b/sql/item_func.h
index 520dbdc90c7..22f0cbd10bd 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -4190,11 +4190,11 @@ public:
class Item_func_setval :public Item_func_nextval
{
- longlong nextval;
+ Longlong_hybrid nextval;
ulonglong round;
bool is_used;
public:
- Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, longlong nextval_arg,
+ Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, Longlong_hybrid nextval_arg,
ulonglong round_arg, bool is_used_arg)
: Item_func_nextval(thd, table_list_arg),
nextval(nextval_arg), round(round_arg), is_used(is_used_arg)
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 65a7e613b72..79966ac9a05 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -10076,3 +10076,11 @@ ER_CM_OPTION_MISSING_REQUIREMENT
eng "CHANGE MASTER TO option '%s=%s' is missing requirement %s"
ER_SLAVE_STATEMENT_TIMEOUT 70100
eng "Slave log event execution was interrupted (slave_max_statement_time exceeded)"
+ER_SEQUENCE_TABLE_HAS_WRONG_NUMBER_OF_COLUMNS
+ eng "Wrong number of columns"
+ER_SEQUENCE_TABLE_CANNOT_HAVE_ANY_KEYS
+ eng "Sequence tables cannot have any keys"
+ER_SEQUENCE_TABLE_CANNOT_HAVE_ANY_CONSTRAINTS
+ eng "Sequence tables cannot have any constraints"
+ER_SEQUENCE_TABLE_ORDER_BY
+ eng "ORDER BY"
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index e3f486486d7..6645f8a0c77 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -8230,7 +8230,7 @@ Item *LEX::create_item_func_lastval(THD *thd,
Item *LEX::create_item_func_setval(THD *thd, Table_ident *table_ident,
- longlong nextval, ulonglong round,
+ Longlong_hybrid nextval, ulonglong round,
bool is_used)
{
TABLE_LIST *table;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 95ed308103d..0d8dd837542 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -4093,8 +4093,9 @@ public:
/*
Create an item for "SETVAL(sequence_name, value [, is_used [, round]])
*/
- Item *create_item_func_setval(THD *thd, Table_ident *ident, longlong value,
- ulonglong round, bool is_used);
+ Item *create_item_func_setval(THD *thd, Table_ident *ident,
+ Longlong_hybrid value, ulonglong round,
+ bool is_used);
/*
Create an item for a name in LIMIT clause: LIMIT var
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index 60da595afd0..3a4f5cfa141 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -30,85 +30,211 @@
#include "wsrep_mysqld.h"
#endif
-struct Field_definition
-{
- const char *field_name;
- uint length;
- const Type_handler *type_handler;
- LEX_CSTRING comment;
- ulong flags;
-};
+#define MAX_AUTO_INCREMENT_VALUE 65535
-/*
- Structure for all SEQUENCE tables
+/**
+ Structure for SEQUENCE tables of a certain value type
- 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.
+ @param in handler The handler of a sequence value type
+
+ @return The sequence table structure given the value type
*/
+Sequence_row_definition sequence_structure(const Type_handler* handler)
+{
+ /*
+ We don't really care about src because it is unused in
+ max_display_length_for_field().
+ */
+ const Conv_source src(handler, 0, system_charset_info);
+ const uint32 len= handler->max_display_length_for_field(src) + 1;
+ const LEX_CSTRING empty= {STRING_WITH_LEN("")};
+ const uint flag_unsigned= handler->is_unsigned() ? UNSIGNED_FLAG : 0;
+#define FNND (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG)
+#define FNNDFU (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG | flag_unsigned)
+ return {{{"next_not_cached_value", len, handler, empty, FNNDFU},
+ {"minimum_value", len, handler, empty, FNNDFU},
+ {"maximum_value", len, handler, empty, FNNDFU},
+ {"start_value", len, handler,
+ {STRING_WITH_LEN("start value when sequences is created or value "
+ "if RESTART is used")}, FNNDFU},
+ {"increment", 21, &type_handler_slonglong,
+ {STRING_WITH_LEN("increment value")}, FNND},
+ {"cache_size", 21, &type_handler_ulonglong, empty,
+ FNND | UNSIGNED_FLAG},
+ {"cycle_option", 1, &type_handler_utiny,
+ {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence "
+ "should begin a new cycle when maximum_value is "
+ "passed")}, FNND | UNSIGNED_FLAG},
+ {"cycle_count", 21, &type_handler_slonglong,
+ {STRING_WITH_LEN("How many cycles have been done")}, FNND},
+ {NULL, 0, &type_handler_slonglong, {STRING_WITH_LEN("")}, 0}}};
+#undef FNNDFU
+#undef FNND
+}
+
+/**
+ Whether a type is allowed as a sequence value type.
-#define FL (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG)
+ @param in type The type to check
-static Field_definition sequence_structure[]=
+ @retval true allowed
+ false not allowed
+*/
+bool sequence_definition::is_allowed_value_type(enum_field_types type)
{
- {"next_not_cached_value", 21, &type_handler_slonglong,
- {STRING_WITH_LEN("")}, FL},
- {"minimum_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("")}, FL},
- {"maximum_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("")}, FL},
- {"start_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("start value when sequences is created or value if RESTART is used")}, FL},
- {"increment", 21, &type_handler_slonglong,
- {STRING_WITH_LEN("increment value")}, FL},
- {"cache_size", 21, &type_handler_ulonglong, {STRING_WITH_LEN("")},
- FL | UNSIGNED_FLAG},
- {"cycle_option", 1, &type_handler_utiny, {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed")},
- FL | UNSIGNED_FLAG },
- {"cycle_count", 21, &type_handler_slonglong,
- {STRING_WITH_LEN("How many cycles have been done")}, FL},
- {NULL, 0, &type_handler_slonglong, {STRING_WITH_LEN("")}, 0}
-};
-
-#undef FL
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONGLONG:
+ return true;
+ default:
+ return false;
+ }
+}
+/*
+ Get the type handler for the value type of a sequence.
+*/
+Type_handler const *sequence_definition::value_type_handler()
+{
+ const Type_handler *handler=
+ Type_handler::get_handler_by_field_type(value_type);
+ return is_unsigned ? handler->type_handler_unsigned() : handler;
+}
-#define MAX_AUTO_INCREMENT_VALUE 65535
+/*
+ Get the upper bound for a sequence value type.
+*/
+longlong sequence_definition::value_type_max()
+{
+ /*
+ Use value_type != MYSQL_TYPE_LONGLONG to avoid undefined behaviour
+ https://stackoverflow.com/questions/9429156/by-left-shifting-can-a-number-be-set-to-zero
+ */
+ return is_unsigned && value_type != MYSQL_TYPE_LONGLONG ?
+ ~(~0ULL << 8 * value_type_handler()->calc_pack_length(0)) :
+ ~value_type_min();
+}
/*
+ Get the lower bound for a sequence value type.
+*/
+longlong sequence_definition::value_type_min() {
+ return is_unsigned ? 0 :
+ ~0ULL << (8 * value_type_handler()->calc_pack_length(0) - 1);
+}
+
+/**
+ Truncate a Longlong_hybrid.
+
+ If `original` is greater than value_type_max(), truncate down to
+ value_type_max()
+
+ If `original` is less than value_type_min(), truncate up to
+ value_type_min()
+
+ Whenever a truncation happens, the resulting value is just out of
+ bounds for sequence values because value_type_max() is the maximum
+ possible sequence value + 1, and the same applies to
+ value_type_min().
+
+ @param in original The value to truncate
+
+ @return The truncated value
+*/
+longlong sequence_definition::truncate_value(const Longlong_hybrid& original)
+{
+ if (is_unsigned)
+ return original.to_ulonglong(value_type_max());
+ else if (original.is_unsigned_outside_of_signed_range())
+ return value_type_max();
+ else
+ return original.value() > value_type_max() ? value_type_max()
+ : original.value() < value_type_min() ? value_type_min()
+ : original.value();
+}
+
+/**
Check whether sequence values are valid.
+
Sets default values for fields that are not used, according to Oracle spec.
- RETURN VALUES
- false valid
- true invalid
+ @param in thd The connection
+ @param in set_reserved_until Whether to set reserved_until to start
+
+ @retval false valid
+ true invalid
*/
-
-bool sequence_definition::check_and_adjust(bool set_reserved_until)
+bool sequence_definition::check_and_adjust(THD *thd, bool set_reserved_until)
{
- longlong max_increment;
- DBUG_ENTER("sequence_definition::check");
+ DBUG_ENTER("sequence_definition::check_and_adjust");
+
+ /* Easy error to detect. */
+ if (!is_allowed_value_type(value_type) || cache < 0)
+ DBUG_RETURN(TRUE);
if (!(real_increment= increment))
real_increment= global_system_variables.auto_increment_increment;
/*
- If min_value is not set, set it to LONGLONG_MIN or 1, depending on
- real_increment
+ If min_value is not set, in case of signed sequence, set it to
+ value_type_min()+1 or 1, depending on real_increment, and in case
+ of unsigned sequence, set it to value_type_min()+1
*/
- if (!(used_fields & seq_field_used_min_value))
- min_value= real_increment < 0 ? LONGLONG_MIN+1 : 1;
+ if (!(used_fields & seq_field_specified_min_value))
+ min_value= real_increment < 0 || is_unsigned ? value_type_min()+1 : 1;
+ else
+ {
+ min_value= truncate_value(min_value_from_parser);
+ if ((is_unsigned &&
+ (ulonglong) min_value <= (ulonglong) value_type_min()) ||
+ (!is_unsigned && min_value <= value_type_min()))
+ {
+ push_warning_printf(
+ thd, Sql_condition::WARN_LEVEL_NOTE, ER_TRUNCATED_WRONG_VALUE,
+ ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "INTEGER", "MINVALUE");
+ min_value= value_type_min() + 1;
+ }
+ }
/*
- If max_value is not set, set it to LONGLONG_MAX or -1, depending on
- real_increment
+ If max_value is not set, in case of signed sequence set it to
+ value_type_max()-1 or -1, depending on real_increment, and in case
+ of unsigned sequence, set it to value_type_max()-1
*/
- if (!(used_fields & seq_field_used_max_value))
- max_value= real_increment < 0 ? -1 : LONGLONG_MAX-1;
+ if (!(used_fields & seq_field_specified_max_value))
+ max_value= real_increment > 0 || is_unsigned ? value_type_max()-1 : -1;
+ else
+ {
+ max_value= truncate_value(max_value_from_parser);
+ if ((is_unsigned &&
+ (ulonglong) max_value >= (ulonglong) value_type_max()) ||
+ (!is_unsigned && max_value >= value_type_max()))
+ {
+ push_warning_printf(
+ thd, Sql_condition::WARN_LEVEL_NOTE, ER_TRUNCATED_WRONG_VALUE,
+ ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "INTEGER", "MAXVALUE");
+ max_value= value_type_max() - 1;
+ }
+ }
if (!(used_fields & seq_field_used_start))
{
/* Use min_value or max_value for start depending on real_increment */
start= real_increment < 0 ? max_value : min_value;
- }
+ } else
+ /*
+ If the supplied start value is out of range for the value type,
+ instead of immediately reporting error, we truncate it to
+ value_type_min or value_type_max depending on which side it is
+ one. Whenever such truncation happens, the condition that
+ max_value >= start >= min_value will be violated, and the error
+ will be reported then.
+ */
+ start= truncate_value(start_from_parser);
if (set_reserved_until)
reserved_until= start;
@@ -116,16 +242,31 @@ bool sequence_definition::check_and_adjust(bool set_reserved_until)
adjust_values(reserved_until);
/* To ensure that cache * real_increment will never overflow */
- max_increment= (real_increment ?
- llabs(real_increment) :
- MAX_AUTO_INCREMENT_VALUE);
+ const longlong max_increment= (real_increment ?
+ llabs(real_increment) :
+ MAX_AUTO_INCREMENT_VALUE);
+
+ /*
+ To ensure that cache * real_increment will never overflow. See the
+ calculation of add_to below in SEQUENCE::next_value(). We need
+ this for unsigned too, because otherwise we will need to handle
+ add_to as an equivalent of Longlong_hybrid type in
+ SEQUENCE::increment_value().
+ */
+ if (cache >= (LONGLONG_MAX - max_increment) / max_increment)
+ DBUG_RETURN(TRUE);
+
+ if (is_unsigned && (ulonglong) max_value >= (ulonglong) start &&
+ (ulonglong) max_value > (ulonglong) min_value &&
+ (ulonglong) start >= (ulonglong) min_value &&
+ ((real_increment > 0 &&
+ (ulonglong) reserved_until >= (ulonglong) min_value) ||
+ (real_increment < 0 &&
+ (ulonglong) reserved_until <= (ulonglong) max_value)))
+ DBUG_RETURN(FALSE);
- if (max_value >= start &&
- max_value > min_value &&
+ if (!is_unsigned && max_value >= start && max_value > min_value &&
start >= min_value &&
- max_value != LONGLONG_MAX &&
- min_value != LONGLONG_MIN &&
- cache >= 0 && cache < (LONGLONG_MAX - max_increment) / max_increment &&
((real_increment > 0 && reserved_until >= min_value) ||
(real_increment < 0 && reserved_until <= max_value)))
DBUG_RETURN(FALSE);
@@ -149,6 +290,11 @@ void sequence_definition::read_fields(TABLE *table)
cache= table->field[5]->val_int();
cycle= table->field[6]->val_int();
round= table->field[7]->val_int();
+ value_type= table->field[0]->type();
+ is_unsigned= table->field[0]->is_unsigned();
+ min_value_from_parser= Longlong_hybrid(min_value, is_unsigned);
+ max_value_from_parser= Longlong_hybrid(max_value, is_unsigned);
+ start_from_parser= Longlong_hybrid(start, is_unsigned);
dbug_tmp_restore_column_map(&table->read_set, old_map);
used_fields= ~(uint) 0;
print_dbug();
@@ -165,10 +311,10 @@ void sequence_definition::store_fields(TABLE *table)
/* 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[0]->store(reserved_until, is_unsigned);
+ table->field[1]->store(min_value, is_unsigned);
+ table->field[2]->store(max_value, is_unsigned);
+ table->field[3]->store(start, is_unsigned);
table->field[4]->store(increment, 0);
table->field[5]->store(cache, 0);
table->field[6]->store((longlong) cycle != 0, 0);
@@ -194,33 +340,40 @@ bool check_sequence_fields(LEX *lex, List<Create_field> *fields)
uint field_count;
uint field_no;
const char *reason;
+ Sequence_row_definition row_structure;
DBUG_ENTER("check_sequence_fields");
field_count= fields->elements;
- if (field_count != array_elements(sequence_structure)-1)
+ if (!field_count)
{
- reason= "Wrong number of columns";
+ reason= my_get_err_msg(ER_SEQUENCE_TABLE_HAS_WRONG_NUMBER_OF_COLUMNS);
+ goto err;
+ }
+ row_structure= sequence_structure(fields->head()->type_handler());
+ if (field_count != array_elements(row_structure.fields)-1)
+ {
+ reason= my_get_err_msg(ER_SEQUENCE_TABLE_HAS_WRONG_NUMBER_OF_COLUMNS);
goto err;
}
if (lex->alter_info.key_list.elements > 0)
{
- reason= "Sequence tables cannot have any keys";
+ reason= my_get_err_msg(ER_SEQUENCE_TABLE_CANNOT_HAVE_ANY_KEYS);
goto err;
}
if (lex->alter_info.check_constraint_list.elements > 0)
{
- reason= "Sequence tables cannot have any constraints";
+ reason= my_get_err_msg(ER_SEQUENCE_TABLE_CANNOT_HAVE_ANY_CONSTRAINTS);
goto err;
}
if (lex->alter_info.flags & ALTER_ORDER)
{
- reason= "ORDER BY";
+ reason= my_get_err_msg(ER_SEQUENCE_TABLE_ORDER_BY);
goto err;
}
for (field_no= 0; (field= it++); field_no++)
{
- Field_definition *field_def= &sequence_structure[field_no];
+ const Sequence_field_definition *field_def= &row_structure.fields[field_no];
if (my_strcasecmp(system_charset_info, field_def->field_name,
field->field_name.str) ||
field->flags != field_def->flags ||
@@ -249,12 +402,15 @@ err:
true Failure (out of memory)
*/
-bool prepare_sequence_fields(THD *thd, List<Create_field> *fields)
+bool sequence_definition::prepare_sequence_fields(List<Create_field> *fields,
+ bool alter)
{
- Field_definition *field_info;
DBUG_ENTER("prepare_sequence_fields");
+ const Sequence_row_definition row_def=
+ sequence_structure(value_type_handler());
- for (field_info= sequence_structure; field_info->field_name ; field_info++)
+ for (const Sequence_field_definition *field_info= row_def.fields;
+ field_info->field_name; field_info++)
{
Create_field *new_field;
LEX_CSTRING field_name= {field_info->field_name,
@@ -269,6 +425,8 @@ bool prepare_sequence_fields(THD *thd, List<Create_field> *fields)
new_field->char_length= field_info->length;
new_field->comment= field_info->comment;
new_field->flags= field_info->flags;
+ if (alter)
+ new_field->change = field_name;
if (unlikely(fields->push_back(new_field)))
DBUG_RETURN(TRUE); /* purify inspected */
}
@@ -297,20 +455,18 @@ bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list)
Reprepare_observer *save_reprepare_observer;
sequence_definition *seq= lex->create_info.seq_create_info;
bool temporary_table= org_table_list->table != 0;
+ /*
+ seq is 0 if sequence was created with CREATE TABLE instead of
+ CREATE SEQUENCE
+ */
+ bool create_new= !seq;
Open_tables_backup open_tables_backup;
Query_tables_list query_tables_list_backup;
TABLE_LIST table_list; // For sequence table
DBUG_ENTER("sequence_insert");
- /*
- 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);
- }
+ if (create_new && !(seq= new (thd->mem_root) sequence_definition))
+ DBUG_RETURN(TRUE);
#ifdef WITH_WSREP
if (WSREP_ON && seq->cache != 0)
@@ -371,7 +527,15 @@ bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list)
else
table= org_table_list->table;
- seq->reserved_until= seq->start;
+ if (create_new)
+ {
+ seq->value_type= (*table->s->field)->type();
+ seq->is_unsigned= (*table->s->field)->is_unsigned();
+ /* We set reserved_until when creating a new sequence. */
+ if (seq->check_and_adjust(thd, true))
+ DBUG_RETURN(TRUE);
+ }
+
error= seq->write_initial_sequence(table);
{
uint save_unsafe_rollback_flags=
@@ -416,9 +580,9 @@ SEQUENCE::~SEQUENCE()
A sequence table can have many readers (trough normal SELECT's).
We mark that we have a write lock in the table object so that
- ha_sequence::ha_write() can check if we have a lock. If already locked, then
+ ha_sequence::write_row() can check if we have a lock. If already locked, then
ha_write() knows that we are running a sequence operation. If not, then
- ha_write() knows that it's an INSERT.
+ ha_write() knows that it's an INSERT statement.
*/
void SEQUENCE::write_lock(TABLE *table)
@@ -588,15 +752,26 @@ void sequence_definition::adjust_values(longlong next_value)
/*
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
+ may overflow.
+
+ 0 <= to_add <= auto_increment_increment <= 65535 so we do not
+ need to cast to_add.
*/
- if (next_free_value > max_value - to_add ||
- next_free_value + to_add > max_value)
+ if ((is_unsigned &&
+ (ulonglong) next_free_value > (ulonglong) max_value - to_add) ||
+ (is_unsigned &&
+ (ulonglong) next_free_value + to_add > (ulonglong) max_value) ||
+ (!is_unsigned && next_free_value > max_value - to_add) ||
+ (!is_unsigned && next_free_value + to_add > max_value))
next_free_value= max_value+1;
else
{
next_free_value+= to_add;
- DBUG_ASSERT(llabs(next_free_value % real_increment) == offset);
+ if (is_unsigned)
+ DBUG_ASSERT((ulonglong) next_free_value % real_increment ==
+ (ulonglong) offset);
+ else
+ DBUG_ASSERT(llabs(next_free_value % real_increment) == offset);
}
}
}
@@ -718,10 +893,10 @@ longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error)
write_lock(table);
res_value= next_free_value;
- next_free_value= increment_value(next_free_value);
+ next_free_value= increment_value(next_free_value, real_increment);
- if ((real_increment > 0 && res_value < reserved_until) ||
- (real_increment < 0 && res_value > reserved_until))
+ if (within_bound(res_value, reserved_until, reserved_until,
+ real_increment > 0))
{
write_unlock(table);
DBUG_RETURN(res_value);
@@ -738,30 +913,10 @@ longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error)
overflow
*/
add_to= cache ? real_increment * cache : real_increment;
- out_of_values= 0;
- if (real_increment > 0)
- {
- if (reserved_until > max_value - add_to ||
- reserved_until + add_to > max_value)
- {
- 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;
- }
+ reserved_until= increment_value(reserved_until, add_to);
+ out_of_values= !within_bound(res_value, max_value + 1, min_value - 1,
+ add_to > 0);
if (out_of_values)
{
if (!cycle || second_round)
@@ -850,15 +1005,14 @@ int SEQUENCE::set_value(TABLE *table, longlong next_val, ulonglong next_round,
write_lock(table);
if (is_used)
- next_val= increment_value(next_val);
+ next_val= increment_value(next_val, real_increment);
if (round > next_round)
goto end; // error = -1
if (round == next_round)
{
- if (real_increment > 0 ?
- next_val < next_free_value :
- next_val > next_free_value)
+ if (within_bound(next_val, next_free_value, next_free_value,
+ real_increment > 0))
goto end; // error = -1
if (next_val == next_free_value)
{
@@ -879,9 +1033,8 @@ int SEQUENCE::set_value(TABLE *table, longlong next_val, ulonglong next_round,
round= next_round;
adjust_values(next_val);
- if ((real_increment > 0 ?
- next_free_value > reserved_until :
- next_free_value < reserved_until) ||
+ if (within_bound(reserved_until, next_free_value, next_free_value,
+ real_increment > 0) ||
needs_to_be_stored)
{
reserved_until= next_free_value;
@@ -937,6 +1090,35 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
first_table))
DBUG_RETURN(TRUE);
#endif /* WITH_WSREP */
+
+ if (new_seq->used_fields & seq_field_used_as)
+ {
+ /* This should have been prevented during parsing. */
+ DBUG_ASSERT(!(new_seq->used_fields - seq_field_used_as));
+
+ first_table->lock_type= TL_READ_NO_INSERT;
+ first_table->mdl_request.set_type(MDL_SHARED_NO_WRITE);
+ Alter_info alter_info;
+ alter_info.flags= ALTER_CHANGE_COLUMN;
+ if (new_seq->prepare_sequence_fields(&alter_info.create_list, true))
+ DBUG_RETURN(TRUE);
+ Table_specification_st create_info;
+ create_info.init();
+ create_info.alter_info= &alter_info;
+ if (if_exists())
+ thd->push_internal_handler(&no_such_table_handler);
+ error= mysql_alter_table(thd, &null_clex_str, &null_clex_str,
+ &create_info, first_table, &alter_info, 0,
+ (ORDER *) 0, 0, 0);
+ if (if_exists())
+ {
+ trapped_errors= no_such_table_handler.safely_trapped_errors();
+ thd->pop_internal_handler();
+ }
+ /* Do we need to store the sequence value in table share, like below? */
+ DBUG_RETURN(error);
+ }
+
if (if_exists())
thd->push_internal_handler(&no_such_table_handler);
error= open_and_lock_tables(thd, first_table, FALSE, 0);
@@ -972,28 +1154,42 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
/* Copy from old sequence those fields that the user didn't specified */
if (!(new_seq->used_fields & seq_field_used_increment))
new_seq->increment= seq->increment;
+ /*
+ We need to assign to foo_from_parser so that things get handled
+ properly in check_and_adjust() later
+ */
if (!(new_seq->used_fields & seq_field_used_min_value))
- new_seq->min_value= seq->min_value;
+ new_seq->min_value_from_parser= Longlong_hybrid(seq->min_value, seq->is_unsigned);
if (!(new_seq->used_fields & seq_field_used_max_value))
- new_seq->max_value= seq->max_value;
+ new_seq->max_value_from_parser= Longlong_hybrid(seq->max_value, seq->is_unsigned);
if (!(new_seq->used_fields & seq_field_used_start))
- new_seq->start= seq->start;
+ new_seq->start_from_parser= Longlong_hybrid(seq->start, seq->is_unsigned);
if (!(new_seq->used_fields & seq_field_used_cache))
new_seq->cache= seq->cache;
if (!(new_seq->used_fields & seq_field_used_cycle))
new_seq->cycle= seq->cycle;
+ /* This should have been prevented during parsing. */
+ DBUG_ASSERT(!(new_seq->used_fields & seq_field_used_as));
+ new_seq->value_type= seq->value_type;
+ new_seq->is_unsigned= seq->is_unsigned;
/* If we should restart from a new value */
if (new_seq->used_fields & seq_field_used_restart)
{
if (!(new_seq->used_fields & seq_field_used_restart_value))
- new_seq->restart= new_seq->start;
- new_seq->reserved_until= new_seq->restart;
+ new_seq->restart_from_parser= new_seq->start_from_parser;
+ /*
+ Similar to start, we just need to truncate reserved_until and
+ the errors will be reported in check_and_adjust if truncation
+ happens on the wrong end.
+ */
+ new_seq->reserved_until=
+ new_seq->truncate_value(new_seq->restart_from_parser);
}
/* Let check_and_adjust think all fields are used */
new_seq->used_fields= ~0;
- if (new_seq->check_and_adjust(0))
+ if (new_seq->check_and_adjust(thd, 0))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
first_table->db.str,
diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h
index 29c589e67cd..a599439e808 100644
--- a/sql/sql_sequence.h
+++ b/sql/sql_sequence.h
@@ -25,12 +25,35 @@
#define seq_field_used_cycle 32
#define seq_field_used_restart 64
#define seq_field_used_restart_value 128
+#define seq_field_used_as 256
+#define seq_field_specified_min_value 512
+#define seq_field_specified_max_value 1024
/* Field position in sequence table for some fields we refer to directly */
#define NEXT_FIELD_NO 0
#define MIN_VALUE_FIELD_NO 1
#define ROUND_FIELD_NO 7
+#include "mysql_com.h"
+#include "sql_type_int.h"
+
+class Create_field;
+class Type_handler;
+
+struct Sequence_field_definition
+{
+ const char *field_name;
+ uint length;
+ const Type_handler *type_handler;
+ LEX_CSTRING comment;
+ ulong flags;
+};
+
+struct Sequence_row_definition
+{
+ Sequence_field_definition fields[9];
+};
+
/**
sequence_definition is used when defining a sequence as part of create
*/
@@ -39,33 +62,56 @@ 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), restart(0), cycle(0), used_fields(0)
+ min_value_from_parser(1, false),
+ max_value_from_parser(LONGLONG_MAX-1, false), start_from_parser(1, false),
+ increment(1), cache(1000), round(0), restart_from_parser(0, false),
+ cycle(0), used_fields(0),
+ /*
+ We use value type and is_unsigned instead of a handler because
+ Type_handler is incomplete, which we cannot initialise here with
+ &type_handler_slonglong.
+ */
+ value_type(MYSQL_TYPE_LONGLONG), is_unsigned(false)
{}
longlong reserved_until;
longlong min_value;
longlong max_value;
longlong start;
+ Longlong_hybrid min_value_from_parser;
+ Longlong_hybrid max_value_from_parser;
+ Longlong_hybrid start_from_parser;
longlong increment;
longlong cache;
ulonglong round;
longlong restart; // alter sequence restart value
+ Longlong_hybrid restart_from_parser;
bool cycle;
uint used_fields; // Which fields where used in CREATE
+ enum_field_types value_type; // value type of the sequence
+ bool is_unsigned;
- bool check_and_adjust(bool set_reserved_until);
+ Type_handler const *value_type_handler();
+ /* max value for the value type, e.g. 32767 for smallint. */
+ longlong value_type_max();
+ /* min value for the value type, e.g. -32768 for smallint. */
+ longlong value_type_min();
+ bool check_and_adjust(THD *thd, bool set_reserved_until);
void store_fields(TABLE *table);
void read_fields(TABLE *table);
int write_initial_sequence(TABLE *table);
int write(TABLE *table, bool all_fields);
/* This must be called after sequence data has been updated */
void adjust_values(longlong next_value);
+ longlong truncate_value(const Longlong_hybrid& original);
inline 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));
}
+ static bool is_allowed_value_type(enum_field_types type);
+ bool prepare_sequence_fields(List<Create_field> *fields, bool alter);
+
protected:
/*
The following values are the values from sequence_definition
@@ -107,31 +153,90 @@ public:
longlong next_value(TABLE *table, bool second_round, int *error);
int set_value(TABLE *table, longlong next_value, ulonglong round_arg,
bool is_used);
- longlong increment_value(longlong value)
+
+ bool all_values_used;
+ seq_init initialized;
+
+private:
+ /**
+ Check that a value is within a relevant bound
+
+ If increasing sequence, check that the value is lower than an
+ upper bound, otherwise check that the value is higher than a lower
+ bound.
+
+ @param in value The value to check
+ @param in upper The upper bound
+ @param in lower The lower bound
+ @param in increasing Which bound to check
+
+ @retval true The value is within the bound.
+ false The value is out of the bound.
+ */
+ bool within_bound(const longlong value, const longlong upper,
+ const longlong lower, bool increasing)
+ {
+ return
+ (is_unsigned && increasing && (ulonglong) value < (ulonglong) upper) ||
+ (is_unsigned && !increasing && (ulonglong) value > (ulonglong) lower) ||
+ (!is_unsigned && increasing && value < upper) ||
+ (!is_unsigned && !increasing && value > lower);
+ }
+
+ /**
+ Increment a value, subject to truncation
+
+ Truncating to the nearer value between max_value + 1 and min_value
+ - 1
+
+ @param in value The original value
+ @param in increment The increment to add to the value
+
+ @return The value after increment and possible truncation
+ */
+ longlong increment_value(longlong value, const longlong increment)
{
- if (real_increment > 0)
+ if (is_unsigned)
{
- if (value > max_value - real_increment ||
- value + real_increment > max_value)
- value= max_value + 1;
+ if (increment > 0)
+ {
+ if (/* in case value + increment overflows */
+ (ulonglong) value >
+ (ulonglong) max_value - (ulonglong) increment ||
+ /* in case max_value - increment underflows */
+ (ulonglong) value + (ulonglong) increment >
+ (ulonglong) max_value)
+ value= max_value + 1;
+ else
+ value+= increment;
+ }
else
- value+= real_increment;
- }
- else
- {
- if (value + real_increment < min_value ||
- value < min_value - real_increment)
- value= min_value - 1;
+ {
+ if ((ulonglong) value - (ulonglong) (-increment) <
+ (ulonglong) min_value ||
+ (ulonglong) value <
+ (ulonglong) min_value + (ulonglong) (-increment))
+ value= min_value - 1;
+ else
+ value+= increment;
+ }
+ } else
+ if (increment > 0)
+ {
+ if (value > max_value - increment || value + increment > max_value)
+ value= max_value + 1;
+ else
+ value+= increment;
+ }
else
- value+= real_increment;
- }
+ {
+ if (value + increment < min_value || value < min_value - increment)
+ value= min_value - 1;
+ else
+ value+= increment;
+ }
return value;
}
-
- bool all_values_used;
- seq_init initialized;
-
-private:
mysql_rwlock_t mutex;
};
@@ -156,12 +261,10 @@ public:
uint length;
bool null_value;
longlong value;
+ bool unsigned_flag;
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 1fd31bd4947..fae01faa468 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2694,12 +2694,33 @@ static int show_create_sequence(THD *thd, TABLE_LIST *table_list,
packet->append(STRING_WITH_LEN("CREATE SEQUENCE "));
append_identifier(thd, packet, &alias);
+ /*
+ Do not show " as <type>" in oracle mode as it is not supported:
+ https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/CREATE-SEQUENCE.html
+ Also, do not show this if the type is the default type,
+ i.e. slonglong, for backward compatibility.
+ */
+ if (!(sql_mode & MODE_ORACLE) &&
+ seq->value_type_handler() != &type_handler_slonglong)
+ {
+ packet->append(STRING_WITH_LEN(" as "));
+ packet->append(seq->value_type_handler()->name().lex_cstring());
+ }
packet->append(STRING_WITH_LEN(" start with "));
- packet->append_longlong(seq->start);
+ if (seq->is_unsigned)
+ packet->append_ulonglong(seq->start);
+ else
+ packet->append_longlong(seq->start);
packet->append(STRING_WITH_LEN(" minvalue "));
- packet->append_longlong(seq->min_value);
+ if (seq->is_unsigned)
+ packet->append_ulonglong(seq->min_value);
+ else
+ packet->append_longlong(seq->min_value);
packet->append(STRING_WITH_LEN(" maxvalue "));
- packet->append_longlong(seq->max_value);
+ if (seq->is_unsigned)
+ packet->append_ulonglong(seq->max_value);
+ else
+ packet->append_longlong(seq->max_value);
packet->append(STRING_WITH_LEN(" increment by "));
packet->append_longlong(seq->increment);
if (seq->cache)
@@ -5512,6 +5533,36 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
DBUG_RETURN(0);
}
+static int get_schema_sequence_record(THD *thd, TABLE_LIST *tables,
+ TABLE *table, bool res,
+ const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name)
+{
+ DBUG_ENTER("get_sequence_record");
+ CHARSET_INFO *cs= system_charset_info;
+ restore_record(table, s->default_values);
+ sequence_definition *seq= tables->table->s->sequence;
+ if (tables->table->s->table_type == TABLE_TYPE_SEQUENCE)
+ {
+ const Type_handler *handler= seq->value_type_handler();
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
+ table->field[1]->store(db_name->str, db_name->length, cs);
+ table->field[2]->store(table_name->str, table_name->length, cs);
+ table->field[3]->store(handler->name().lex_cstring(), cs);
+ table->field[4]->store(8 * handler->calc_pack_length(0));
+ table->field[5]->store(2);
+ table->field[5]->set_notnull();
+ table->field[6]->store(0);
+ table->field[6]->set_notnull();
+ table->field[7]->store(seq->start, seq->is_unsigned);
+ table->field[8]->store(seq->min_value, seq->is_unsigned);
+ table->field[9]->store(seq->max_value, seq->is_unsigned);
+ table->field[10]->store(seq->increment, 0);
+ table->field[11]->store(seq->cycle);
+ DBUG_RETURN(schema_table_store_record(thd, table));
+ }
+ DBUG_RETURN(0);
+}
static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
@@ -9419,6 +9470,31 @@ ST_FIELD_INFO proc_fields_info[]=
};
+ST_FIELD_INFO sequence_fields_info[]=
+{
+ Column("SEQUENCE_CATALOG", Catalog(), NOT_NULL,
+ OPEN_FRM_ONLY),
+ Column("SEQUENCE_SCHEMA", Name(), NOT_NULL,
+ OPEN_FRM_ONLY),
+ Column("SEQUENCE_NAME", Name(), NOT_NULL, "Table",
+ OPEN_FRM_ONLY),
+ Column("DATA_TYPE", Name(), NOT_NULL),
+ Column("NUMERIC_PRECISION", SLong(21), NOT_NULL),
+ Column("NUMERIC_PRECISION_RADIX", SLong(21), NULLABLE),
+ Column("NUMERIC_SCALE", SLong(21), NULLABLE),
+ /*
+ Decimal types for these values to incorporate possibly unsigned
+ longlongs.
+ */
+ Column("START_VALUE", Decimal(2100), NOT_NULL),
+ Column("MINIMUM_VALUE", Decimal(2100), NOT_NULL),
+ Column("MAXIMUM_VALUE", Decimal(2100), NOT_NULL),
+ Column("INCREMENT", SLonglong(21), NOT_NULL),
+ Column("CYCLE_OPTION", Yes_or_empty(), NOT_NULL),
+ CEnd()
+};
+
+
ST_FIELD_INFO stat_fields_info[]=
{
Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
@@ -9985,6 +10061,8 @@ ST_SCHEMA_TABLE schema_tables[]=
fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0},
{"SCHEMA_PRIVILEGES", Show::schema_privileges_fields_info, 0,
fill_schema_schema_privileges, 0, 0, -1, -1, 0, 0},
+ {"SEQUENCES", Show::sequence_fields_info, 0,
+ get_all_tables, make_old_format, get_schema_sequence_record, 1, 2, 0, 0},
{"SESSION_STATUS", Show::variables_fields_info, 0,
fill_status, make_old_format, 0, 0, -1, 0, 0},
{"SESSION_VARIABLES", Show::variables_fields_info, 0,
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 3759c0ba02f..1207ffd4fb8 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -43,7 +43,7 @@ Named_type_handler<Type_handler_short> type_handler_sshort("smallint");
Named_type_handler<Type_handler_long> type_handler_slong("int");
Named_type_handler<Type_handler_int24> type_handler_sint24("mediumint");
Named_type_handler<Type_handler_longlong> type_handler_slonglong("bigint");
-Named_type_handler<Type_handler_utiny> type_handler_utiny("tiny unsigned");
+Named_type_handler<Type_handler_utiny> type_handler_utiny("tinyint unsigned");
Named_type_handler<Type_handler_ushort> type_handler_ushort("smallint unsigned");
Named_type_handler<Type_handler_ulong> type_handler_ulong("int unsigned");
Named_type_handler<Type_handler_uint24> type_handler_uint24("mediumint unsigned");
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index f0ed46646ae..87458122f44 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -200,6 +200,11 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)()
ulonglong ulonglong_number;
longlong longlong_number;
uint sp_instr_addr;
+ /*
+ Longlong_hybrid does not have a default constructor, hence the
+ default value below.
+ */
+ Longlong_hybrid longlong_hybrid_number= Longlong_hybrid(0, false);
/* structs */
LEX_CSTRING lex_str;
@@ -1466,7 +1471,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
ulonglong_num real_ulonglong_num
%type <longlong_number>
- longlong_num
+ sequence_value_num
+
+%type <longlong_hybrid_number>
+ sequence_value_hybrid_num sequence_truncated_value_hybrid_num
%type <choice> choice
@@ -2419,7 +2427,8 @@ create:
{
LEX *lex= thd->lex;
- if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1)))
+ if (unlikely(lex->create_info.seq_create_info->
+ check_and_adjust(thd, 1)))
{
my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
lex->first_select_lex()->table_list.first->db.str,
@@ -2429,12 +2438,13 @@ create:
}
/* No fields specified, generate them */
- if (unlikely(prepare_sequence_fields(thd,
- &lex->alter_info.create_list)))
+ if (unlikely(
+ lex->create_info.seq_create_info->prepare_sequence_fields(
+ &lex->alter_info.create_list, false)))
MYSQL_YYABORT;
/* CREATE SEQUENCE always creates a sequence */
- Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
+ Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
Lex->create_info.sequence= 1;
create_table_set_open_action_and_adjust_tables(lex);
@@ -2605,66 +2615,100 @@ sequence_defs:
;
sequence_def:
- MINVALUE_SYM opt_equal longlong_num
+ AS int_type field_options
+ {
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_as))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "AS"));
+ if ($3 & ZEROFILL_FLAG)
+ my_yyabort_error((ER_BAD_OPTION_VALUE, MYF(0), "ZEROFILL",
+ "AS"));
+ Lex->create_info.seq_create_info->value_type = $2->field_type();
+ Lex->create_info.seq_create_info->is_unsigned =
+ $3 & UNSIGNED_FLAG ? true : false;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_as;
+ }
+ | MINVALUE_SYM opt_equal sequence_truncated_value_hybrid_num
{
- Lex->create_info.seq_create_info->min_value= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_min_value))
+ my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MINVALUE"));
+ Lex->create_info.seq_create_info->min_value_from_parser= $3;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_min_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_specified_min_value;
}
| NO_SYM MINVALUE_SYM
{
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value))
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_min_value))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MINVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_min_value;
}
| NOMINVALUE_SYM
{
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value))
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_min_value))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MINVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_min_value;
}
- | MAXVALUE_SYM opt_equal longlong_num
+ | MAXVALUE_SYM opt_equal sequence_truncated_value_hybrid_num
{
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_max_value))
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_max_value))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->max_value= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
+ Lex->create_info.seq_create_info->max_value_from_parser= $3;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_max_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_specified_max_value;
}
| NO_SYM MAXVALUE_SYM
{
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value))
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_max_value))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_max_value;
}
| NOMAXVALUE_SYM
{
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value))
+ if (unlikely(Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_max_value))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_max_value;
}
- | START_SYM opt_with longlong_num
+ | START_SYM opt_with sequence_value_hybrid_num
{
if (unlikely(Lex->create_info.seq_create_info->used_fields &
seq_field_used_start))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "START"));
- Lex->create_info.seq_create_info->start= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_start;
+ Lex->create_info.seq_create_info->start_from_parser= $3;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_start;
}
- | INCREMENT_SYM opt_by longlong_num
+ | INCREMENT_SYM opt_by sequence_value_num
{
if (unlikely(Lex->create_info.seq_create_info->used_fields &
seq_field_used_increment))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "INCREMENT"));
Lex->create_info.seq_create_info->increment= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_increment;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_increment;
}
- | CACHE_SYM opt_equal longlong_num
+ | CACHE_SYM opt_equal sequence_value_num
{
if (unlikely(Lex->create_info.seq_create_info->used_fields &
seq_field_used_cache))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CACHE"));
Lex->create_info.seq_create_info->cache= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_cache;
}
| NOCACHE_SYM
{
@@ -2672,7 +2716,8 @@ sequence_def:
seq_field_used_cache))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CACHE"));
Lex->create_info.seq_create_info->cache= 0;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_cache;
}
| CYCLE_SYM
{
@@ -2680,7 +2725,8 @@ sequence_def:
seq_field_used_cycle))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CYCLE"));
Lex->create_info.seq_create_info->cycle= 1;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_cycle;
}
| NOCYCLE_SYM
{
@@ -2688,7 +2734,8 @@ sequence_def:
seq_field_used_cycle))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CYCLE"));
Lex->create_info.seq_create_info->cycle= 0;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_cycle;
}
| RESTART_SYM
{
@@ -2700,9 +2747,10 @@ sequence_def:
if (unlikely(Lex->create_info.seq_create_info->used_fields &
seq_field_used_restart))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "RESTART"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_restart;
}
- | RESTART_SYM opt_with longlong_num
+ | RESTART_SYM opt_with sequence_value_hybrid_num
{
if (unlikely(Lex->sql_command != SQLCOM_ALTER_SEQUENCE))
{
@@ -2712,8 +2760,9 @@ sequence_def:
if (unlikely(Lex->create_info.seq_create_info->used_fields &
seq_field_used_restart))
my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "RESTART"));
- Lex->create_info.seq_create_info->restart= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart | seq_field_used_restart_value;
+ Lex->create_info.seq_create_info->restart_from_parser= $3;
+ Lex->create_info.seq_create_info->used_fields|=
+ seq_field_used_restart | seq_field_used_restart_value;
}
;
@@ -7119,6 +7168,13 @@ alter:
{
/* Create a generic ALTER SEQUENCE statment. */
Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3);
+ if ((Lex->create_info.seq_create_info->used_fields &
+ seq_field_used_as) &&
+ (Lex->create_info.seq_create_info->used_fields -
+ seq_field_used_as))
+ my_yyabort_error((ER_NOT_SUPPORTED_YET, MYF(0),
+ "ALTER SEQUENCE with both AS <type> and "
+ "something else."));
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
} stmt_end {}
@@ -9728,19 +9784,23 @@ column_default_non_parenthesized_expr:
if (unlikely(!($$= Lex->create_item_func_lastval(thd, $3))))
MYSQL_YYABORT;
}
- | SETVAL_SYM '(' table_ident ',' longlong_num ')'
+ | SETVAL_SYM '(' table_ident ',' sequence_value_hybrid_num ')'
{
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0, 1))))
+ if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0,
+ 1))))
MYSQL_YYABORT;
}
- | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ')'
+ | SETVAL_SYM '(' table_ident ',' sequence_value_hybrid_num ',' bool ')'
{
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0, $7))))
+ if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0,
+ $7))))
MYSQL_YYABORT;
}
- | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ',' ulonglong_num ')'
+ | SETVAL_SYM '(' table_ident ',' sequence_value_hybrid_num ',' bool ','
+ ulonglong_num ')'
{
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, $9, $7))))
+ if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, $9,
+ $7))))
MYSQL_YYABORT;
}
;
@@ -12560,11 +12620,126 @@ real_ulong_num:
| dec_num_error { MYSQL_YYABORT; }
;
-longlong_num:
- opt_plus NUM { int error; $$= (longlong) my_strtoll10($2.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); }
+/*
+ For simple sequence metadata values that are signed and do not need
+ truncation
+*/
+sequence_value_num:
+ opt_plus NUM
+ {
+ int error;
+ $$= (longlong) my_strtoll10($2.str, (char**) 0, &error);
+ }
+ | opt_plus LONG_NUM
+ {
+ int error;
+ $$= (longlong) my_strtoll10($2.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
+ {
+ int error;
+ const ulonglong abs= my_strtoll10($2.str, (char**) 0, &error);
+ if (abs == 1 + (ulonglong) LONGLONG_MAX)
+ $$= LONGLONG_MIN;
+ else
+ thd->parse_error(ER_DATA_OUT_OF_RANGE);
+ }
+ ;
+
+/*
+ For sequence metadata values that may be unsigned but do not need
+ truncation (start, restart)
+*/
+sequence_value_hybrid_num:
+ opt_plus NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | opt_plus LONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | opt_plus ULONGLONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ true);
+ }
+ | '-' NUM
+ {
+ int error;
+ $$= Longlong_hybrid(- my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | '-' LONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(- my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | '-' ULONGLONG_NUM
+ {
+ int error;
+ const ulonglong abs= my_strtoll10($2.str, (char**) 0, &error);
+ if (abs == 1 + (ulonglong) LONGLONG_MAX)
+ $$= Longlong_hybrid(LONGLONG_MIN, false);
+ else
+ thd->parse_error(ER_DATA_OUT_OF_RANGE);
+ }
+ ;
+
+/*
+ For sequence metadata values that may be unsigned and need
+ truncation (maxvalue, minvalue)
+*/
+sequence_truncated_value_hybrid_num:
+ opt_plus NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | opt_plus LONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | opt_plus ULONGLONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(my_strtoll10($2.str, (char**) 0, &error),
+ true);
+ }
+ | opt_plus DECIMAL_NUM { $$= Longlong_hybrid(ULONGLONG_MAX, true); }
+ | '-' NUM
+ {
+ int error;
+ $$= Longlong_hybrid(- my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | '-' LONG_NUM
+ {
+ int error;
+ $$= Longlong_hybrid(- my_strtoll10($2.str, (char**) 0, &error),
+ false);
+ }
+ | '-' ULONGLONG_NUM { $$= Longlong_hybrid(LONGLONG_MIN, false); }
+ | '-' DECIMAL_NUM { $$= Longlong_hybrid(LONGLONG_MIN, false); }
;
ulonglong_num: