如何在函数中使用 SELECT...FOR UPDATE?

How do you use SELECT...FOR UPDATE in a function?

离开this example,我正在尝试锁定某人的信用卡帐户(行),检查他们是否有足够的钱,如果有,就用它来支付。我需要锁定它以防止他们有足够信用的情况,但随后它被用于另一笔交易,我的程序仍然认为它仍然有足够的信用。

在终端级别,我可以这样完成:我可以在两个终端中打开两个 psql 会话,我可以在一个终端中发出 SELECT * FROM credit_card WHERE credit_card_number = 1234 FOR UPDATE; 命令,然后发出 SELECT * FROM credit_card FOR UDPATE在另一个(或其他类似 UPDATE credit_card SET credits = credits -99 WHERE credit_card_number = 1234)中,我可以看到前一个调用阻止了后一个调用。但是,当我做同样的事情但在这样的函数中时

CREATE OR REPLACE FUNCTION foo (p_credit_card_number BIGINT)
RETURNS VOID
AS $$
BEGIN
    SELECT * FROM credit_card WHERE credit_card_number = p_credit_card_number FOR UPDATE
    ...
END
$$
LANGUAGE 'plpgsql';

我得到了典型的 query has no destination for result data 错误(例如,参见 here)。

问题: 如何在函数内部使用 SELECT ...FOR UPDATE 时锁定特定行或行数,同时避免上述错误?如果不可能或不可取,我还能如何在函数内执行此操作?

您遇到的错误与“for update”子句无关。它在 not 的存储过程中工作完全相同。您必须在过程(或函数或 do 块)内执行 select 语句的错误。当您在过程中 select 时,您 必须 告诉过程如何处理 selected 变量。 你用 Select 做成 ...
因此,假设您确实需要数据,那么如下所示:

create or replace function foo (p_credit_card_number bigint)
  returns void
  language 'plpgsql'
as $$
    v_credit_card  credit_card%type;
begin
    select * 
      into v_credit_card
      from credit_card 
      where credit_card_number = p_credit_card_number 
        for update;
    ...
    
    update credit_card
      set ... 
    where credit_card =  v_credit_card ;
      
end;
$$;

虽然更常见的用法是使用 'where current of'

创建游标和更新
create or replace function foo (p_credit_card_number bigint)
  returns void
  language 'plpgsql'
as $$
    c_credit_cards cursor for 
        select * 
          from credit_card 
         where credit_card_number = p_credit_card_number 
           for update;
    v_credit_card  credit_card%type;
begin

    for v_cc in c_credit_cards 
    loop
       ... additional processing ... 
    
       update credit_card
          set ... 
        where current of c_credit_cards;
    end loop; 
   ... 
end;

但请注意,当游标打开时,其中的所有行都被锁定。