Reputation: 126329
Is it possible to do something like the following with SQL, not PL/pgSQL (note if it's only possible with PL/pgSQL, then how)?
IF password = 'swordfish' THEN
SELECT a, b, c FROM users;
ELSE
SELECT -1; -- unauthorized error code
END IF;
Ideally, could I wrap the above in a function with TRUE
being an argument?
Rather, is it possible to set the command status string to -1?
I'm asking this because I want the query to return an error code, like -1, if someone tries to get a list of all the users with the wrong password. This is for a web app with user accounts that each have a password. So, this is not something I want to manage with database roles/permissions.
Upvotes: 4
Views: 5051
Reputation: 126329
Algorithm
1
into a
(authorized) if we find a user_id_1
-session_id
match.0, NULL, NULL
into u
(unauthorized) if we didn't find a match in step 1.user_id, body, sent
into s
(select) if we did find a match in step 1.u
and s
.Code
-- List messages between two users with `user_id_1`, `session_id`, `user_id_2`
CREATE FUNCTION messages(bigint, uuid, bigint) RETURNS TABLE(i bigint, b text, s double precision) AS
$$
WITH a AS (
SELECT 1
FROM sessions
WHERE user_id = $1
AND id = $2
), u AS (
SELECT 0, NULL::text, NULL::double precision
WHERE NOT EXISTS (SELECT 1 FROM a)
), s AS (
SELECT user_id, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages
WHERE EXISTS (SELECT 1 FROM a)
AND chat_id = pair($1, $3)
LIMIT 20
)
SELECT * FROM u UNION ALL SELECT * FROM s;
$$
LANGUAGE SQL STABLE;
Upvotes: 3
Reputation: 36234
There is a CASE
expression in addition to the (pl/pgsql only) CASE
control structure.
EDIT: CASE
expression in sql context:
SELECT CASE
WHEN my_conditions_are_met THEN a
ELSE NULL
END AS a_or_null,
b,
c
FROM users;
EDIT 2: given your example that's how you can do it in pure SQL:
WITH params AS (
SELECT user_auth(:user_id, :key) AS user_auth,
pair(:user_id, :with_user_id) AS chat_id
), error_message AS (
SELECT -1 AS "from",
'auth error' AS "body",
EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) AS "sent"
)
SELECT from, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages
JOIN params ON messages.chat_id = params.chat_id AND params.user_auth
UNION ALL
SELECT error_message.*
FROM error_message
JOIN params ON NOT params.user_auth
Upvotes: 0
Reputation: 126329
The PL/pgsql function below returns the messages
sent between user_id
& with_user_id
if the user_id
:key
pair is authorized, as determined by the user-defined function (UDF) user_auth
. Otherwise, it returns one row with from = -1
. The other UDF, pair
, is a unique unordered pairing function that, given two user IDs, returns the chat_id
to which the messages belong.
--- Arguments: user_id, key, with_user_id
CREATE FUNCTION messages(bigint, uuid, bigint)
RETURNS TABLE(from bigint, body text, sent double precision) AS $$
BEGIN
IF user_auth($1, $2) THEN
RETURN QUERY SELECT from, body, trunc(EXTRACT(EPOCH FROM sent))
FROM messages WHERE chat_id = pair($1, $3);
ELSE
i := -1;
RETURN NEXT;
END IF;
END;
$$ LANGUAGE plpgsql STABLE;
I don't know how to translate this to an SQL function or whether that would be better.
Upvotes: 2
Reputation: 7726
This will work, but it's not pretty:
WITH
u AS (SELECT * FROM user WHERE mail = '..'),
code AS (
SELECT
CASE (SELECT count(*) FROM u)
WHEN 0 THEN
'not found'
ELSE
CASE (SELECT count(*) FROM u WHERE password = '..')
WHEN 1 THEN
'right password'
ELSE
'wrong password'
END
END)
SELECT
code.*,
u.*
FROM code NATURAL LEFT OUTER JOIN u
I think you might want to look into creating a result set returning function instead.
Upvotes: 1