Postgresql 选择并使用一列 请考虑下面的存储过程。此函数接收一个整数作为其第一个参数,指示必须针对当前用户检查哪个privilegeid。特权存储在“特权”表中,由id和名称varchar组成

Postgresql 选择并使用一列 请考虑下面的存储过程。此函数接收一个整数作为其第一个参数,指示必须针对当前用户检查哪个privilegeid。特权存储在“特权”表中,由id和名称varchar组成,postgresql,stored-procedures,plpgsql,Postgresql,Stored Procedures,Plpgsql,每个权限属于存储在用户\角色中的一个或多个角色。每个用户被分配到一个或多个角色。此函数检索分配给当前_用户的所有角色,并根据给定的priviligeid检查它们(如前所述) CREATE OR REPLACE FUNCTION "public"."has_privilege" (in int4) RETURNS bool AS $BODY$ DECLARE asked_privilegeid ALIAS FOR $1; userid int; role_row users

每个权限属于存储在用户\角色中的一个或多个角色。每个用户被分配到一个或多个角色。此函数检索分配给当前_用户的所有角色,并根据给定的priviligeid检查它们(如前所述)

CREATE OR REPLACE FUNCTION "public"."has_privilege" (in int4) RETURNS bool AS
$BODY$
DECLARE
    asked_privilegeid ALIAS FOR $1;
    userid int;
    role_row users_roles%rowtype;
    privilege_row privileges%rowtype;
BEGIN
    EXECUTE 'SELECT userid FROM users WHERE username=$1' INTO userid USING current_user;

    FOR role_row IN SELECT * FROM users_roles
    WHERE userid=userid 
    LOOP
    IF role_row.roleid = 1 THEN
        return TRUE;
    END IF;

    FOR privilege_row IN SELECT * FROM privileges WHERE roleid=role_row.roleid LOOP
        IF privilege_row.privilegeid = asked_privilegeid THEN
            return TRUE;
        END IF;
    END LOOP;
    END LOOP;

    return FALSE;
END
$BODY$
LANGUAGE 'plpgsql'
然而,这段代码并不是一个有效的方法,因为它可能会考虑检索用户的所有rowValue\u角色和权限。我试图按照以下方式编写过程,但似乎不起作用:

CREATE OR REPLACE FUNCTION "public"."has_privilege" (in int4) RETURNS bool AS
$BODY$
DECLARE
    asked_privilegeid ALIAS FOR $1;
    privilegeid int;
    userid int;
    roleid int;

    //role_row users_roles%rowtype;
    //privilege_row privileges%rowtype;
BEGIN
    EXECUTE 'SELECT userid FROM users WHERE username=$1' INTO userid USING current_user;

    FOR roleid IN SELECT roleid FROM users_roles
    WHERE userid=userid 
    LOOP
    IF roleid = 1 THEN
        return TRUE;
    END IF;

    FOR privilegeid IN SELECT privilegeid FROM privileges WHERE roleid=roleid LOOP
        IF privilegeid = asked_privilegeid THEN
            return TRUE;
        END IF;
    END LOOP;
    END LOOP;

    return FALSE;
END
$BODY$
LANGUAGE 'plpgsql'
我做错了什么?提前谢谢

编辑:缩进没有按预期完成。以下是pastebin链接:
问题出在行上

WHERE userid=userid 
表中的列和变量不明确。同样的问题

FROM privileges WHERE roleid=roleid 
不要使用同时也是要引用的列名的变量名

您还可以将过程主体重写为直接SQL语句,这可能会更快

CREATE OR REPLACE FUNCTION "public"."has_privilege" (in int4) RETURNS bool AS
$BODY$
DECLARE
    asked_privilegeid ALIAS FOR $1;
BEGIN

