MDEV-22491 Support mariadb-check and CHECK TABLE with SEQUENCE
The check go through the following steps: 1. Run check on the underlying engine. If not ok, then return. 2. Check that there's only one row in the table, and 2.1 warn if more than one row 2.2 return HA_ADMIN_CORRUPT if fewer than one row (i.e. 0 rows) 3. If the sequence is not initialised (e.g. after an ALTER TABLE ... SEQUENCE=1), initialise the sequence by reading the sequence metadata from the table. This will also flush the next_free_value, i.e. set it to the next not cached value (SEQUENCE::reserved_until) 4. Check that the sequence metadata is valid, i.e. nothing out of order e.g. minvalue < maxvalue etc. If invalid it reports HA_ERR_SEQUENCE_INVALID_DATA 5. Check that the sequence has not been exhausted. It reports ER_SEQUENCE_RUN_OUT as a warning if and only if a SELECT NEXTVAL would do so Limitations: 1. The check is independent of flags, so the vanilla check is the same as CHECK ... EXTENDED or CHECK ... FOR UPGRADE etc. 2. When the check discovers invalid metadata from the table, subsequent SELECT NEXTVAL will carry on (or fail) without this piece of knowledge, independent of the CHECK. This is to ensure consistency, i.e. CHECK does not modify behaviour of SELECT, and if anything it makes more sense that SELECT reports HA_ERR_SEQUENCE_INVALID_DATA in this case, regardless of prior CHECK
This commit is contained in:
parent
26ea37be5d
commit
d52ddae57b
@ -32,14 +32,14 @@ drop sequence t1;
|
||||
create sequence s1;
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
select next value for s1;
|
||||
next value for s1
|
||||
1
|
||||
flush tables;
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
select next value for s1;
|
||||
next value for s1
|
||||
1001
|
||||
|
@ -23,7 +23,7 @@ Table Op Msg_type Msg_text
|
||||
test.s1 repair status OK
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
rename table s1 to tmp_s;
|
||||
rename table tmp_s to s1;
|
||||
drop sequence s1;
|
||||
|
117
mysql-test/suite/sql_sequence/check.result
Normal file
117
mysql-test/suite/sql_sequence/check.result
Normal file
@ -0,0 +1,117 @@
|
||||
create sequence s;
|
||||
call mtr.add_suppression("ha_myisam");
|
||||
call mtr.add_suppression("Checking table");
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check error Size of datafile is: 4 Should be: 58
|
||||
test.s check error Corrupt
|
||||
drop table s;
|
||||
create sequence s;
|
||||
insert into s values (3,1,9223372036854775806,1,1,1000,0,0);
|
||||
select * from s;
|
||||
next_not_cached_value minimum_value maximum_value start_value increment cache_size cycle_option cycle_count
|
||||
3 1 9223372036854775806 1 1 1000 0 0
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
select nextval(s);
|
||||
nextval(s)
|
||||
3
|
||||
drop sequence s;
|
||||
CREATE SEQUENCE s;
|
||||
ALTER TABLE s sequence=0;
|
||||
insert into s values (3,1,9223372036854775806,1,1,1000,0,0);
|
||||
FLUSH TABLES;
|
||||
CHECK TABLE s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning More than one row in the table
|
||||
test.s check status OK
|
||||
DROP SEQUENCE s;
|
||||
CREATE SEQUENCE s;
|
||||
ALTER TABLE s sequence=0;
|
||||
delete from s;
|
||||
FLUSH TABLES;
|
||||
CHECK TABLE s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Error Fewer than one row in the table
|
||||
test.s check error Corrupt
|
||||
DROP SEQUENCE s;
|
||||
CREATE SEQUENCE s;
|
||||
ALTER TABLE s sequence=0;
|
||||
update s set minimum_value=200, maximum_value=100;
|
||||
FLUSH TABLES;
|
||||
CHECK TABLE s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Error Sequence 'test.s' has out of range value for options
|
||||
test.s check error Corrupt
|
||||
DROP SEQUENCE s;
|
||||
create sequence s minvalue 13 maxvalue 15 increment by 4;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
select nextval(s);
|
||||
nextval(s)
|
||||
13
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning Sequence 'test.s' has run out
|
||||
test.s check status OK
|
||||
alter sequence s cycle;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
alter sequence s nocycle;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning Sequence 'test.s' has run out
|
||||
test.s check status OK
|
||||
alter sequence s increment by 1;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning Sequence 'test.s' has run out
|
||||
test.s check status OK
|
||||
alter sequence s increment by 4;
|
||||
select nextval(s);
|
||||
ERROR HY000: Sequence 'test.s' has run out
|
||||
alter sequence s cycle;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
alter sequence s maxvalue 23 nocycle;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
alter sequence s maxvalue 15;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning Sequence 'test.s' has run out
|
||||
test.s check status OK
|
||||
drop sequence s;
|
||||
create sequence s minvalue 13 maxvalue 20 increment by 1;
|
||||
select nextval(s);
|
||||
nextval(s)
|
||||
13
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check status OK
|
||||
select nextval(s);
|
||||
nextval(s)
|
||||
14
|
||||
drop sequence s;
|
||||
create sequence s minvalue 13 maxvalue 20 increment by 1;
|
||||
select nextval(s);
|
||||
nextval(s)
|
||||
13
|
||||
alter table s sequence=0;
|
||||
alter table s sequence=1;
|
||||
check table s;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s check Warning Sequence 'test.s' has run out
|
||||
test.s check status OK
|
||||
select nextval(s);
|
||||
ERROR HY000: Sequence 'test.s' has run out
|
||||
drop sequence s;
|
||||
create sequence s;
|
||||
update s set minimum_value=500, maximum_value=200;
|
||||
ERROR HY000: Storage engine SEQUENCE of the table `test`.`s` doesn't have this option
|
||||
drop sequence s;
|
139
mysql-test/suite/sql_sequence/check.test
Normal file
139
mysql-test/suite/sql_sequence/check.test
Normal file
@ -0,0 +1,139 @@
|
||||
--source include/have_innodb.inc
|
||||
|
||||
# Check failure of underlying engine
|
||||
create sequence s;
|
||||
let $datadir= `select @@datadir`;
|
||||
remove_file $datadir/test/s.MYD;
|
||||
write_file $datadir/test/s.MYD;
|
||||
foo
|
||||
EOF
|
||||
call mtr.add_suppression("ha_myisam");
|
||||
call mtr.add_suppression("Checking table");
|
||||
check table s;
|
||||
drop table s;
|
||||
|
||||
# Insert a row into a sequence table updates that row
|
||||
create sequence s;
|
||||
insert into s values (3,1,9223372036854775806,1,1,1000,0,0);
|
||||
select * from s;
|
||||
# ok
|
||||
check table s;
|
||||
--disable_ps2_protocol
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
drop sequence s;
|
||||
|
||||
# More than one row
|
||||
let $datadir=`select @@datadir`;
|
||||
CREATE SEQUENCE s;
|
||||
copy_file $datadir/test/s.frm $datadir/test/s1.frm;
|
||||
ALTER TABLE s sequence=0;
|
||||
insert into s values (3,1,9223372036854775806,1,1,1000,0,0);
|
||||
FLUSH TABLES;
|
||||
remove_file $datadir/test/s.frm;
|
||||
move_file $datadir/test/s1.frm $datadir/test/s.frm;
|
||||
CHECK TABLE s;
|
||||
DROP SEQUENCE s;
|
||||
|
||||
# Fewer than one row
|
||||
let $datadir=`select @@datadir`;
|
||||
CREATE SEQUENCE s;
|
||||
copy_file $datadir/test/s.frm $datadir/test/s1.frm;
|
||||
ALTER TABLE s sequence=0;
|
||||
delete from s;
|
||||
FLUSH TABLES;
|
||||
remove_file $datadir/test/s.frm;
|
||||
move_file $datadir/test/s1.frm $datadir/test/s.frm;
|
||||
CHECK TABLE s;
|
||||
DROP SEQUENCE s;
|
||||
|
||||
# Wrong metadata (minvalue > maxvalue)
|
||||
let $datadir=`select @@datadir`;
|
||||
CREATE SEQUENCE s;
|
||||
copy_file $datadir/test/s.frm $datadir/test/s1.frm;
|
||||
ALTER TABLE s sequence=0;
|
||||
update s set minimum_value=200, maximum_value=100;
|
||||
FLUSH TABLES;
|
||||
remove_file $datadir/test/s.frm;
|
||||
move_file $datadir/test/s1.frm $datadir/test/s.frm;
|
||||
CHECK TABLE s;
|
||||
DROP SEQUENCE s;
|
||||
|
||||
# Sequence run out tests.
|
||||
#
|
||||
# General principle: CHECK TABLE of a sequence table returns OK with a
|
||||
# warning of ER_SEQUENCE_RUN_OUT if a SELECT NEXTVAL of the sequence
|
||||
# in place of the CHECK TABLE statement would report
|
||||
# ER_SEQUENCE_RUN_OUT.
|
||||
create sequence s minvalue 13 maxvalue 15 increment by 4;
|
||||
check table s;
|
||||
--disable_ps2_protocol
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
check table s;
|
||||
alter sequence s cycle;
|
||||
check table s;
|
||||
alter sequence s nocycle;
|
||||
check table s;
|
||||
# Still get run out because next_free_value has not changed. same
|
||||
# would happen with a SELECT NEXTVAL(s) statement without the
|
||||
# preceding check table statement.
|
||||
alter sequence s increment by 1;
|
||||
check table s;
|
||||
alter sequence s increment by 4;
|
||||
# If all_values_used is true, and then we make sequence cycle, check
|
||||
# table will be ok without warning, as expected. this is because the
|
||||
# ALTER SEQUENCE statement causes all_values_used to be reset.
|
||||
--disable_ps2_protocol
|
||||
--error ER_SEQUENCE_RUN_OUT
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
alter sequence s cycle;
|
||||
check table s;
|
||||
alter sequence s maxvalue 23 nocycle;
|
||||
check table s;
|
||||
alter sequence s maxvalue 15;
|
||||
check table s;
|
||||
drop sequence s;
|
||||
|
||||
# CHECK TABLE calls sequence_definition::check_and_adjust() with
|
||||
# adjust_next=false, so that there will be no flushing of
|
||||
# next_free_value in this call, hence no running out
|
||||
create sequence s minvalue 13 maxvalue 20 increment by 1;
|
||||
--disable_ps2_protocol
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
check table s;
|
||||
--disable_ps2_protocol
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
drop sequence s;
|
||||
|
||||
# Without the CHECK TABLE statement below, the ALTER TABLE ...
|
||||
# SEQUENCE=1 statement would still cause flushing in subsequent SELECT
|
||||
# NEXTVAL statement (initialized == SQUENCE:SQL_UNITIALIZED =>
|
||||
# read_initial_value() => => sequence_defitinion::adjust_values()),
|
||||
# resulting in sequence running out.
|
||||
|
||||
# Same effect takes place with ALTER SEQUENCE, though different cause:
|
||||
# in ALTER SEQUENCE, sequence_defitinion::adjust_values() is called in
|
||||
# sequence_definition::check_and_adjust() which is called in
|
||||
# Sql_cmd_alter_sequence::execute()
|
||||
create sequence s minvalue 13 maxvalue 20 increment by 1;
|
||||
--disable_ps2_protocol
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
alter table s sequence=0;
|
||||
alter table s sequence=1;
|
||||
check table s;
|
||||
--disable_ps2_protocol
|
||||
--error ER_SEQUENCE_RUN_OUT
|
||||
select nextval(s);
|
||||
--enable_ps2_protocol
|
||||
drop sequence s;
|
||||
|
||||
# UPDATE is banned by the storage engine - no need to check.
|
||||
create sequence s;
|
||||
--error ER_ILLEGAL_HA
|
||||
update s set minimum_value=500, maximum_value=200;
|
||||
drop sequence s;
|
@ -4,14 +4,14 @@
|
||||
create sequence s1 engine=innodb;
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
select next value for s1;
|
||||
next value for s1
|
||||
1
|
||||
flush tables;
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
select next value for s1;
|
||||
next value for s1
|
||||
1001
|
||||
@ -32,7 +32,7 @@ Table Op Msg_type Msg_text
|
||||
test.s1 repair status OK
|
||||
check table s1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.s1 check note The storage engine for the table doesn't support check
|
||||
test.s1 check status OK
|
||||
select next value for s1;
|
||||
next value for s1
|
||||
1001
|
||||
|
@ -420,6 +420,50 @@ void ha_sequence::print_error(int error, myf errflag)
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
int ha_sequence::check(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
{
|
||||
DBUG_ENTER("ha_sequence::check");
|
||||
/* Check the underlying engine */
|
||||
if (int ret= file->check(thd, check_opt))
|
||||
DBUG_RETURN(ret);
|
||||
/* Check number of rows */
|
||||
if ((file->table_flags() & HA_STATS_RECORDS_IS_EXACT))
|
||||
{
|
||||
if (file->stats.records > 1)
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_SEQUENCE_TABLE_HAS_TOO_MANY_ROWS,
|
||||
ER_THD(thd, ER_SEQUENCE_TABLE_HAS_TOO_MANY_ROWS));
|
||||
else if (file->stats.records == 0)
|
||||
{
|
||||
my_error(ER_SEQUENCE_TABLE_HAS_TOO_FEW_ROWS, MYF(0));
|
||||
DBUG_RETURN(HA_ADMIN_CORRUPT);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Initialise the sequence from the table if needed.
|
||||
*/
|
||||
if (sequence->initialized == SEQUENCE::SEQ_UNINTIALIZED)
|
||||
{
|
||||
if (sequence->read_stored_values(table))
|
||||
DBUG_RETURN(HA_ADMIN_FAILED);
|
||||
else
|
||||
sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
|
||||
}
|
||||
DBUG_ASSERT(sequence->initialized == SEQUENCE::SEQ_READY_TO_USE);
|
||||
/* Check and adjust sequence state */
|
||||
if (sequence->check_and_adjust(thd, false, /*adjust_next=*/false))
|
||||
{
|
||||
print_error(HA_ERR_SEQUENCE_INVALID_DATA, MYF(0));
|
||||
DBUG_RETURN(HA_ADMIN_CORRUPT);
|
||||
}
|
||||
/* Check value not exhausted */
|
||||
if (sequence->has_run_out())
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_SEQUENCE_RUN_OUT, ER_THD(thd, ER_SEQUENCE_RUN_OUT),
|
||||
table->s->db.str, table->s->table_name.str);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
Sequence plugin interface
|
||||
*****************************************************************************/
|
||||
|
@ -159,6 +159,7 @@ public:
|
||||
{ return file->is_crashed(); }
|
||||
void column_bitmaps_signal() override
|
||||
{ return file->column_bitmaps_signal(); }
|
||||
int check(THD* thd, HA_CHECK_OPT* check_opt) override;
|
||||
|
||||
/* New methods */
|
||||
void register_original_handler(handler *file_arg)
|
||||
|
@ -12308,3 +12308,7 @@ ER_LATITUDE_OUT_OF_RANGE
|
||||
eng "Latitude %f is out of range in function %s. It must be within [-90.000000, 90.000000]."
|
||||
ER_GIS_DIFFERENT_SRIDS_AGGREGATION
|
||||
eng "Arguments to function %s contains geometries with different SRIDs: %d and %d. All geometries must have the same SRID."
|
||||
ER_SEQUENCE_TABLE_HAS_TOO_FEW_ROWS
|
||||
eng "Fewer than one row in the table"
|
||||
ER_SEQUENCE_TABLE_HAS_TOO_MANY_ROWS
|
||||
eng "More than one row in the table"
|
||||
|
@ -161,16 +161,19 @@ longlong sequence_definition::truncate_value(const Longlong_hybrid& original)
|
||||
|
||||
/**
|
||||
Check whether sequence values are valid.
|
||||
|
||||
|
||||
Sets default values for fields that are not used, according to Oracle spec.
|
||||
|
||||
@param in thd The connection
|
||||
@param in set_reserved_until Whether to set reserved_until to start
|
||||
|
||||
@param in adjust_next Whether to call flush
|
||||
next_free_value. Default to true
|
||||
|
||||
@retval false valid
|
||||
true invalid
|
||||
*/
|
||||
bool sequence_definition::check_and_adjust(THD *thd, bool set_reserved_until)
|
||||
bool sequence_definition::check_and_adjust(THD *thd, bool set_reserved_until,
|
||||
bool adjust_next)
|
||||
{
|
||||
DBUG_ENTER("sequence_definition::check_and_adjust");
|
||||
|
||||
@ -241,7 +244,8 @@ bool sequence_definition::check_and_adjust(THD *thd, bool set_reserved_until)
|
||||
if (set_reserved_until)
|
||||
reserved_until= start;
|
||||
|
||||
adjust_values(reserved_until);
|
||||
if (adjust_next)
|
||||
adjust_values(reserved_until);
|
||||
|
||||
/* To ensure that cache * real_increment will never overflow */
|
||||
const longlong max_increment= (real_increment ?
|
||||
@ -758,7 +762,11 @@ int SEQUENCE::read_stored_values(TABLE *table)
|
||||
|
||||
|
||||
/*
|
||||
Adjust values after reading a the stored state
|
||||
Adjust next_free_value after reading a the stored state
|
||||
|
||||
Also assign auto_increment_increment to real_increment if increment
|
||||
is 0, though this assignment may have already happened (e.g. in
|
||||
check_and_adjust())
|
||||
*/
|
||||
|
||||
void sequence_definition::adjust_values(longlong next_value)
|
||||
|
@ -95,7 +95,8 @@ public:
|
||||
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);
|
||||
bool check_and_adjust(THD *thd, bool set_reserved_until,
|
||||
bool adjust_next= true);
|
||||
void store_fields(TABLE *table);
|
||||
void read_fields(TABLE *table);
|
||||
int write_initial_sequence(TABLE *table);
|
||||
@ -153,6 +154,13 @@ 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);
|
||||
bool has_run_out()
|
||||
{
|
||||
return all_values_used ||
|
||||
(!cycle &&
|
||||
!within_bound(next_free_value, max_value + 1, min_value - 1,
|
||||
real_increment > 0));
|
||||
}
|
||||
|
||||
bool all_values_used;
|
||||
seq_init initialized;
|
||||
|
Loading…
x
Reference in New Issue
Block a user