MDEV-12930 Testing SEQUENCE object

Fixed the following things from the above MDEV:
- Ensure the user has INSERT privilege when generating new sequence values
  with NEXT VALUE FOR or SETVAL()
- Fixed bug in InnoDB when generating several sequence values in one statement
- Ensure that read_set is up to date before calling ha_sequence::ha_write_row()
  - This is only a potential bug with storage engines that trusts the column maps completely
This commit is contained in:
Monty 2017-05-30 21:31:30 +03:00
parent d5d8fa6e04
commit 959891662d
10 changed files with 181 additions and 7 deletions

View File

@ -6,9 +6,11 @@ grant all on s_db.* to normal_1@'%' identified by 'pass';
grant all on test.* to normal_2@'%' identified by 'pass'; grant all on test.* to normal_2@'%' identified by 'pass';
grant all on s_db.* to normal_3@'%' identified by 'pass'; grant all on s_db.* to normal_3@'%' identified by 'pass';
grant all on test.* to normal_4@'%' identified by 'pass'; grant all on test.* to normal_4@'%' identified by 'pass';
grant select on test.* to normal_5@'%' identified by 'pass';
connection slave; connection slave;
connect m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT; connect m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT;
connect m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT; connect m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT;
connect m_normal_3, 127.0.0.1, normal_5, pass, test, $MASTER_MYPORT;
connect s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT; connect s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT;
connect s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT; connect s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT;
connection slave; connection slave;
@ -171,7 +173,7 @@ create sequence s_db.s2;
drop sequence s_db.s2; drop sequence s_db.s2;
connection m_normal_2; connection m_normal_2;
select next value for s_db.s1; select next value for s_db.s1;
ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' ERROR 42000: INSERT command denied to user 'normal_2'@'localhost' for table 's1'
create sequence s_db.s2; create sequence s_db.s2;
ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2'
connection m_normal_1; connection m_normal_1;
@ -771,6 +773,51 @@ next value for s1
drop function f1; drop function f1;
drop table t1; drop table t1;
drop sequence s1; drop sequence s1;
##############
Test GRANT
##############
connection m_normal_2;
create table t1 (a int);
create sequence s1;
select next value for s1;
next value for s1
1
insert into t1 values (1);
connection m_normal_3;
select * from t1;
a
1
select * from s1;
next_value min_value max_value start increment cache cycle round
1001 1 9223372036854775806 1 1 1000 0 0
select previous value for s1;
previous value for s1
NULL
insert into t1 values (2);
ERROR 42000: INSERT command denied to user 'normal_5'@'localhost' for table 't1'
select next value for s1;
ERROR 42000: INSERT command denied to user 'normal_5'@'localhost' for table 's1'
do setval(s1,1000,0);
ERROR 42000: INSERT command denied to user 'normal_5'@'localhost' for table 's1'
connection master;
grant insert on test.* to normal_5@'%' identified by 'pass';
disconnect m_normal_3;
connect m_normal_3, 127.0.0.1, normal_5, pass, test, $MASTER_MYPORT;
insert into t1 values (2);
select t1.*, (next value for s1) from t1;
a (next value for s1)
1 2
2 3
do setval(s1,10000,0);
select * from s1;
next_value min_value max_value start increment cache cycle round
10000 1 9223372036854775806 1 1 1000 0 0
connection m_normal_2;
drop table t1;
drop sequence s1;
#
# Cleanup
#
connection master; connection master;
use s_db; use s_db;
drop database s_db; drop database s_db;
@ -778,4 +825,5 @@ drop user normal_1@'%';
drop user normal_2@'%'; drop user normal_2@'%';
drop user normal_3@'%'; drop user normal_3@'%';
drop user normal_4@'%'; drop user normal_4@'%';
drop user normal_5@'%';
include/rpl_end.inc include/rpl_end.inc

View File

