游标检索在 plpgsql 函数中创建的 table 中已删除的行
Cursor retrieves a deleted row in table created within plpgsql function
在 plpgsql 函数中,我创建了一个 table 并使用游标访问它的行。在第一行,我删除了下一行,令人惊讶的是(至少对我而言)游标获取了它。在同一个函数中重复时,它按我预期的那样工作。
但是,如果 table 预先存在并且不是在函数内创建的,则永远不会提取已删除的行。
我错过了什么?
DECLARE
curs1 refcursor;
rec record;
BEGIN
CREATE TABLE test as select generate_series(1,5,1) test_id;
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
IF rec.test_id=1 THEN
DELETE FROM TEST WHERE test_id=3;
END IF;
END LOOP;
CLOSE curs1;
RAISE NOTICE 'AGAIN';
--just repeating without deleting
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
END LOOP;
CLOSE curs1;
输出为:
NOTICE: ROW:1
NOTICE: ROW:2
NOTICE: ROW:3
NOTICE: ROW:4
NOTICE: ROW:5
NOTICE: AGAIN
NOTICE: ROW:1
NOTICE: ROW:2
NOTICE: ROW:4
NOTICE: ROW:5
原因是 Postgres 游标默认为 "insensitive"。 The documentation:
The SQL standard says that it is implementation-dependent whether
cursors are sensitive to concurrent updates of the underlying data by
default. In PostgreSQL, cursors are insensitive by default, and can be
made sensitive by specifying FOR UPDATE
. Other products may work differently.
大胆强调我的。
所以尝试使用 FOR UPDATE
子句:
DO
$$
DECLARE
curs1 refcursor;
rec record;
BEGIN
CREATE TABLE test AS SELECT generate_series(1,5) test_id;
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id <b>FOR UPDATE</b>;
DELETE FROM test WHERE test_id = 3;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
END LOOP;
CLOSE curs1;
END
$$
你得到:
NOTICE: ROW:1
NOTICE: ROW:2
NOTICE: ROW:4
NOTICE: ROW:5
第 3 行不再可见。
在 plpgsql 函数中,我创建了一个 table 并使用游标访问它的行。在第一行,我删除了下一行,令人惊讶的是(至少对我而言)游标获取了它。在同一个函数中重复时,它按我预期的那样工作。
但是,如果 table 预先存在并且不是在函数内创建的,则永远不会提取已删除的行。
我错过了什么?
DECLARE
curs1 refcursor;
rec record;
BEGIN
CREATE TABLE test as select generate_series(1,5,1) test_id;
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
IF rec.test_id=1 THEN
DELETE FROM TEST WHERE test_id=3;
END IF;
END LOOP;
CLOSE curs1;
RAISE NOTICE 'AGAIN';
--just repeating without deleting
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
END LOOP;
CLOSE curs1;
输出为:
NOTICE: ROW:1
NOTICE: ROW:2
NOTICE: ROW:3
NOTICE: ROW:4
NOTICE: ROW:5
NOTICE: AGAIN
NOTICE: ROW:1
NOTICE: ROW:2
NOTICE: ROW:4
NOTICE: ROW:5
原因是 Postgres 游标默认为 "insensitive"。 The documentation:
The SQL standard says that it is implementation-dependent whether cursors are sensitive to concurrent updates of the underlying data by default. In PostgreSQL, cursors are insensitive by default, and can be made sensitive by specifying
FOR UPDATE
. Other products may work differently.
大胆强调我的。
所以尝试使用 FOR UPDATE
子句:
DO
$$
DECLARE
curs1 refcursor;
rec record;
BEGIN
CREATE TABLE test AS SELECT generate_series(1,5) test_id;
OPEN curs1 FOR SELECT * FROM test ORDER BY test_id <b>FOR UPDATE</b>;
DELETE FROM test WHERE test_id = 3;
LOOP
FETCH curs1 INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'ROW:%',rec.test_id;
END LOOP;
CLOSE curs1;
END
$$
你得到:
NOTICE: ROW:1 NOTICE: ROW:2 NOTICE: ROW:4 NOTICE: ROW:5
第 3 行不再可见。