fix_assertion_error.patch

application/octet-stream

Filename: fix_assertion_error.patch
Type: application/octet-stream
Part: 0
Message: Re: [BUG] Crash of logical replica with trigger.

Patch

Format: unified
File+
src/backend/catalog/pg_proc.c 9 6
src/test/subscription/t/100_bugs.pl 58 0
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();