diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index a9fe45e347..c98f150fe6 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -1016,7 +1016,7 @@ bool function_parse_error_transpose(const char *prosrc) { int origerrposition; - int newerrposition; + int newerrposition = 0; const char *queryText; /* @@ -1034,12 +1034,15 @@ function_parse_error_transpose(const char *prosrc) return false; } - /* We can get the original query text from the active portal (hack...) */ - Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE); - queryText = ActivePortal->sourceText; + if (ActivePortal) + { + /* We can get the original query text from the active portal (hack...) */ + Assert(ActivePortal->status == PORTAL_ACTIVE); + queryText = ActivePortal->sourceText; - /* Try to locate the prosrc in the original text */ - newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition); + /* Try to locate the prosrc in the original text */ + newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition); + } if (newerrposition > 0) { diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl index 6247aa7730..30b38f300e 100644 --- a/src/test/subscription/t/100_bugs.pl +++ b/src/test/subscription/t/100_bugs.pl @@ -299,4 +299,62 @@ is( $node_subscriber->safe_psql( $node_publisher->stop('fast'); $node_subscriber->stop('fast'); +# The bug was that we assumed that there is always an active Portal when +# handling an error occurring inside the function body of a CREATE FUNCTION. +# Since the logical replication workers (both apply workers and tablesync +# workers) do not have an active Portal, it crashes. +$node_publisher = PostgreSQL::Test::Cluster->new('publisher4'); +$node_publisher->init(allows_streaming => 'logical'); +$node_publisher->start; + +$node_subscriber = PostgreSQL::Test::Cluster->new('subscriber4'); +$node_subscriber->init(allows_streaming => 'logical'); +$node_subscriber->start; + +$node_publisher->safe_psql( + 'postgres', qq{ + CREATE TABLE test_tab(a int); + CREATE PUBLICATION test_pub FOR TABLE test_tab; + }); + +$node_subscriber->safe_psql('postgres', "CREATE TABLE test_tab(a int)"); + +# Create the trigger function on the table that raises an error while +# executing the CREATE FUNCTION. +$node_subscriber->safe_psql( + 'postgres', qq{ +CREATE FUNCTION test_trig_func() RETURNS trigger AS +\$\$ +BEGIN + EXECUTE E'CREATE FUNCTION public.errfunc() RETURNS void AS + \$body\$ + SELECT FROM nonexist_table; + \$body\$ LANGUAGE sql'; + RETURN new; +END; +\$\$ LANGUAGE plpgsql; +CREATE TRIGGER test_trig AFTER INSERT ON test_tab FOR EACH ROW EXECUTE PROCEDURE test_trig_func(); +ALTER TABLE test_tab ENABLE REPLICA TRIGGER test_trig; + }); + +# Create the subscription with disable_on_error so that the logical replication +# stops after the error. +$publisher_connstr = $node_publisher->connstr . ' dbname=postgres'; +$node_subscriber->safe_psql( + 'postgres', + "CREATE SUBSCRIPTION test_sub CONNECTION '$publisher_connstr' PUBLICATION test_pub WITH (disable_on_error = 'true')"); + +# Wait for initial table sync to finish +$node_subscriber->wait_for_subscription_sync($node_publisher, 'test_sub'); + +# The insertion leads to the "relation does not exist" error on the subscriber. +$node_publisher->safe_psql('postgres', "INSERT INTO test_tab VALUES (1)"); + +$node_subscriber->poll_query_until( + 'postgres', + "SELECT subenabled IS FALSE FROM pg_subscription WHERE subname = 'test_sub'"); + +$node_publisher->stop('fast'); +$node_subscriber->stop('fast'); + done_testing();