PostgreSQL:函数中的条件语句

PostgreSQL: conditional statements in functions

我一直在尝试在 postgreSQL 9.4 中创建一个基本的用户身份验证系统,但一直没有成功。我的用户 table 看起来像这样:

-- Users table
CREATE TABLE users (
  user_id SERIAL NOT NULL PRIMARY KEY,

  first_name TEXT NOT NULL,
  last_name TEXT NOT NULL,
  email TEXT NOT NULL,
  password TEXT NOT NULL,

  failed_login_attempts INT NOT NULL DEFAULT 0
    CONSTRAINT positive_login_attempts CHECK (failed_login_attempts >= 0), 
  last_failed_login_attempt TIMESTAMP NULL,

  UNIQUE(email),

  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL,
  deleted_at TIMESTAMP NULL
);

这些函数工作正常:

-- check to see if a user exists
CREATE OR REPLACE FUNCTION user_exists (auth_email VARCHAR(254))
RETURNS SETOF users AS $$
  SELECT *
  FROM users
  WHERE email = auth_email
$$ LANGUAGE SQL;

-- authenticates a user against the system
CREATE OR REPLACE FUNCTION authenticate_user (auth_email VARCHAR(254), auth_password VARCHAR(72))
RETURNS SETOF users AS $$
  SELECT *
  FROM users
  WHERE email = auth_email
  AND password = crypt(auth_password, password))
$$ LANGUAGE SQL;

但是,当我尝试将这些结合起来时,我的脸就垮了。在半伪代码中,我想做的是:

-- login function
CREATE OR REPLACE FUNCTION user_login (auth_email VARCHAR(254), auth_password VARCHAR(72)) RETURNS SETOF users AS $$
  IF EXISTS (SELECT COUNT(*) FROM user_exists(auth_email))
    IF EXISTS (SELECT COUNT(*) FROM authenticate_user (auth_email, auth_password))
      -- set the failed_login_attempts value to 0
      -- set the last failed login attempt as NULL
      -- return the user details
    ELSE
      -- increment the failed_login_attempts value
      -- set the last failed login attempt as the current time
      -- return nothing
    END IF;
  ELSE
    -- return nothing
  END IF;
$$ LANGUAGE SQL;

这可能吗?我是否完全走错了路线?

'failed login attempts' 的目的是将其设置为逐渐延长冷静期 - 例如失败的尝试:

  1. 1秒
  2. 2秒
  3. 4s
  4. 8s
  5. 16s
  6. ...

是否要求使用SQL语言编写函数? 如果您接受 PLPGSQL.

,则有 PLPGSQL 程序的解决方案
CREATE OR REPLACE FUNCTION user_login (auth_email VARCHAR(254), auth_password VARCHAR(72)) 
RETURNS SETOF users AS 
$$
DECLARE 
    found_user users;
BEGIN
    SELECT u.* 
    FROM users u
    WHERE u.email=auth_email
    INTO found_user;

    -- Check password here using your algorithm
    IF found_user.password = auth_password THEN
        RETURN NEXT found_user;
        RETURN;
    END IF;

    UPDATE users SET
          failed_login_attempts = failed_login_attempts + 1
        , last_failed_login_attempt = now()
    WHERE user_id = found_user.user_id;
END;
$$
LANGUAGE plpgsql;