Pretend I have a users table where users are members of a specific tenant, and their e-mails are uniquely indexed to their tenant, like this:
User
id | tenant_id | email
1 1 person1@example.com
2 1 person2@example.com
This user is allowed because despite a duplicate e-mail, they are at a different tenant:
3 2 person1@example.com
This user is prevented because the e-mail is a duplicate at the same tenant:
4 2 person1@example.com <--- will throw an error
We have this much covered with a unique index -- that part is easy.
Now pretend that I want to be able to add a global user that can access all tenants, but only if the e-mail doesn't already exist in the table at all. Additionally, once the record exists, nobody else -- whether tenanted or not -- will be able to use the same e-mail.
For clarity, the global users could simply have a null tenant ID but we would likely also add a global
boolean.
Is there a way to write constraints for this logic? You can't simply make e-mails globally uniquely constrained because they won't be able to be repeated across tenants, and if you index with a null tenant ID, postgres will allow an untenanted user if there are tenanted users with the same e-mail.
I've looked at exclusion constraints and checks but couldn't figure out how to combine them (uniquely constrain e-mail globally if tenant_id is null, and check for records with null tenant ID and matching e-mail when inserting any record).
Please don't ask why I'm doing things this way -- my table isn't actually users and we've considered and dismissed other architectures :)
Thanks in advance!
According to PostgreSQL Documentation you can create unique partial index which will be effectively the same as creating unique partial constraint on your table:
CREATE UNIQUE INDEX some_index ON some_table (col_a) WHERE (col_b is null);
Using this technique you can create 2 separate unique indexes for admin and non-admin users.
You can use a UNIQUE constraint for both fields:
create table myUsers
(
id int not null,
tenant int not null,
email varchar(200) not null,
UNIQUE(email, tenant)
);
insert into myUsers values
(1, 1, 'person1@example.com'),
(2, 1, 'person2@example.com');
insert into myUsers values
(3, 2, 'person1@example.com');
Next insert will throw an error:
insert into myUsers values
(4, 2, 'person1@example.com');
Error(s), warning(s):
23505: duplicate key value violates unique constraint "myusers_email_tenant_key"
Check it here: http://rextester.com/AJZVI34616
For the second part of the question:
Now pretend that I want to be able to add a global user that can access all tenants, but only if the e-mail doesn't already exist in the table at all.
One solution could be to reserve a tenant for admin users:
tenant = 0 <-- admin users
But the UNIQUE constraint allow duplicated emails, I recommend you to add a rol
field to this table, or have another table of admin users for this purpose.
In my case, we use two tables, and both have a rol
field.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.