@ -12,11 +12,13 @@ grant all on s_db.* to normal_1@'%' identified by 'pass';
grant all on test.* to normal_2@'%' identified by 'pass'; grant all on test.* to normal_2@'%' identified by 'pass';
grant all on s_db.* to normal_3@'%' identified by 'pass'; grant all on s_db.* to normal_3@'%' identified by 'pass';
grant all on test.* to normal_4@'%' identified by 'pass'; grant all on test.* to normal_4@'%' identified by 'pass';
grant select on test.* to normal_5@'%' identified by 'pass';
--sync_slave_with_master --sync_slave_with_master
connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT); connect(m_normal_1, 127.0.0.1, normal_1, pass, s_db, $MASTER_MYPORT);
connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT); connect(m_normal_2, 127.0.0.1, normal_2, pass, test, $MASTER_MYPORT);
connect(m_normal_3, 127.0.0.1, normal_5, pass, test, $MASTER_MYPORT);
connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT); connect(s_normal_3, 127.0.0.1, normal_3, pass, s_db, $SLAVE_MYPORT);
connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT); connect(s_normal_4, 127.0.0.1, normal_4, pass, test, $SLAVE_MYPORT);
@ -655,6 +657,41 @@ drop function f1;
drop table t1; drop table t1;
drop sequence s1; drop sequence s1;
--echo ##############
--echo Test GRANT
--echo ##############
connection m_normal_2;
create table t1 (a int);
create sequence s1;
select next value for s1;
insert into t1 values (1);
connection m_normal_3;
select * from t1;
select * from s1;
select previous value for s1;
--error ER_TABLEACCESS_DENIED_ERROR
insert into t1 values (2);
--error ER_TABLEACCESS_DENIED_ERROR
select next value for s1;
--error ER_TABLEACCESS_DENIED_ERROR
do setval(s1,1000,0);
connection master;
grant insert on test.* to normal_5@'%' identified by 'pass';
disconnect m_normal_3;
connect(m_normal_3, 127.0.0.1, normal_5, pass, test, $MASTER_MYPORT);
insert into t1 values (2);
select t1.*, (next value for s1) from t1;
do setval(s1,10000,0);
select * from s1;
connection m_normal_2;
drop table t1;
drop sequence s1;
--echo #
--echo # Cleanup
--echo #
connection master; connection master;
use s_db; use s_db;
drop database s_db; drop database s_db;
@ -662,5 +699,6 @@ drop user normal_1@'%';
drop user normal_2@'%'; drop user normal_2@'%';
drop user normal_3@'%'; drop user normal_3@'%';
drop user normal_4@'%'; drop user normal_4@'%';
drop user normal_5@'%';
--source include/rpl_end.inc --source include/rpl_end.inc

View File

@ -113,3 +113,38 @@ LOCK TABLES s1 WRITE;
insert into s1 values (1,1,9223372036854775806, 1, 1, 1000, 0, 0); insert into s1 values (1,1,9223372036854775806, 1, 1, 1000, 0, 0);
UNLOCK TABLES; UNLOCK TABLES;
drop table s1; drop table s1;
#
# Many sequence calls with innodb
#
create sequence s1 cache=1000 engine=innodb;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
count(nextval(s1))
2000
commit;
select * from s1;
next_value min_value max_value start increment cache cycle round
2001 1 9223372036854775806 1 1 1000 0 0
drop sequence s1;
create sequence s1 cache=1000 engine=innodb;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
count(nextval(s1))
2000
connect addconroot, localhost, root,,;
connection addconroot;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
count(nextval(s1))
2000
select * from s1;
next_value min_value max_value start increment cache cycle round
4001 1 9223372036854775806 1 1 1000 0 0
commit;
disconnect addconroot;
connection default;
select * from s1;
next_value min_value max_value start increment cache cycle round
4001 1 9223372036854775806 1 1 1000 0 0
commit;
drop sequence s1;

View File

@ -90,3 +90,30 @@ LOCK TABLES s1 WRITE;
insert into s1 values (1,1,9223372036854775806, 1, 1, 1000, 0, 0); insert into s1 values (1,1,9223372036854775806, 1, 1, 1000, 0, 0);
UNLOCK TABLES; UNLOCK TABLES;
drop table s1; drop table s1;
--echo #
--echo # Many sequence calls with innodb
--echo #
create sequence s1 cache=1000 engine=innodb;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
commit;
select * from s1;
drop sequence s1;
create sequence s1 cache=1000 engine=innodb;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
connect (addconroot, localhost, root,,);
connection addconroot;
start transaction;
select count(nextval(s1)) from seq_1_to_2000;
select * from s1;
commit;
disconnect addconroot;
connection default;
select * from s1;
commit;
drop sequence s1;

View File

@ -286,7 +286,7 @@ create sequence s_db.s2;
drop sequence s_db.s2; drop sequence s_db.s2;
connection m_normal_2; connection m_normal_2;
select NEXT VALUE for s_db.s1; select NEXT VALUE for s_db.s1;
ERROR 42000: SELECT command denied to user 'normal_2'@'localhost' for table 's1' ERROR 42000: INSERT command denied to user 'normal_2'@'localhost' for table 's1'
create sequence s_db.s2; create sequence s_db.s2;
ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2' ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table 's2'
connection m_normal_1; connection m_normal_1;

