Vamos supor que eu tenha uma tabela de itens:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Agora, quero introduzir o conceito de "permissões" para cada item (observe, aqui não estou falando de permissões de acesso ao banco de dados, mas de permissões de lógica de negócios para esse item). Cada item tem permissões padrão e também permissões por usuário que podem substituir as permissões padrão.
Tentei pensar em várias maneiras de implementar isso e vi as seguintes soluções, mas não tenho certeza sobre qual é o melhor e por que:
1) A solução booleana
Use uma coluna booleana para cada permissão:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Vantagens : Cada permissão é nomeada.
Desvantagens : Existem dezenas de permissões que aumentam significativamente o número de colunas e você deve defini-las duas vezes (uma vez em cada tabela).
2) A Solução Inteira
Use um número inteiro e trate-o como um campo de bits (ou seja, o bit 0 é para can_change_description
, o bit 1 é para can_change_price
e assim por diante, e use operações bit a bit para definir ou ler permissões).
CREATE DOMAIN permissions AS integer;
Vantagens : muito rápido.
Desvantagens : Você deve acompanhar qual bit representa a permissão no banco de dados e na interface front-end.
3) A solução Bitfield
O mesmo que 2), mas use bit(n)
. Provavelmente as mesmas vantagens e desvantagens, talvez um pouco mais lentas.
4) A solução Enum
Use um tipo de enum para as permissões:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
e crie uma tabela extra para permissões padrão:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
e altere a tabela de definição por usuário para:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Vantagens : Fácil de nomear permissões individuais (você não precisa lidar com posições de bits).
Desvantagens : Mesmo ao recuperar as permissões padrão, é necessário acessar duas tabelas adicionais: primeiro, a tabela de permissões padrão e, segundo, o catálogo do sistema que armazena os valores de enumeração.
Especialmente porque as permissões padrão devem ser recuperadas para cada visualização de página única desse item , o impacto no desempenho da última alternativa pode ser significativo.
5) A solução de matriz Enum
Igual a 4), mas use uma matriz para armazenar todas as permissões (padrão):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Vantagens : Fácil de nomear permissões individuais (você não precisa lidar com posições de bits).
Desvantagens : quebra a 1ª forma normal e é um pouco feio. Ocupa um número considerável de bytes em uma linha se o número de permissões for grande (cerca de 50).
Você consegue pensar em outras alternativas?
Qual abordagem deve ser adotada e por quê?
Observe: esta é uma versão modificada de uma pergunta postada anteriormente no Stackoverflow .
bigint
campos (cada um bom para 64 bits) ou uma sequência de bits. Escrevi algumas respostas relacionadas ao SO que podem ser úteis.