Let's assume I have a table of items:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Now I want to introduce the concept of "permissions" for each item (note, I'm not talking about database access permissions here, but business logic permissions for that item). Each item has default permissions and also per-user permissions that may override default permissions.
I tried to think of ways to implement this and came up with the following solutions:
1) The Boolean Solution
Use a boolean column for each permission:
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,
...
);
Advantages: Each permission is named.
Disadvantages: There are dozens of permissions which increases the number of columns significantly and you have to define them twice (once in each table).
2) The Integer Solution
Use an integer and treat it as a bitfield (i.e. bit 0 is for can_change_description, bit 1 is for can_change_price, and so on, and use bitwise operations to set or read permissions).
CREATE DOMAIN permissions AS integer;
Advantages: very fast.
Disadvantages: You have to keep track of which bit stands for which permission in both the database and the front-end interface.
3) The Bitfield Solution
Same as 2), but use bit(n). Most likely the same advantages and disadvantages, maybe slightly slower.
4) The Enum Solution
Use an enum type for the permissions:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
and then create an extra table for default permissions:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
and change the per-user definition table to:
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)
);
Advantages: Easy to name individual permissions (you don't have to handle bit positions).
Disadvantages: Even when just retrieving the default permissions, it requires accessing two additional tables: first, the default permissions table, and second, the system catalog storing the enum values.
Especially because the default permissions must be retrieved for every single page view of that item, the performance impact of the last alternative might be significant.
Can you think of other alternatives?
Which approach should be taken?
Please note: this question has been reposted on DBA.
bit(n)and good documentation. With that many permissions bits using abigintis unsuitable too, as you only have 64 bits to play with. FWIW I agree an array of enums is ugly, but what you're trying to do is an optimisation that's sacrificing normalisation for performance anyway, otherwise you'd just have a child-table. So you're already disregarding the "clean" way (sensibly, IMO).primary key(object_type, bit_position). You could use an enum instead, but there's not much benefit and enums have a floating point internal value to allow nondestructive intermediate value insertions, so they're not really ideal. Then have a code generator that creates a class from your in-database permissions table. Or creates SQL for the permissions table from the code. Or creates both from an intermediate representation in revision control. Whatever.