Opened 3 months ago

Last modified 3 months ago

#5793 new defect

Crash on Buffer of Empty array on PG17

Reported by: pramsey Owned by: pramsey
Priority: medium Milestone: PostGIS PostgreSQL
Component: postgis Version: 3.4.x
Keywords: pg17 Cc:

Description (last modified by pramsey)

create extension postgis;

create table tab (shape geography);

select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;

Change History (12)

comment:1 by pramsey, 3 months ago

This is such a large collection of functions, it would stand to reason that a simpler version would still crash, but this doesn't, even though I think it's pushing the same stuff into the buffer call.

select st_buffer(st_collect(tab.shape::geometry)::geography, 10) from tab;

comment:2 by robe, 3 months ago

Does it only crash for pg17? Wondering if maybe something changed with array_agg function in PG17 or something with the transitioning of that.

comment:3 by robe, 3 months ago

Hmm this is interesting:

— this doesn't crash

select st_buffer(st_collect(array_agg(tab.shape::geometry))::geography, 10.0) from tab

Not sure why the order of casting should matter

comment:4 by robe, 3 months ago

Keywords: pg17 added
Summary: Crash on Buffer of Empty arrayCrash on Buffer of Empty array on PG17

This does seem to be isolated to pg17. Just tested this on a pg16 instance with 3.5.0.

It does not crash.

comment:5 by pramsey, 3 months ago

Oh interesting. I have a stack trace and no postgis code is touched. Exercise for the reader: reconstruct this crash using only core functions.

  * frame #0: 0x00000001053a945c postgres`find_base_rel(root=0x0000000000000000, relid=1) at relnode.c:419:38
    frame #1: 0x00000001056c5d64 postgres`examine_variable(root=0x0000000000000000, node=0x000000011e8092c8, varRelid=0, vardata=0x000000016afc76f0) at selfuncs.c:5073:14
    frame #2: 0x00000001056c8474 postgres`estimate_array_length(root=0x0000000000000000, arrayexpr=0x000000011e8092c8) at selfuncs.c:2164:3
    frame #3: 0x000000010531cbec postgres`cost_qual_eval_walker(node=0x000000011e809dc8, context=0x000000016afc82e0) at costsize.c:4861:5
    frame #4: 0x000000010529f46c postgres`expression_tree_walker_impl(node=0x000000011e809ff8, walker=(postgres`cost_qual_eval_walker at costsize.c:4681), context=0x000000016afc82e0) at nodeFuncs.c:2524:9
    frame #5: 0x000000010529e404 postgres`expression_tree_walker_impl(node=0x000000011e80a048, walker=(postgres`cost_qual_eval_walker at costsize.c:4681), context=0x000000016afc82e0) at nodeFuncs.c:2193:9
    frame #6: 0x000000010531cf84 postgres`cost_qual_eval_walker(node=0x000000011e80a048, context=0x000000016afc82e0) at costsize.c:4945:9
    frame #7: 0x000000010529f46c postgres`expression_tree_walker_impl(node=0x000000011e80a098, walker=(postgres`cost_qual_eval_walker at costsize.c:4681), context=0x000000016afc82e0) at nodeFuncs.c:2524:9
    frame #8: 0x000000010529e404 postgres`expression_tree_walker_impl(node=0x000000011e80a0e8, walker=(postgres`cost_qual_eval_walker at costsize.c:4681), context=0x000000016afc82e0) at nodeFuncs.c:2193:9
    frame #9: 0x000000010531cf84 postgres`cost_qual_eval_walker(node=0x000000011e80a0e8, context=0x000000016afc82e0) at costsize.c:4945:9
    frame #10: 0x0000000105315b18 postgres`cost_qual_eval(cost=0x000000016afc8378, quals=0x000000011e80b768, root=0x0000000000000000) at costsize.c:4655:3
    frame #11: 0x000000010538fab0 postgres`inline_function(funcid=18459, result_type=18318, result_collid=0, input_collid=0, args=0x000000011e80a138, funcvariadic=false, func_tuple=0x00000001480ab478, context=0x000000016afca1b8) at clauses.c:4816:4
    frame #12: 0x000000010538e59c postgres`simplify_function(funcid=18459, result_type=18318, result_typmod=-1, result_collid=0, input_collid=0, args_p=0x000000016afc89f0, funcvariadic=false, process_args=true, allow_non_const=true, context=0x000000016afca1b8) at clauses.c:4133:13
    frame #13: 0x0000000105389fa4 postgres`eval_const_expressions_mutator(node=0x000000011e809788, context=0x000000016afca1b8) at clauses.c:2591:14
    frame #14: 0x00000001052a1f94 postgres`expression_tree_mutator_impl(node=0x000000011e8097d8, mutator=(postgres`eval_const_expressions_mutator at clauses.c:2442), context=0x000000016afca1b8) at nodeFuncs.c:3459:5
    frame #15: 0x000000010538c178 postgres`eval_const_expressions_mutator(node=0x000000011e8097d8, context=0x000000016afca1b8) at clauses.c:3711:9
    frame #16: 0x00000001052a236c postgres`expression_tree_mutator_impl(node=0x000000011e809828, mutator=(postgres`eval_const_expressions_mutator at clauses.c:2442), context=0x000000016afca1b8) at nodeFuncs.c:3545:12
    frame #17: 0x000000010538c178 postgres`eval_const_expressions_mutator(node=0x000000011e809828, context=0x000000016afca1b8) at clauses.c:3711:9
    frame #18: 0x00000001053898bc postgres`eval_const_expressions(root=0x000000011e809918, node=0x000000011e809828) at clauses.c:2266:9
    frame #19: 0x0000000105358664 postgres`preprocess_expression(root=0x000000011e809918, expr=0x000000011e809828, kind=1) at planner.c:1175:10
    frame #20: 0x0000000105357388 postgres`subquery_planner(glob=0x000000012e016580, parse=0x000000012e016690, parent_root=0x0000000000000000, hasRecursion=false, tuple_fraction=0, setops=0x0000000000000000) at planner.c:842:3
    frame #21: 0x000000010535635c postgres`standard_planner(parse=0x000000012e016690, query_string="select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;", cursorOptions=2048, boundParams=0x0000000000000000) at planner.c:421:9
    frame #22: 0x0000000105356078 postgres`planner(parse=0x000000012e016690, query_string="select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;", cursorOptions=2048, boundParams=0x0000000000000000) at planner.c:282:12
    frame #23: 0x00000001054fe95c postgres`pg_plan_query(querytree=0x000000012e016690, query_string="select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;", cursorOptions=2048, boundParams=0x0000000000000000) at postgres.c:908:9
    frame #24: 0x00000001054feb3c postgres`pg_plan_queries(querytrees=0x000000011e8098c8, query_string="select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;", cursorOptions=2048, boundParams=0x0000000000000000) at postgres.c:1000:11
    frame #25: 0x0000000105502684 postgres`exec_simple_query(query_string="select st_buffer(st_collect(array_agg (tab.shape)::geometry[])::geography, 10.0) from tab;") at postgres.c:1197:19
    frame #26: 0x00000001055018f4 postgres`PostgresMain(dbname="pramsey", username="pramsey") at postgres.c:4767:7
    frame #27: 0x00000001054f8fc0 postgres`BackendMain(startup_data="", startup_data_len=4) at backend_startup.c:105:2
    frame #28: 0x00000001053d6974 postgres`postmaster_child_launch(child_type=B_BACKEND, startup_data="", startup_data_len=4, client_sock=0x000000016afcaab8) at launch_backend.c:277:3
    frame #29: 0x00000001053de840 postgres`BackendStartup(client_sock=0x000000016afcaab8) at postmaster.c:3593:8
    frame #30: 0x00000001053db48c postgres`ServerLoop at postmaster.c:1674:6
    frame #31: 0x00000001053da358 postgres`PostmasterMain(argc=3, argv=0x00006000026e2d80) at postmaster.c:1372:11
    frame #32: 0x000000010525ed00 postgres`main(argc=3, argv=0x00006000026e2d80) at main.c:197:3
    frame #33: 0x000000018792b154 dyld`start + 2476

