PLpgSQL INSERT-RETURNING-INTO bug at PostgreSQL v12.6?



已声明PLpgSQL变量的命令INSERT ... RETURNING c1 INTO v1在第二次使用时丢失值。

在下面的完整函数中;v1";变量是q_file_id:它在第一次INSERT(jins(时有一个非零值,但在第二次INSERT(ins2(中它的值似乎不相同,或者无法识别数据依赖关系。

CREATE or replace FUNCTION ingest.geojson_load(
p_file text, p_ftid int, p_ftype text DEFAULT NULL
) RETURNS text AS $f$
DECLARE
q_file_id integer;
q_ret text;
BEGIN
INSERT INTO ingest.file(ftid,file_type,file_meta)
SELECT p_ftid::smallint,
COALESCE( p_ftype, substring(p_file from '[^.]+$') ),
geojson_readfile_headers(p_file)
RETURNING file_id INTO q_file_id;
RAISE NOTICE 'The File_id is %', q_file_id;
WITH jins AS ( -- FIRST use is working fine!
INSERT INTO ingest.tmp_geojson_feature
SELECT * 
FROM geojson_readfile_features_jgeom(p_file, q_file_id )
RETURNING 1
), ins2 AS ( -- bug at this SECOND use of q_file_id
INSERT INTO ingest.feature
SELECT file_id, feature_id, properties, ST_GeomFromGeoJSON(jgeom)
FROM ingest.tmp_geojson_feature
WHERE file_id = q_file_id
RETURNING 1
)
SELECT 'Hello jins='|| (SELECT COUNT(*) FROM jins) 
||' items from file_id '|| q_file_id ||'.'
||E'n ins2= '|| (SELECT COUNT(*) FROM ins2) ||' items.'
INTO q_ret;
-- if no bug: DELETE FROM ingest.tmp_geojson_feature WHERE file_id = q_file_id;
RETURN q_ret;
END;
$f$ LANGUAGE PLpgSQL;

错误证据:函数说";你好ins2=0";,但是,当RAISE NOTICE为8并且我只运行带有8的INSERT片段ins2时,它工作正常。

INSERT INTO ingest.feature
SELECT file_id, feature_id, properties, ST_GeomFromGeoJSON(jgeom)
FROM ingest.tmp_geojson_feature
WHERE file_id = 8
-- inserted!

注释

回顾考虑到这一证据的函数,第一个INSERT是分开的,以避免执行并发性,因此自然的解决方案也是将with语句拆分为两个INSERT的序列。。。但这里的问题不是";如何避免这个问题">,是关于为什么(?(PostgreSQL计划器/优化器没有优化";INSERTs的数据流";WITH子句的。

我们可以想象planner在引擎盖下进行拆分,或者识别数据管道,并检查一种管道并发性的机会——将两个INSERT拆分为阶段以使用流处理策略。


(根据@LaurenzAlbe的回答编辑(

PS:;INSERTs的数据流";可以进行优化。大数据流和小执行管道是流行的优化策略,使用相同的概念。它存在于数据流应用程序和许多语言中,如Go、Scala。。。或者像Spark这样的框架。

也许对于一个专家来说说";不存在要识别的数据依赖性";。。。因此,如果它不是PostgreSQL的bug(可能是我错误的假设导致了错误的问题(,那么子问题是
"如何对PostgreSQL规划师说有优化(管道(的机会,是可能的">;或";为什么规划者没有利用这种优化机会">

不出所料,这个bug是你的。请参阅文档:

WITH中的子语句与主查询同时执行。因此,当在WITH中使用数据修改语句时,指定的更新实际发生的顺序是不可预测的。所有语句都使用相同的快照执行(请参阅第13章(,因此它们无法"看到"彼此对目标表的影响。这减轻了行更新的实际顺序的不可预测性的影响,并意味着RETURNING数据是在不同的WITH子语句和主查询之间传递更改的唯一方式。

您的误解似乎是SQL是一种过程语言,语句的某些部分按特定顺序执行。但是SQL不是,而且你不能依赖它。

补救方法很简单:使用两个单独的SQL语句,然后第二个语句可以看到第一个语句的效果。

最新更新