Question

I have a table to hold user information and is used as part of the login/registration process.
Part of the requirement is to allow the user to login using either their username or email address.
A snippet of the table is below and DB is PostgreSQL 12.4

CREATE TABLE account_users
(
    id            int          default nextval('postgres.account_users_id_seq'),
    username      varchar(100) not null unique,
    email         varchar(100) not null unique,
);

However this has raised a concern that the username field could be set to an email address of another user, which could cause a potential issue with users being able to login to their account as the SQL to get the users password from the database is:

SELECT password FROM account_users WHERE username = $1 or email = $1 LIMIT 1;

What I'm looking for is a way to ensure that the value in username and email are not the same for any row

For example:

username          | email
----------------------------------------------
user1             | user1@example.com - OK
user2             | user2@example.com - OK
user3@example.com | user3@example.com - Not OK
user1@example.com | user4@example.com - Not OK

Is this possible to do at the DB level or would it be something for the application to handle?

There is going to be validation done at the application level, client-side and server-side, however would a be nice to have if the DB could also catch this as well.

Was it helpful?

Solution

To ensure that the e-mail field has priority, use ORDER BY:

SELECT password
FROM account_users
WHERE username = $1
   OR email = $1
ORDER BY email = $1 DESC
LIMIT 1;

Alternatively, use UNION ALL, which might be easier to understand:

SELECT password FROM account_users WHERE email = $1
UNION ALL
SELECT password FROM account_users WHERE username = $1
LIMIT 1;

Is there any reason to allow @ in the user name?

username      text not null unique check (username !~ '@'),
email         text not null unique check (email    ~  '@'),

(You might allow the user name to be an e-mail address if it is the user's own.)


A CHECK constraint is not allowed to look at other rows. So you would have to use triggers (on any INSERT or UPDATE, check that no other row with the same value exists).

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top