View File

@ -152,6 +152,8 @@ public:
{ return file->check_and_repair(thd); } { return file->check_and_repair(thd); }
bool is_crashed() const bool is_crashed() const
{ return file->is_crashed(); } { return file->is_crashed(); }
void column_bitmaps_signal()
{ return file->column_bitmaps_signal(); }
/* New methods */ /* New methods */
void register_original_handler(handler *file_arg) void register_original_handler(handler *file_arg)

View File

@ -7524,7 +7524,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx; Security_context *sctx= thd->security_ctx;
uint i; uint i;
ulong orig_want_access= want_access; ulong original_want_access= want_access;
bool locked= 0; bool locked= 0;
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL; GRANT_TABLE *grant_table_role= NULL;
@ -7558,6 +7558,16 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
TABLE_LIST *const t_ref= TABLE_LIST *const t_ref=
tl->correspondent_table ? tl->correspondent_table : tl; tl->correspondent_table ? tl->correspondent_table : tl;
sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
ulong orig_want_access= original_want_access;
if (t_ref->sequence)
{
/* We want to have either SELECT or INSERT rights to sequences depending
on how they are accessed
*/
orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
INSERT_ACL : SELECT_ACL);
}
const ACL_internal_table_access *access= const ACL_internal_table_access *access=
get_cached_table_access(&t_ref->grant.m_internal, get_cached_table_access(&t_ref->grant.m_internal,

View File

@ -7045,6 +7045,15 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
thd->security_ctx= sctx; thd->security_ctx= sctx;
if (table_ref->sequence)
{
/* We want to have either SELECT or INSERT rights to sequences depending
on how they are accessed
*/
want_access= ((table_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
INSERT_ACL : SELECT_ACL);
}
if (check_access(thd, want_access, table_ref->get_db_name(), if (check_access(thd, want_access, table_ref->get_db_name(),
&table_ref->grant.privilege, &table_ref->grant.privilege,
&table_ref->grant.m_internal, &table_ref->grant.m_internal,

View File

@ -576,7 +576,7 @@ int sequence_definition::write_initial_sequence(TABLE *table)
int sequence_definition::write(TABLE *table, bool all_fields) int sequence_definition::write(TABLE *table, bool all_fields)
{ {
int error; int error;
MY_BITMAP *save_rpl_write_set, *save_write_set; MY_BITMAP *save_rpl_write_set, *save_write_set, *save_read_set;
DBUG_ASSERT(((ha_sequence*) table->file)->is_locked()); DBUG_ASSERT(((ha_sequence*) table->file)->is_locked());
save_rpl_write_set= table->rpl_write_set; save_rpl_write_set= table->rpl_write_set;
@ -593,12 +593,16 @@ int sequence_definition::write(TABLE *table, bool all_fields)
/* Update table */ /* Update table */
save_write_set= table->write_set; save_write_set= table->write_set;
table->write_set= &table->s->all_set; save_read_set= table->read_set;
table->read_set= table->write_set= &table->s->all_set;
table->file->column_bitmaps_signal();
store_fields(table); store_fields(table);
if ((error= table->file->ha_write_row(table->record[0]))) if ((error= table->file->ha_write_row(table->record[0])))
table->file->print_error(error, MYF(0)); table->file->print_error(error, MYF(0));
table->rpl_write_set= save_rpl_write_set; table->rpl_write_set= save_rpl_write_set;
table->read_set= save_read_set;
table->write_set= save_write_set; table->write_set= save_write_set;
table->file->column_bitmaps_signal();
return error; return error;
} }

View File

@ -8129,10 +8129,11 @@ ha_innobase::build_template(
ibool fetch_primary_key_cols = FALSE; ibool fetch_primary_key_cols = FALSE;
ulint i; ulint i;
if (m_prebuilt->select_lock_type == LOCK_X) { if (m_prebuilt->select_lock_type == LOCK_X || m_prebuilt->table->no_rollback()) {
/* We always retrieve the whole clustered index record if we /* We always retrieve the whole clustered index record if we
use exclusive row level locks, for example, if the read is use exclusive row level locks, for example, if the read is
done in an UPDATE statement. */ done in an UPDATE statement or if we are using a no rollback
table */
whole_row = true; whole_row = true;
} else if (!whole_row) { } else if (!whole_row) {