将变量分配给PostgreSQL触发器中的输出参数

时间:2011-08-18 15:35:58

标签: postgresql triggers plpgsql

我在Sybase和SQL Server工作多年之后开始了PostgreSQL项目,并且对它的怪癖很新,但到目前为止给人留下了深刻的印象。

我写了一个返回OUT参数的小函数。我传入一个明确的密码,该函数返回加密的密码和一个盐。这是:

CREATE OR REPLACE FUNCTION iSecGenPwdSalt(pwdin text, out salt text, out userpassword text)
AS 
$BODY$
DECLARE 

    BEGIN
        -- Generate a salt...
        salt = gen_salt('bf', 10);

        -- Now generate the password hash...
        userpassword = crypt(pwdin, salt);
    END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION iSecGenPwdSalt(text, out text , out text ) OWNER TO postgres; 

我想在触发器中调用此函数并使用这些值填充我的密码和salt列,但我无法理解语法。我在主题上尝试了多种变体,但我仍然遇到问题。

select salt, userpassword from iSecGenPwdSalt('mymagicpassword');

很好地返回它的结果,所以我想调用这个函数并将结果输入到触发器中的一对变量中,这样我就可以赋值了。

这是我的代码:

SELECT usersalt=salt, userpwd=userpassword 
  FROM iSecGenPwdSalt(NEW.systemuserpassword);

我得到的错误是:

  

错误:查询没有结果数据的目的地   提示:如果要放弃SELECT的结果,请改用PERFORM。   语境:SQL语句中的PL / pgSQL函数“isec_trg_systemuser”第13行

因此,简单地将一对变量分配给从触发器中的OUT参数派生的结果集会让我感到悲伤。

2 个答案:

答案 0 :(得分:1)

这不是你要问的问题的答案,但最近我不得不做类似的事情,我用视图和规则而不是触发器来做。我希望这是有帮助的,尽管不是真正回答你直接提出的问题。

这是我的用户表和视图:

CREATE TABLE users_t (
  username VARCHAR(16) PRIMARY KEY,
  email    VARCHAR UNIQUE,
  password CHAR(60),
  salt     CHAR(29)
);

CREATE VIEW users AS SELECT username, email, '********'::varchar AS password FROM users_t;

插入规则负责生成salt值:

CREATE RULE users_insert_rule AS ON INSERT TO users DO INSTEAD
INSERT INTO users_t (username, email, password, salt) 
  SELECT new.username, new.email, crypt(new.password, salt.salt), salt.salt 
  FROM (SELECT gen_salt('bf') as salt) salt
RETURNING username, email, '********'::varchar;

为了完整性,我包括更新和删除规则,即使它们不是特别相关:

CREATE RULE users_update_rule AS ON UPDATE TO users DO INSTEAD
  UPDATE users_t 
  SET 
    username = new.username, 
    email = new.email, 
    password = crypt(new.password, salt)
RETURNING username, email, '********'::varchar;

CREATE RULE users_delete_rule AS ON DELETE TO users DO INSTEAD
  DELETE FROM users_t WHERE username = old.username
RETURNING username, email, '********'::varchar;

然后我编写了一个认证功能,负责使用salt:

CREATE OR REPLACE FUNCTION authenticate(varchar, varchar) RETURNS setof users AS $$
SELECT * FROM users 
WHERE username =
  (SELECT username FROM users_t 
   WHERE username = $1 AND crypt($2, salt) = password)
$$ LANGUAGE sql;

因此,您可以通过查询SELECT * FROM authenticate('username', 'password');“登录”,并通过插入users视图来创建用户。您可以设置权限,使您具有可以查看用户视图但不能查看基础users_t表的非特权角色。通过这种方式,您可以轻松地使用salted密码与users表进行交互,而无需在应用程序中处理salt。

答案 1 :(得分:1)

在您的具体情况下,我认为您可以将SELECT INTO语句与多列一起使用:

CREATE OR REPLACE FUNCTION userAuth_insert_f() RETURNS TRIGGER AS $$
BEGIN
    SELECT INTO NEW.usersalt, NEW.userpwd
        salt, userpassword FROM iSecGenPwdSalt(NEW.systemuserpassword);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

完整示例(使用pgcrypto contrib模块):

DROP TABLE IF EXISTS userAuth; 
CREATE TABLE userAuth (
    id serial,
    systemuserpassword text,
    usersalt text,
    userpwd text
);

CREATE OR REPLACE FUNCTION iSecGenPwdSalt
    (pwdin text, OUT salt text, OUT userpassword text)
AS $$ BEGIN
    salt = gen_salt('bf', 10);
    userpassword = crypt(pwdin, salt);
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS userAuth_insert_t ON userAuth;
CREATE TRIGGER userAuth_insert_t
    BEFORE INSERT ON userAuth
    FOR EACH ROW
    EXECUTE PROCEDURE userAuth_insert_f();

调用:

=>INSERT INTO userAuth (systemuserpassword) VALUES ('mymagicpassword');
INSERT 0 1
=>SELECT usersalt, userpwd FROM userauth;
           usersalt            |                           userpwd                
-------------------------------+---------------------------------------------------
 $2a$10$7FqU5a/DrM.CWGlQPlGz6e | $2a$10$7FqU5a/DrM.CWGlQPlGz6eLjuN3UTwh/hlgxTnogS..
(1 row)