Index: pglogical/expected/row_filter_2.out
===================================================================
--- /dev/null
+++ pglogical/expected/row_filter_2.out
@@ -0,0 +1,744 @@
+-- row based filtering
+SELECT * FROM pglogical_regress_variables()
+\gset
+\c :provider_dsn
+SELECT pglogical.replicate_ddl_command($$
+	CREATE TABLE public.basic_dml (
+		id serial primary key,
+		other integer,
+		data text,
+		"SomeThing" interval,
+		insert_xid bigint DEFAULT txid_current()
+	);
+$$);
+ replicate_ddl_command 
+-----------------------
+ t
+(1 row)
+
+-- used to check if initial copy does row filtering
+\COPY basic_dml(id, other, data, "SomeThing") FROM STDIN WITH CSV
+-- create some functions:
+CREATE FUNCTION funcn_add(integer, integer) RETURNS integer
+    AS 'select $1 + $2;'
+    LANGUAGE SQL
+    IMMUTABLE
+    RETURNS NULL ON NULL INPUT;
+create function funcn_nochange(text) returns text
+  as 'select $1 limit 1' language sql stable;
+create or replace function funcn_get_curr_decade() returns integer as
+$$ (SELECT EXTRACT(DECADE FROM NOW()):: integer); $$
+language sql volatile;
+-- we allow volatile functions, it's user's responsibility to not do writes
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := 'current_user = data');
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+-- fail -- subselect
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := '(SELECT count(*) FROM pg_class) > 1');
+ERROR:  cannot use subquery in check constraint
+-- fail -- SELECT
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := 'SELECT true');
+ERROR:  syntax error at or near "SELECT"
+CONTEXT:  invalid row_filter expression "SELECT true"
+-- fail -- nonexisting column
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := 'foobar');
+ERROR:  column "foobar" does not exist
+-- fail -- not coercable to bool
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := 'data');
+ERROR:  argument of row_filter must be type boolean, not type text
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := $rf$id between 2 AND 4$rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := NULL);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := $rf$id > funcn_add(1,2) $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := $rf$data = funcn_nochange('baz') $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, row_filter := $rf$ other > funcn_get_curr_decade()  $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+-- use this filter for rest of the test
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', true, row_filter := $rf$id > 1 AND data IS DISTINCT FROM 'baz' AND data IS DISTINCT FROM 'bbb'$rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT nspname, relname, set_name FROM pglogical.tables WHERE relname = 'basic_dml';
+ nspname |  relname  | set_name 
+---------+-----------+----------
+ public  | basic_dml | default
+(1 row)
+
+-- fail, the membership in repset depends on data column
+\set VERBOSITY terse
+ALTER TABLE basic_dml DROP COLUMN data;
+ERROR:  cannot drop table basic_dml column data because other objects depend on it
+\set VERBOSITY default
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+-- wait for the initial data to copy
+BEGIN;
+SET LOCAL statement_timeout = '60s';
+SELECT pglogical.wait_for_subscription_sync_complete('test_subscription');
+ wait_for_subscription_sync_complete 
+-------------------------------------
+ 
+(1 row)
+
+COMMIT;
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+  id  | other | data | SomeThing 
+------+-------+------+-----------
+ 5000 |     1 | aaa  | @ 1 hour
+ 5002 |     3 | ccc  | @ 3 mins
+ 5003 |     4 | ddd  | @ 4 days
+(3 rows)
+
+ALTER TABLE public.basic_dml ADD COLUMN subonly integer;
+ALTER TABLE public.basic_dml ADD COLUMN subonly_def integer DEFAULT 99;
+ALTER TABLE public.basic_dml ADD COLUMN subonly_def_ts timestamptz DEFAULT current_timestamp;
+\c :provider_dsn
+TRUNCATE basic_dml;
+-- check basic insert replication
+INSERT INTO basic_dml(other, data, "SomeThing")
+VALUES (5, 'foo', '1 minute'::interval),
+       (4, 'bar', '12 weeks'::interval),
+       (3, 'baz', '2 years 1 hour'::interval),
+       (2, 'qux', '8 months 2 days'::interval),
+       (1, NULL, NULL);
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data |    SomeThing    
+----+-------+------+-----------------
+  2 |     4 | bar  | @ 84 days
+  4 |     2 | qux  | @ 8 mons 2 days
+  5 |     1 |      | 
+(3 rows)
+
+-- update one row
+\c :provider_dsn
+UPDATE basic_dml SET other = '4', data = NULL, "SomeThing" = '3 days'::interval WHERE id = 4;
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data | SomeThing 
+----+-------+------+-----------
+  2 |     4 | bar  | @ 84 days
+  4 |     4 |      | @ 3 days
+  5 |     1 |      | 
+(3 rows)
+
+-- update multiple rows
+\c :provider_dsn
+UPDATE basic_dml SET other = id, data = data || id::text;
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data | SomeThing 
+----+-------+------+-----------
+  2 |     2 | bar2 | @ 84 days
+  4 |     4 |      | @ 3 days
+  5 |     5 |      | 
+(3 rows)
+
+\c :provider_dsn
+UPDATE basic_dml SET other = id, "SomeThing" = "SomeThing" - '10 seconds'::interval WHERE id < 3;
+UPDATE basic_dml SET other = id, "SomeThing" = "SomeThing" + '10 seconds'::interval WHERE id > 3;
+DELETE FROM basic_dml WHERE id = 3;
+INSERT INTO basic_dml VALUES (3, 99, 'bazbaz', '2 years 1 hour'::interval);
+INSERT INTO basic_dml VALUES (7, 100, 'bazbaz', '2 years 1 hour'::interval);
+UPDATE basic_dml SET data = 'baz' WHERE id in (3,7);
+-- This update would be filtered at subscriber
+SELECT id, other, data, "SomeThing" from basic_dml ORDER BY id;
+ id | other | data |     SomeThing      
+----+-------+------+--------------------
+  1 |     1 | foo1 | @ 50 secs
+  2 |     2 | bar2 | @ 84 days -10 secs
+  3 |    99 | baz  | @ 2 years 1 hour
+  4 |     4 |      | @ 3 days 10 secs
+  5 |     5 |      | 
+  7 |   100 | baz  | @ 2 years 1 hour
+(6 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing", subonly, subonly_def FROM basic_dml ORDER BY id;
+ id | other |  data  |     SomeThing      | subonly | subonly_def 
+----+-------+--------+--------------------+---------+-------------
+  2 |     2 | bar2   | @ 84 days -10 secs |         |          99
+  3 |    99 | bazbaz | @ 2 years 1 hour   |         |          99
+  4 |     4 |        | @ 3 days 10 secs   |         |          99
+  5 |     5 |        |                    |         |          99
+  7 |   100 | bazbaz | @ 2 years 1 hour   |         |          99
+(5 rows)
+
+\c :provider_dsn
+UPDATE basic_dml SET data = 'bar' WHERE id = 3;
+-- This update would again start to be received at subscriber
+DELETE FROM basic_dml WHERE data = 'baz';
+-- Delete reaches the subscriber for a filtered row
+INSERT INTO basic_dml VALUES (6, 100, 'baz', '2 years 1 hour'::interval);
+-- insert would be filtered
+SELECT id, other, data, "SomeThing" from basic_dml ORDER BY id;
+ id | other | data |     SomeThing      
+----+-------+------+--------------------
+  1 |     1 | foo1 | @ 50 secs
+  2 |     2 | bar2 | @ 84 days -10 secs
+  3 |    99 | bar  | @ 2 years 1 hour
+  4 |     4 |      | @ 3 days 10 secs
+  5 |     5 |      | 
+  6 |   100 | baz  | @ 2 years 1 hour
+(6 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing", subonly, subonly_def FROM basic_dml ORDER BY id;
+ id | other | data |     SomeThing      | subonly | subonly_def 
+----+-------+------+--------------------+---------+-------------
+  2 |     2 | bar2 | @ 84 days -10 secs |         |          99
+  3 |    99 | bar  | @ 2 years 1 hour   |         |          99
+  4 |     4 |      | @ 3 days 10 secs   |         |          99
+  5 |     5 |      |                    |         |          99
+(4 rows)
+
+\c :provider_dsn
+UPDATE basic_dml SET data = 'bar' WHERE id = 6;
+UPDATE basic_dml SET data = 'abcd' WHERE id = 6;
+-- These updates would continue to be missed on subscriber
+-- as it does not have the primary key
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data |     SomeThing      
+----+-------+------+--------------------
+  2 |     2 | bar2 | @ 84 days -10 secs
+  3 |    99 | bar  | @ 2 years 1 hour
+  4 |     4 |      | @ 3 days 10 secs
+  5 |     5 |      | 
+(4 rows)
+
+-- transaction timestamp should be updated for each row (see #148)
+SELECT count(DISTINCT subonly_def_ts) = count(DISTINCT insert_xid) FROM basic_dml;
+ ?column? 
+----------
+ t
+(1 row)
+
+-- delete multiple rows
+\c :provider_dsn
+DELETE FROM basic_dml WHERE id < 4;
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data |    SomeThing     
+----+-------+------+------------------
+  4 |     4 |      | @ 3 days 10 secs
+  5 |     5 |      | 
+(2 rows)
+
+-- truncate
+\c :provider_dsn
+TRUNCATE basic_dml;
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+ id | other | data | SomeThing 
+----+-------+------+-----------
+(0 rows)
+
+-- copy
+\c :provider_dsn
+\COPY basic_dml(id, other, data, "SomeThing") FROM STDIN WITH CSV
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT id, other, data, "SomeThing" FROM basic_dml ORDER BY id;
+  id  | other | data | SomeThing 
+------+-------+------+-----------
+ 9000 |     1 | aaa  | @ 1 hour
+ 9002 |     3 | ccc  | @ 3 mins
+ 9003 |     4 | ddd  | @ 4 days
+(3 rows)
+
+\c :provider_dsn
+SELECT pglogical.replicate_ddl_command($$
+	CREATE TABLE public.test_jsonb (
+		json_type text primary key,
+		test_json jsonb
+	);
+$$);
+ replicate_ddl_command 
+-----------------------
+ t
+(1 row)
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five", [1,2,3],{"f1":9}]'),
+('object','{"field1":"val1","field2":"val2","field3":null, "field4": 4, "field5": [1,2,3], "field6": {"f1":9}}');
+SELECT * FROM pglogical.replication_set_add_table('default', 'test_jsonb', true, row_filter := $rf$(test_json ->> 'field2') IS DISTINCT FROM 'val2' $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+DO $$
+BEGIN
+    FOR i IN 1..100 LOOP
+        IF NOT EXISTS (SELECT 1 FROM pglogical.local_sync_status WHERE sync_status != 'r') THEN
+            EXIT;
+        END IF;
+        PERFORM pg_sleep(0.1);
+    END LOOP;
+END;$$;
+SELECT * FROM test_jsonb ORDER BY json_type;
+ json_type |                             test_json                              
+-----------+--------------------------------------------------------------------
+ array     | ["zero", "one", "two", null, "four", "five", [1, 2, 3], {"f1": 9}]
+ scalar    | "a scalar"
+(2 rows)
+
+\c :provider_dsn
+-- Filter may refer to not-replicated columns
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false, columns := ARRAY['id', 'data'], row_filter := $rf$other = 2$rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+INSERT INTO basic_dml(other, data, "SomeThing") VALUES (2, 'itstwo', '1 second'::interval);
+SELECT other, data, "SomeThing" FROM basic_dml WHERE data = 'itstwo';
+ other |  data  | SomeThing 
+-------+--------+-----------
+     2 | itstwo | @ 1 sec
+(1 row)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+-- 'other' will be NULL as it wasn't in the repset
+-- even though we filtered on it. So will SomeThing.
+SELECT other, data, "SomeThing" FROM basic_dml WHERE data = 'itstwo';
+ other |  data  | SomeThing 
+-------+--------+-----------
+       | itstwo | 
+(1 row)
+
+\c :provider_dsn
+---------------------------------------------------
+-- Enhanced function tests covering basic plpgsql
+---------------------------------------------------
+CREATE FUNCTION func_plpgsql_simple(arg integer)
+RETURNS integer
+LANGUAGE plpgsql
+AS $$
+BEGIN
+  RETURN arg;
+END;
+$$;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ func_plpgsql_simple(other) = 100 $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+-- Should FAIL due to dependency
+--
+-- FIXME: Succeeds incorrectly (RM#5880) leading to
+--     cache lookup failed for function" errors in logs if allowed to commit
+--
+BEGIN;
+DROP FUNCTION func_plpgsql_simple(integer);
+ROLLBACK;
+INSERT INTO basic_dml (other) VALUES (100), (101);
+SELECT other FROM basic_dml WHERE other IN (100,101);
+ other 
+-------
+   100
+   101
+(2 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT other FROM basic_dml WHERE other IN (100,101);
+ other 
+-------
+   100
+(1 row)
+
+\c :provider_dsn
+CREATE FUNCTION func_plpgsql_logic(arg integer)
+RETURNS integer
+LANGUAGE plpgsql
+AS $$
+BEGIN
+  IF arg = 200 THEN
+    RETURN arg;
+  ELSE
+    RETURN 0;
+  END IF;
+END;
+$$;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ func_plpgsql_logic(other) = other $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+INSERT INTO basic_dml (other) VALUES (200), (201);
+SELECT other FROM basic_dml WHERE other IN (200,201);
+ other 
+-------
+   200
+   201
+(2 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT other FROM basic_dml WHERE other IN (200,201);
+ other 
+-------
+   200
+(1 row)
+
+\c :provider_dsn
+CREATE FUNCTION func_plpgsql_security_definer(arg integer)
+RETURNS integer
+LANGUAGE plpgsql
+SECURITY DEFINER
+AS $$
+BEGIN
+  RAISE NOTICE 'c_u: %, s_u: %', current_user, session_user;
+  RETURN arg;
+END;
+$$;
+CREATE ROLE temp_owner;
+ALTER FUNCTION func_plpgsql_security_definer(integer) OWNER TO temp_owner;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ func_plpgsql_security_definer(other) = 300 $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+INSERT INTO basic_dml (other) VALUES (300), (301);
+SELECT other FROM basic_dml WHERE other IN (300,301);
+ other 
+-------
+   300
+   301
+(2 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT other FROM basic_dml WHERE other IN (300,301);
+ other 
+-------
+   300
+(1 row)
+
+\c :provider_dsn
+CREATE FUNCTION func_plpgsql_exception(arg integer)
+RETURNS integer
+LANGUAGE plpgsql
+AS $$
+BEGIN
+  BEGIN
+    SELECT arg/0;
+  EXCEPTION
+    WHEN division_by_zero THEN
+      RETURN arg;
+  END;
+  RAISE EXCEPTION 'should be unreachable';
+END;
+$$;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ func_plpgsql_exception(other) = 400 $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+INSERT INTO basic_dml (other) VALUES (400), (401);
+SELECT other FROM basic_dml WHERE other IN (400,401);
+ other 
+-------
+   400
+   401
+(2 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT other FROM basic_dml WHERE other IN (400,401);
+ other 
+-------
+   400
+(1 row)
+
+\c :provider_dsn
+-- Should not be able to use a SETOF or TABLE func directly
+-- but we can do it via a wrapper:
+CREATE FUNCTION func_plpgsql_srf_retq(arg integer)
+RETURNS TABLE (result integer, dummy boolean)
+LANGUAGE plpgsql
+SECURITY DEFINER
+AS $$
+BEGIN
+  RETURN QUERY SELECT arg * x, true FROM generate_series(1,2) x;
+  RETURN;
+END;
+$$;
+-- fails with SRF context error
+BEGIN;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ (func_plpgsql_srf_retq(other)).result = 500 $rf$);
+ERROR:  argument of row_filter must not return a set
+ROLLBACK;
+CREATE FUNCTION func_plpgsql_call_set(arg integer)
+RETURNS boolean
+LANGUAGE plpgsql
+AS $$
+BEGIN
+  RETURN (SELECT true FROM func_plpgsql_srf_retq(arg) WHERE result = arg * 2);
+END;
+$$;
+SELECT * FROM pglogical.replication_set_remove_table('default', 'basic_dml');
+ replication_set_remove_table 
+------------------------------
+ t
+(1 row)
+
+SELECT * FROM pglogical.replication_set_add_table('default', 'basic_dml', false,
+	row_filter := $rf$ func_plpgsql_call_set(other) $rf$);
+ replication_set_add_table 
+---------------------------
+ t
+(1 row)
+
+INSERT INTO basic_dml (other) VALUES (500), (501);
+SELECT other FROM basic_dml WHERE other IN (500,501);
+ other 
+-------
+   500
+   501
+(2 rows)
+
+SELECT pglogical.wait_slot_confirm_lsn(NULL, NULL);
+ wait_slot_confirm_lsn 
+-----------------------
+ 
+(1 row)
+
+\c :subscriber_dsn
+SELECT other FROM basic_dml WHERE other IN (500,501);
+ other 
+-------
+   500
+   501
+(2 rows)
+
+\c :provider_dsn
+DROP FUNCTION func_plpgsql_simple(integer);
+DROP FUNCTION func_plpgsql_logic(integer);
+DROP FUNCTION func_plpgsql_security_definer(integer);
+DROP FUNCTION func_plpgsql_exception(integer);
+DROP FUNCTION func_plpgsql_srf_retq(integer);
+DROP FUNCTION func_plpgsql_call_set(integer);
+DROP ROLE temp_owner;
+---------------------------------------------------
+-- ^^^ End plpgsql tests
+---------------------------------------------------
+\c :provider_dsn
+\set VERBOSITY terse
+DROP FUNCTION funcn_add(integer, integer);
+DROP FUNCTION funcn_nochange(text);
+DROP FUNCTION funcn_get_curr_decade();
+SELECT pglogical.replicate_ddl_command($$
+	DROP TABLE public.basic_dml CASCADE;
+	DROP TABLE public.test_jsonb CASCADE;
+$$);
+NOTICE:  drop cascades to table public.basic_dml membership in replication set default
+NOTICE:  drop cascades to table public.test_jsonb membership in replication set default
+ replicate_ddl_command 
+-----------------------
+ t
+(1 row)
+