had a quick stab at it so suggest you double check the results !
full script here : http://pastie.org/1213230
-- TABLES
drop table if exists roles;
create table roles
(
role_id tinyint unsigned not null primary key,
name varchar(255) unique not null,
parent_role_id tinyint unsigned,
key (parent_role_id)
)
engine=innodb;
drop table if exists actions;
create table actions
(
action_id smallint unsigned not null auto_increment primary key,
name varchar(255) unique not null
)
engine=innodb;
drop table if exists role_actions;
create table role_actions
(
role_id tinyint unsigned not null,
action_id smallint unsigned not null,
primary key (role_id, action_id)
)
engine=innodb;
-- STORED PROCEDURES
drop procedure if exists list_role_actions;
delimiter #
create procedure list_role_actions
(
in p_role_id tinyint unsigned
)
proc_main:begin
declare done tinyint unsigned default 0;
declare dpth smallint unsigned default 0;
drop temporary table if exists hier;
drop temporary table if exists tmp;
create temporary table hier(
parent_role_id tinyint unsigned,
role_id tinyint unsigned,
depth smallint unsigned default 0
)engine = memory;
insert into hier select parent_role_id, role_id, dpth from roles where role_id = p_role_id;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
create temporary table tmp engine=memory select * from hier;
while done <> 1 do
if exists( select 1 from roles r inner join hier on r.parent_role_id = hier.role_id and hier.depth = dpth) then
insert into hier
select r.parent_role_id, r.role_id, dpth + 1 from roles r
inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth;
set dpth = dpth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = dpth;
else
set done = 1;
end if;
end while;
/*
select
r.*,
p.name as parent_role_name,
hier.depth
from
hier
inner join roles r on hier.role_id = r.role_id
inner join roles p on hier.parent_role_id = p.role_id
order by
hier.depth, hier.role_id;
*/
select
ra.*,
r.name as role_name,
a.name as action_name
from
role_actions ra
inner join hier h on h.role_id = ra.role_id
inner join actions a on ra.action_id = a.action_id
inner join roles r on ra.role_id = r.role_id
order by
ra.role_id desc;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end proc_main #
delimiter ;
-- TEST DATA
insert into roles values
(0,'guest',1),(1,'member',2),(2,'moderator',3),(3,'admin',99),(99,'super admin',null);
insert into actions (name) values
('view post'), ('create post'), ('edit post'), ('move forum'), ('create forum');
insert into role_actions values
-- guest
(0,1),
-- member
(1,2),
-- moderator
(2,3),
-- admin
(3,4),
-- super admin
(99,5);
-- TESTING
call list_role_actions(0);
call list_role_actions(1);
call list_role_actions(2);
call list_role_actions(3);
call list_role_actions(99);