comment:7 by pramsey, 3 months ago

Description: modified (diff)

comment:8 by pramsey, 3 months ago

Ladles and jelly spoons, I give you a pure PgSQL reproduction:

CREATE TABLE n (i integer);

CREATE OR REPLACE FUNCTION add(integer)
    RETURNS integer
    AS 'SELECT 2 * $1 + 4 * $1'
    LANGUAGE 'sql' IMMUTABLE STRICT PARALLEL SAFE;

SELECT add(array_length(array_agg(i)::numeric[],1)::integer) FROM n;

comment:9 by robe, 3 months ago

Milestone: PostGIS 3.5.1PostGIS PostgreSQL

comment:10 by pramsey, 3 months ago

Milestone: PostGIS PostgreSQLPostGIS 3.5.1

Actually, can get it down to this

CREATE TABLE n (i integer);

CREATE OR REPLACE FUNCTION l2(numeric[])
    RETURNS integer
    AS 'SELECT array_length($1,1) + array_length($1,1)'
    LANGUAGE 'sql' IMMUTABLE;

SELECT l2(array_agg(i)::numeric[]) FROM n;
Last edited 3 months ago by pramsey (previous) (diff)

comment:12 by robe, 3 months ago

Milestone: PostGIS 3.5.1PostGIS PostgreSQL

switching back to PostgreSQL, looks like they have a tentative fix as discussed here:

https://www.postgresql.org/message-id/Zwbri-LE-8OJEIlW%40nathan

and the culprit was ​this commit https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=9391f71

Note: See TracTickets for help on using tickets.