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

将变量分配给PostgreSQL触发器中的输出参数,postgresql,triggers,plpgsql,Postgresql,Triggers,Plpgsql,在使用Sybase和SQL Server多年后,我开始了一个PostgreSQL项目,我对它的怪癖还不熟悉,但到目前为止我印象深刻 我编写了一个返回参数的小函数。我传入一个清晰的密码,函数返回加密的密码和一个salt。这是: CREATE OR REPLACE FUNCTION iSecGenPwdSalt(pwdin text, out salt text, out userpassword text) AS $BODY$ DECLARE BEGIN -- Gen

在使用Sybase和SQL Server多年后,我开始了一个PostgreSQL项目,我对它的怪癖还不熟悉,但到目前为止我印象深刻

我编写了一个返回参数的小函数。我传入一个清晰的密码,函数返回加密的密码和一个salt。这是:

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列,但我无法理解语法。我尝试了一个主题的多种变体,但仍然有问题

从iSecGenPwdSalt(“mymagicpassword”)中选择salt、userpassword

漂亮地返回结果,因此我想调用此函数,并将结果放入触发器中的一对变量中,以便分配值

这是我的密码:

SELECT usersalt=salt, userpwd=userpassword 
  FROM iSecGenPwdSalt(NEW.systemuserpassword);
因此,我得到的错误是:

错误:查询没有结果数据的目标 提示:如果要放弃选择的结果,请改用“执行”。 上下文:PL/pgSQL函数“isec_trg_systemuser”在SQL语句的第13行


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

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

以下是我的用户表和视图:

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;
insert规则负责生成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
视图创建的。您可以设置权限,使您拥有一个可以查看用户视图但不能查看基础用户表的非特权角色。通过这种方式,您可以轻松地使用salt密码与用户表进行交互,而无需在应用程序中处理salt。

在您的特定情况下,我认为您可以使用具有多列的
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;
完整示例(使用contrib模块):

援引:

=>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)

我认为你是对的,我必须改变处理问题的方式。也许我是在用Sybase/SQL Server的思维方式选择变量。SELECT INTO NEW.columnname似乎是一个很好的解决方法,也许是一个比我计划实现的更好的方法。非常感谢。我认为您和Grzegorz Szpetkowski在这里提供的解决方案非常有用,它们都指向SELECT INTO NEW.columnname方法,而不是中间变量。
=>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)