PL / pgSQL:函数返回时插入的数据不可用

时间:2014-07-24 08:22:49

标签: postgresql postgresql-8.3

我有一个有趣的问题,我和我的同事现在有些困难。

我在PostgreSQL-8.3中有一个PL / pgSQL函数(抱歉那个旧版本,我无法改变它),它执行以下四项操作:

  1. 从序列中获取新的序列号(ID)
  2. 将一些记录插入到包含该序列的表中
  3. 发送插入发生的通知信号
  4. 将ID返回给调用者。

简化功能:

CREATE OR REPLACE FUNCTION add_entry(_user_name text, _visible_attr integer[])
  RETURNS bigint AS
$BODY$
  DECLARE
    user_name text := '#' || $1;
    user_id bigint;
  BEGIN

    -- get the ID from the sequence
    SELECT nextval('my_sequence') INTO user_id;

    -- insert the name (and some other data not shown here) 5x
    FOR item IN 1..5
    LOOP
      INSERT INTO mytable
        (id,index,username,visible)
        VALUES (user_id,item,user_name,$2[item]);
    END LOOP;

    -- send notify that an insertion took place
    notify my_notify;

    RETURN user_id;
  END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

所以,我的同事从他的应用程序中调用了这个函数。他获取返回的ID并在其应用程序中使用另一个线程(DB池)来调用一个函数,该函数将返回先前使用该ID插入的数据。但是,这不是第一次工作。只有第二个请求他才能选择数据。似乎INSERT在函数已经返回时没有完成?!

我们检查了多次,数据将正确地插入到表中,但不知何时它的可用速度与返回值(序列中的ID)一样快!为什么会这样?

更新:错误的假设

我进一步检查并将示例缩减为一个真正显示问题的简单查询:

select * from mytable where id = (select add_entry('MyTestUser'));

此查询不返回任何行。但是,如果我在两个单独的步骤中执行此操作,我可以选择使用add_entry函数插入的数据。

我不知道我做错了什么,或者我如何加快插入......

1 个答案:

答案 0 :(得分:1)

From the 8.3 manual

  

实际上,SELECT查询会在查询开始运行的那一刻看到数据库的快照

由于更新是在select本身完成的,因此不会看到插入的行。

http://www.postgresql.org/docs/8.3/static/tutorial-transactions.html

更改功能以返回setof mytable。它可以是普通的SQL。要更改返回类型,必须先删除该功能

drop function add_entry(text);

create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$

    notify my_notify;
    with ins as (
        insert into mytable (id, index, username, visible)
        select user_id, item, '#' || $1, $2[item]
        from
            generate_series(1, 5) g(item)
            cross join
            (values (nextval('my_sequence'))) s(user_id)
        returning *
    )
    select * from ins;

$body$ language sql volatile;

通知必须在从函数返回任何内容之前发生。这不是一个问题,好像插入失败了事务回滚包括通知。称之为

select * from add_entry('MyTestUser');

select不会看到修改后的表,而是返回mytable行。

如果函数必须为plpgsql,请使用return query

create or replace function add_entry (_user_name text, _visible_attr integer[])
returns setof mytable as $body$
begin
    notify my_notify;
    return query
        insert into mytable (id, index, username, visible)
        select user_id, item, '#' || $1, $2[item]
        from
            generate_series(1, 5) g(item)
            cross join
            (values (nextval('my_sequence'))) s(user_id)
        returning *
    ;
end;
$body$ language plpgsql volatile;