RETURN EXISTS (
    SELECT *
    FROM users u
    INNER JOIN users_roles r on r.userid=u.userid
    LEFT JOIN privileges p on p.roleid=r.roleid
       AND p.privilegeid = asked_privilegeid
       AND r.roleid <> 1 //  don't need to process this join if we already have our answer
    WHERE u.username = $1
      AND (r.roleid=1 OR p.privilegeid is not null))

END
$BODY$
LANGUAGE 'plpgsql'

问题在于争吵

WHERE userid=userid 
表中的列和变量不明确。同样的问题

FROM privileges WHERE roleid=roleid 
不要使用同时也是要引用的列名的变量名

您还可以将过程主体重写为直接SQL语句,这可能会更快

CREATE OR REPLACE FUNCTION "public"."has_privilege" (in int4) RETURNS bool AS
$BODY$
DECLARE
    asked_privilegeid ALIAS FOR $1;
BEGIN

RETURN EXISTS (
    SELECT *
    FROM users u
    INNER JOIN users_roles r on r.userid=u.userid
    LEFT JOIN privileges p on p.roleid=r.roleid
       AND p.privilegeid = asked_privilegeid
       AND r.roleid <> 1 //  don't need to process this join if we already have our answer
    WHERE u.username = $1
      AND (r.roleid=1 OR p.privilegeid is not null))

END
$BODY$
LANGUAGE 'plpgsql'
在postgre中声明变量时,最好使用下划线b4变量名“\u userid”。使其与列名不同


在postgre中声明变量时,最好使用下划线b4变量名“\u userid”。为了使它与列名不同,我认为只需一条SELECT语句就可以解决这个问题:

SELECT count(*) FROM privileges p JOIN roles r ON r.privilegeid = p.privilegeid JOIN user_roles ur ON ur.roleid = r.roleid JOIN users u ON u.userid = ur.userid AND u.username = session_user WHERE p.privilegeid = asked_privilegeid 未经测试


如果计数为零,则不会分配特权,否则会分配。

我认为只需一个SELECT语句即可解决此问题:

SELECT count(*) FROM privileges p JOIN roles r ON r.privilegeid = p.privilegeid JOIN user_roles ur ON ur.roleid = r.roleid JOIN users u ON u.userid = ur.userid AND u.username = session_user WHERE p.privilegeid = asked_privilegeid 未经测试


如果计数为零,则不分配特权,否则为。

还有roleid=1的特例,我猜是admin=all access。过程中也跳过了角色,因为它不是必需的。还有roleid=1的特例,我猜是admin/all access。在这个过程中,角色也被跳过,因为它不是必需的,因为我绝不是一个SQL专家,我仍在试图理解您的代码。我想知道来自u部分的用户。这不应该引起错误吗?@Mzialla-对不起!只是提供一个替代方案,如果不起作用,就坚持原来的固定方案。您可以尝试在过程之外的查询,轻松地将RETURN更改为SELECT。对于找到的任何行,EXISTS返回true。它将用户加入到用户角色中,还可以选择加入正确加入的权限。如果r.role_id为1,它甚至不会尝试加入特权,并且最后的and条件将返回true。如果roleid不是一个,它将继续搜索与所请求的_privilegeid匹配的角色的权限。如果成功,最后的OR将返回true。无需道歉,我非常感谢您的努力!谢谢你的解释!因为我绝不是SQL专家,所以我仍在努力理解您的代码。我想知道来自u部分的用户。这不应该引起错误吗?@Mzialla-对不起!只是提供一个替代方案,如果不起作用,就坚持原来的固定方案。您可以尝试在过程之外的查询,轻松地将RETURN更改为SELECT。对于找到的任何行,EXISTS返回true。它将用户加入到用户角色中,还可以选择加入正确加入的权限。如果r.role_id为1,它甚至不会尝试加入特权,并且最后的and条件将返回true。如果roleid不是一个,它将继续搜索与所请求的_privilegeid匹配的角色的权限。如果成功,最后的OR将返回true。无需道歉,我非常感谢您的努力!谢谢你的解释!感谢您对session_用户的评论!感谢您对session_用户的评论!