/*
         * If the joinrel is parallel-safe, we may be able to consider a
-        * partial hash join.  However, the resulting path must not be
-        * parameterized.
+        * partial hash join.
+        *
+        * However, we can't handle JOIN_RIGHT_SEMI, because the hash table is
+        * either a shared hash table or a private hash table per backend.  In
+        * the shared case, there is no concurrency protection for the match
+        * flags, so multiple workers could inspect and set the flags
+        * concurrently, potentially producing incorrect results.  In the
+        * private case, each worker has its own copy of the hash table, so no
+        * single process has all the match flags.
+        *
+        * Also, the resulting path must not be parameterized.
         */
        if (joinrel->consider_parallel &&
+           jointype != JOIN_RIGHT_SEMI &&
            outerrel->partial_pathlist != NIL &&
            bms_is_empty(joinrel->lateral_relids))
        {
             * Normally, given that the joinrel is parallel-safe, the cheapest
             * total inner path will also be parallel-safe, but if not, we'll
             * have to search for the cheapest safe, unparameterized inner
-            * path.  If full, right, right-semi or right-anti join, we can't
-            * use parallelism (building the hash table in each backend)
-            * because no one process has all the match bits.
+            * path.  If full, right, or right-anti join, we can't use
+            * parallelism (building the hash table in each backend) because
+            * no one process has all the match bits.
             */
            if (jointype == JOIN_FULL ||
                jointype == JOIN_RIGHT ||
-               jointype == JOIN_RIGHT_SEMI ||
                jointype == JOIN_RIGHT_ANTI)
                cheapest_safe_inner = NULL;
            else if (cheapest_total_inner->parallel_safe)
 
  3 | 3 | 4 | 4
 (6 rows)
 
+--
+-- regression test for bug with parallel-hash-right-semi join
+--
+begin;
+-- encourage use of parallel plans
+set local parallel_setup_cost=0;
+set local parallel_tuple_cost=0;
+set local min_parallel_table_scan_size=0;
+set local max_parallel_workers_per_gather=4;
+-- ensure we don't get parallel hash right semi join
+explain (costs off)
+select * from tenk1 t1
+where exists (select 1 from tenk1 t2 where fivethous = t1.fivethous)
+and t1.fivethous < 5;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Gather
+   Workers Planned: 4
+   ->  Parallel Hash Semi Join
+         Hash Cond: (t1.fivethous = t2.fivethous)
+         ->  Parallel Seq Scan on tenk1 t1
+               Filter: (fivethous < 5)
+         ->  Parallel Hash
+               ->  Parallel Seq Scan on tenk1 t2
+(8 rows)
+
+rollback;
 --
 -- regression test for bug #13908 (hash join with skew tuples & nbatch increase)
 --
 
             (select t1.a+t3.a from tbl_rs t3) and t2.a < 5)
   on true;
 
+--
+-- regression test for bug with parallel-hash-right-semi join
+--
+
+begin;
+
+-- encourage use of parallel plans
+set local parallel_setup_cost=0;
+set local parallel_tuple_cost=0;
+set local min_parallel_table_scan_size=0;
+set local max_parallel_workers_per_gather=4;
+
+-- ensure we don't get parallel hash right semi join
+explain (costs off)
+select * from tenk1 t1
+where exists (select 1 from tenk1 t2 where fivethous = t1.fivethous)
+and t1.fivethous < 5;
+
+rollback;
+
 --
 -- regression test for bug #13908 (hash join with skew tuples & nbatch increase)
 --