PostgreSQL v12.6 中的 PLpgSQL INSERT-RETURNING-INTO 错误?
Posted
技术标签:
【中文标题】PostgreSQL v12.6 中的 PLpgSQL INSERT-RETURNING-INTO 错误?【英文标题】:PLpgSQL INSERT-RETURNING-INTO bug at PostgreSQL v12.6? 【发布时间】:2021-04-07 11:08:30 【问题描述】:声明的 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;
Bug 证据:函数显示“Hello...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语句拆分成两个INSERTS的序列……但是问题这里不是“如何避免问题”,是关于为什么(?)PostgreSQL planner/optimizer 没有优化 WITH 子句的“INSERT 数据流”。
我们可以想象 planner 在后台进行拆分,或者识别 数据管道并检查一种管道并发的机会-将两个INSERT分成多个阶段以使用stream processing strategy。
(在@LaurenzAlbe 的回答后编辑)
PS:“INSERT 的数据流”可以优化。 Big data streams 和 small execution pipelines 是流行的优化策略,使用相同的概念。它存在于数据流应用程序和许多语言中,如Go、Scala、...或Spark等框架。
也许专家说“没有需要识别的数据依赖性”是很自然的......所以,如果它不是 PostgreSQL 的错误(也许我的wrong hypothesis 导致了一个错误的问题),一个子问题是“如何对 PostgreSQL 规划者说有一个优化(管道)机会,是可能的吗?”;或“为什么规划者不使用这种优化机会?”
【问题讨论】:
【参考方案1】:正如所料,这个错误是你的。见the documentation:
WITH
中的子语句彼此并发执行,并与主查询同时执行。因此,当在WITH
中使用数据修改语句时,指定更新实际发生的顺序是不可预测的。所有语句都使用相同的快照执行(请参阅Chapter 13),因此它们无法“看到”彼此对目标表的影响。这减轻了行更新的实际顺序的不可预测性的影响,并且意味着RETURNING
数据是在不同的WITH
子语句和主查询之间传达更改的唯一方式。
您的误解似乎是 SQL 是一种过程语言,其中语句的某些部分以特定顺序执行。但 SQL 不是,你不能依赖它。
解决办法很简单:使用两条单独的SQL语句,然后第二条就可以看到第一条的效果了。
【讨论】:
嗨,有趣的好线索。但没有意义:PLpgSQL是一种顺序语言,步骤顺序为: 1. "INSERT INTO ...RETURNING file_id INTO q_file_id"; 2. RAISE NOTICE q_file_id"; 3. "WITH ... using q_file_id"。所以q_file_id
必须对于所有 WITH 语句都相同。
...哼...第二个INSERT依赖第一个INSERT,ok,不过我需要一个pipeline INSERT execution,优化一下就好,每个INSERT作为一个CPU进程... PostgreSQL 优化器必须检测存在“数据流依赖”(不能是完全并行的过程,它只是流水线级别的并发)。所以问题是“有意义吗?”。如果答案是肯定的,我们就断定这是一个错误;否则(如果我的解释是错误的)那是我的失败。
你在说大话,但这与 CPU 实现无关。当然,PostgreSQL 可以以不同的方式工作,但它不需要。我没有发现问题 - 运行两个不同的 SQL 语句。
您好,很抱歉使用“CPU”这个词,这与“CPU 规模”无关,而且我不是数据库并发方面的专家...在 PostgreSQL宇宙,正确的行话它可能是“快照”(或“线程”或“并行事务”......)。我评论的重点是管道并行性(一个与规模无关的概念),然后一个子问题是“为什么 PostgreSQL 不使用管道?”。
您基本上是在抱怨,因为 PostgreSQL 的工作方式与您希望的不同。 “为什么”问题大多是无用的:答案总是“因为它的设计/实现方式不同”。要么以 PostgreSQL 的方式做事,要么使用不同的数据库。以上是关于PostgreSQL v12.6 中的 PLpgSQL INSERT-RETURNING-INTO 错误?的主要内容,如果未能解决你的问题,请参考以下文章
Orange.Technologies.Cadpipe.Suite.v12.6 1CD(管道设计软件)
Orange.Technologies.Cadpipe.Suite.v12.6 1CD(管道设计软件)