Skip to content

Conversation

@avamingli
Copy link
Contributor

@avamingli avamingli commented Jul 24, 2023

I have a fix for GPDB https://github.com/greenplum-db/gpdb/pull/15862 since June 19, the issue #51 exists for CBDB.
Check back to CBDB since we are open source and welcome for comments.
Most codes are same with some refinement and conflicts resolved, as we have parallel feature and change functions like cbdpath_motion_for_join().

context

Replicated locus could happen besides we Broadcast a node.
Ex: Writeable CTE on replicated table join with others.
See issue: https://github.com/greenplum-db/gpdb/issues/15860.
And all cased added in rpt_joins will cause such a failure because all that are blocked by

	if (CdbPathLocus_IsReplicated(outer.locus) ||
		CdbPathLocus_IsReplicated(inner.locus))
		goto fail;

And while I open it, there are more issues than https://github.com/greenplum-db/gpdb/issues/15860 , we never expect to handle Replicated locus could join with others.
The pr enables all of that except OuterQuery locus.

The principle to decide join locus of Replicated with others is:
the slice does the writeable operation on replicated table
must be executed on all segments.
To be more specific, ex: Replicated join SegmentGeneral should return Replicated locus.
If return SegmentGeneral, we will allocate gang on only one segment which will insert/update/delete on only segment0 for replicated tabes. That's wrong.
Before issue 15860, we have the logic return SegmentGeneral in cdbpathlocus_join(). As we have blocked Replicated locus
in cdbpath_motion_for_join(), the only scenario is update a replicated table from values of another replicated table whose num segment is less.
in theses codes:

			if (root->upd_del_replicated_table > 0)
			{
				if ((CdbPathLocus_NumSegments(outer.locus) >
					 CdbPathLocus_NumSegments(inner.locus)) &&
					bms_is_member(root->upd_del_replicated_table,
								  outer.path->parent->relids))
				{
					/*
					 * the updated resultrelation is replicated table
					 * and its numsegments is larger, we should broadcast
					 * the other path
					 */
					if (!inner.ok_to_replicate)
						goto fail;

					/*
					 * FIXME: do we need to test inner's movable?
					 */
					CdbPathLocus_MakeReplicated(&inner.move_to,
												CdbPathLocus_NumSegments(outer.locus));
    explain update d2 a set c3=b.c3 from d1 b returning *;
                                                QUERY PLAN                                                
----------------------------------------------------------------------------------------------------------
 Explicit Gather Motion 2:1  (slice1; segments: 2)  (cost=10000000002.06..10000000002.06 rows=4 width=44)
   ->  Update on d2 a  (cost=10000000000.00..10000000002.06 rows=2 width=44)
         ->  Nested Loop  (cost=10000000000.00..10000000002.06 rows=2 width=44)
               ->  Seq Scan on d2 a  (cost=0.00..1.00 rows=1 width=22)
               ->  Materialize  (cost=0.00..1.04 rows=1 width=22)
                     ->  Broadcast Motion 1:2  (slice2; segments: 1)  (cost=0.00..1.03 rows=2 width=22)
                           ->  Seq Scan on d1 b  (cost=0.00..1.00 rows=1 width=22)
 Optimizer: Postgres query optimizer
(8 rows)

Wile broadcasting the d1 whose num segments is less than d2, and we return SegmentGeneral locus for the join node,
yeah, GDB it and found that the Nested Loop node locus is SegmentGeneral. That's wrong as we mentioned: allocate gang only on one segment.
I was curious why the plan is finally right until I found that adjust_modifytable_subpaths() reset the Nested Loop node locus
to Replicated locus.
That's a little hack and wried, and not work for issue 15860 and cases added in this mr.
I didn't see any good reason why Replicated join SegmentGeneral should return SegmentGeneral and force set it back to Replicated finally. And that's wrong for writeable on replicated tables from values or a non-segmentgeneral table(hashed for example)

See examples below.

DDL for examples

xxx_2_segments means define table on 2 segments of a 3 segments cluster.

select gp_debug_set_create_table_default_numsegments(2);
create table rpt_issue_15860_2_segments(c1 int, c2 int) distributed replicated;
create table hash_issue_15860_2_segments(c1 int, c2 int) distributed by (c1);
select gp_debug_reset_create_table_default_numsegments();
create table rpt_issue_15860 (c1 int, c2 int) distributed replicated;
create table rpt2_issue_15860 (c1 int, c2 int) distributed replicated;
create table hash_issue_15860(c1 int, c2 int) distributed by (c1);
create table strewn_issue_15860(c1 int, c2 int) distributed randomly;

Replicated join SegmentGeneral.

-- Replicated join SegmentGeneral.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join rpt2_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Explicit Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: (rpt2_issue_15860.c1 = rpt_issue_15860.c1)
         ->  Seq Scan on rpt2_issue_15860
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

When locus is not ok to replicate, ex: as the nullable side of outer join, we must gather them to single.

-- Replicated join SegmentGeneral, Replicated is not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 left join rpt2_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Right Join
   Hash Cond: (rpt2_issue_15860.c1 = rpt_issue_15860.c1)
   ->  Gather Motion 1:1  (slice1; segments: 1)
         ->  Seq Scan on rpt2_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

-- Replicated join SegmentGeneral, SegmentGeneral is not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 right join rpt2_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Left Join
   Hash Cond: (rpt2_issue_15860.c1 = cte1.c1)
   ->  Gather Motion 1:1  (slice1; segments: 1)
         ->  Seq Scan on rpt2_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Subquery Scan on cte1
                     ->  Insert on rpt_issue_15860
                           ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

-- Replicated join SegmentGeneral, both are not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 full join rpt2_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Full Join
   Hash Cond: (rpt2_issue_15860.c1 = rpt_issue_15860.c1)
   ->  Gather Motion 1:1  (slice1; segments: 1)
         ->  Seq Scan on rpt2_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

While SegmentGeneral's num segments are different with Replicated locus, we have two ways:

  1. Gather them to SingleQE.
  2. Broadcast SegmentGeneral.
    It seems both are ok, but Broadcast to all segments will cost more. And as we will need a Gather Motion or Explicit Gather Motion finally, we should make as less data as possible from the end to top for planner.
    Maybe we should estimate rel size of both, but it's hard to tell size of a returning statement.
    table rpt_issue_15860_2_segments is defined with 2 segments of a 3 segments cluster.
-- Replicated join SegmentGeneral, num segments are not matched.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join rpt_issue_15860_2_segments using(c1);
                            QUERY PLAN                             
-------------------------------------------------------------------
 Hash Join
   Hash Cond: (rpt_issue_15860_2_segments.c1 = rpt_issue_15860.c1)
   ->  Gather Motion 1:1  (slice1; segments: 1)
         ->  Seq Scan on rpt_issue_15860_2_segments
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

Replicated join General.

Similar to SegmentGeneral, but as General could be on QD, and we must guarantee the Principle above to insert/update/delete on all segments for replicated table.

-- Replicated join General.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join generate_series(1, 5) i on i= cte1.c1 ;
                    QUERY PLAN                     
---------------------------------------------------
 Explicit Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: (i.i = rpt_issue_15860.c1)
         ->  Function Scan on generate_series i
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

-- Replicated join General, Replicated is not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 left join generate_series(1, 5) i on i= cte1.c1 ;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Right Join
   Hash Cond: (i.i = rpt_issue_15860.c1)
   ->  Function Scan on generate_series i
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice1; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

-- Replicated join General, General is not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 right join generate_series(1, 5) i on i= cte1.c1 ;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Left Join
   Hash Cond: (i.i = rpt_issue_15860.c1)
   ->  Function Scan on generate_series i
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice1; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

-- Replicated join General, both are not not ok to replicate.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 full join generate_series(1, 5) i on i= cte1.c1 ;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Full Join
   Hash Cond: (i.i = rpt_issue_15860.c1)
   ->  Function Scan on generate_series i
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice1; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

Replicate join SingleQE.

-- Replicate join SingleQE.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join (select count(*) as c from hash_issue_15860) a on a.c = cte1.c1;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Join
   Hash Cond: ((count(*)) = rpt_issue_15860.c1)
   ->  Aggregate
         ->  Gather Motion 3:1  (slice1; segments: 3)
               ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 left join (select count(*) as c from hash_issue_15860) a on a.c = cte1.c1;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Right Join
   Hash Cond: ((count(*)) = rpt_issue_15860.c1)
   ->  Aggregate
         ->  Gather Motion 3:1  (slice1; segments: 3)
               ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 right join (select count(*) as c from hash_issue_15860) a on a.c = cte1.c1;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Left Join
   Hash Cond: ((count(*)) = rpt_issue_15860.c1)
   ->  Aggregate
         ->  Gather Motion 3:1  (slice1; segments: 3)
               ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 full join (select count(*) as c from hash_issue_15860) a on a.c = cte1.c1;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Full Join
   Hash Cond: ((count(*)) = rpt_issue_15860.c1)
   ->  Aggregate
         ->  Gather Motion 3:1  (slice1; segments: 3)
               ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

Replicate join Entry

Similar to SingleQE, and guarantee the Principle too, that is to avoid to be Motion Elided early, see codes for details.

-- Replicate join Entry.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join gp_segment_configuration g on g.dbid = cte1.c1;
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Join
   Hash Cond: (g.dbid = rpt_issue_15860.c1)
   ->  Seq Scan on gp_segment_configuration g
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice1; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

Replicated join Partitioned

Hashed/Strewn/HashedOJ handle is similar.

-- Replicated join Hashed.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join hash_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: (hash_issue_15860.c1 = rpt_issue_15860.c1)
         ->  Seq Scan on hash_issue_15860
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 right join hash_issue_15860 using(c1);
                     QUERY PLAN                     
----------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Left Join
         Hash Cond: (hash_issue_15860.c1 = cte1.c1)
         ->  Seq Scan on hash_issue_15860
         ->  Hash
               ->  Subquery Scan on cte1
                     ->  Insert on rpt_issue_15860
                           ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

-- Replicated join Hashed, Replicated is not ok to replicate
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 left join hash_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Right Join
   Hash Cond: (hash_issue_15860.c1 = rpt_issue_15860.c1)
   ->  Gather Motion 3:1  (slice1; segments: 3)
         ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 full join hash_issue_15860 using(c1);
                          QUERY PLAN                           
---------------------------------------------------------------
 Hash Full Join
   Hash Cond: (hash_issue_15860.c1 = rpt_issue_15860.c1)
   ->  Gather Motion 3:1  (slice1; segments: 3)
         ->  Seq Scan on hash_issue_15860
   ->  Hash
         ->  Explicit Gather Motion 3:1  (slice2; segments: 3)
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(9 rows)

-- Replicated join Strewn = Strewn.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join strewn_issue_15860 using(c1);
                           QUERY PLAN                            
-----------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: (strewn_issue_15860.c1 = rpt_issue_15860.c1)
         ->  Seq Scan on strewn_issue_15860
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(8 rows)

-- Replicated join HashedOJ = HashedOJ
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join (select * from hash_issue_15860 a full join hash_issue_15860 b using(c1)) c using(c1);
                            QUERY PLAN                            
------------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: ((COALESCE(a.c1, b.c1)) = rpt_issue_15860.c1)
         ->  Hash Full Join
               Hash Cond: (a.c1 = b.c1)
               ->  Seq Scan on hash_issue_15860 a
               ->  Hash
                     ->  Seq Scan on hash_issue_15860 b
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(12 rows)

When num segments are not match for Partitioned locus, we redistribute it to num segments of Replicated.

-- Replicated join Hashed, num segments are not match.
explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select * from cte1 join hash_issue_15860_2_segments  using(c1);
                                QUERY PLAN                                
--------------------------------------------------------------------------
 Gather Motion 3:1  (slice1; segments: 3)
   ->  Hash Join
         Hash Cond: (hash_issue_15860_2_segments.c1 = rpt_issue_15860.c1)
         ->  Redistribute Motion 2:3  (slice2; segments: 2)
               Hash Key: hash_issue_15860_2_segments.c1
               ->  Seq Scan on hash_issue_15860_2_segments
         ->  Hash
               ->  Insert on rpt_issue_15860
                     ->  Result
 Optimizer: Postgres query optimizer
(10 rows)

Replicated join OuterQuery.

When Replicated join OuterQuery, make Replicated to OuterQuery locus may be wrong.
OuterQuery will be finally be Broadcast or Gathered.
If it's Gathered, we will insert/update/delete only on one segment for a replicated table, that's not right.
I don't have a good idea to handle OuterQuery, left a FIXME and created the below example.

explain(costs off) with cte1 as (insert into rpt_issue_15860 values (1, 2) returning *) select ( select foo.c1 from (select * from strewn_issue_15860) foo join cte1  using(c2)  where foo.c1 = hash_issue_15860.c1) from hash_issue_15860;
ERROR:  could not devise a query plan for the given query (pathnode.c:275)

Resolve GPDB_96_MERGE_FIXME in adjust_modifytable_subpaths()

And with this change, a GPDB_96_MERGE_FIXME left by Heikki in adjust_modifytable_subpaths () can be resolved.

         * GPDB_96_MERGE_FIXME: it might with e.g. a INSERT RETURNING in a CTE
	 * I tried here, the locus setting is quite simple, but failed if it's not
	 * in a CTE and the locus is General. Haven't figured out how to create
	 * flow in that case.
	 * Example:
	 * CREATE TABLE cte_returning_locus(c1 int) DISTRIBUTED BY (c1);
	 * COPY cte_returning_locus FROM PROGRAM 'seq 1 100';
	 * EXPLAIN WITH aa AS (
	 *        INSERT INTO cte_returning_locus SELECT generate_series(3,300) RETURNING c1
	 * )
	 * SELECT count(*) FROM aa,cte_returning_locus WHERE aa.c1 = cte_returning_locus.c1;
	 *

closes: #51


Change logs

Describe your change clearly, including what problem is being solved or what feature is being added.

If it has some breaking backward or forward compatibility, please clary.

Why are the changes needed?

Describe why the changes are necessary.

Does this PR introduce any user-facing change?

If yes, please clarify the previous behavior and the change this PR proposes.

How was this patch tested?

Please detail how the changes were tested, including manual tests and any relevant unit or integration tests.

Contributor's Checklist

Here are some reminders before you submit the pull request:

  • Document changes
  • Communicate in the GitHub Issues or Discussions (list them if needed)
  • Add tests for the change
  • Pass make installcheck
  • Pass make -C src/test installcheck-cbdb-parallel

@avamingli avamingli force-pushed the make_replicated_locus_join branch from 8b42508 to f2b043f Compare July 26, 2023 03:05
@avamingli avamingli marked this pull request as draft July 26, 2023 03:09
@avamingli avamingli force-pushed the make_replicated_locus_join branch from f2b043f to fdb7c55 Compare July 27, 2023 03:25
@avamingli avamingli marked this pull request as ready for review July 27, 2023 05:31
@avamingli avamingli force-pushed the make_replicated_locus_join branch from fdb7c55 to a46943a Compare July 30, 2023 10:27
@avamingli avamingli self-assigned this Aug 1, 2023
@my-ship-it my-ship-it requested a review from yjhjstz August 31, 2023 01:20
@avamingli avamingli force-pushed the make_replicated_locus_join branch from a46943a to 46ed7d1 Compare October 8, 2023 09:02
Replicated locus could happen besides we Broadcast a node.
Ex: Writeable CTE on replicated table join with others.
See issue: https://github.com/greenplum-db/gpdb/issues/15860.

The principle to decide join locus of Replicated with others is:
the slice does the writeable operation on replicated table
must be executed on all segments.

Replicated locus join SegmentGeneral locus.
If either is not ok to replicate(outer join) or the num segments
of SegmentGeneral is less than Replicated, Gather them to SingleQE.
We need a Explicit Gather Motion to operate on replicated tables
and Gather SegmentGeneral too, avoiding to be elided to Entry locus.
Else return Replicated locus.

Replicated locus join Partitioned locus.
If Replicated is not ok to replicate, gather them to SingleQE.
If num segments of Partitioned(M) is not equal to Replicated(N), redistribute
Paritioned(M) to Paritioned(N).
Else return Partitioned locus.

Replicated locus join General locus.
If either of Replicated and General is not ok to replicate,
gather them to SingleQE.
Else return Replicated locus.

Replicated locus join SingleQE locus.
Bring Replicated(Explicit Gather Motion) to SingleQE for join.

Replicated locus join Entry locus.
Bring Replicated(Explicit Gather Motion) to SingleQE for join.
We should return SingleQE here to guarantee not to be elided to Entry loucs early.
Let cdbpathlocus_join() do it after Motion added.

Replicated locus join OuterQuery locus.
Make Replicated to OuterQuery locus may be wrong.
OuterQuery will be finally be Broadcast or Gathered to single.
If it's Gathered, we will insert/update/delete only on one segment for a raplicated table, that's not right.
It may also be wrong if Broadcast, we don't have such a Motion for that.And it causes an error.
Left a FXIME for this case.

Authored-by: Zhang Mingli [email protected]
@avamingli avamingli force-pushed the make_replicated_locus_join branch from 46ed7d1 to 6913f4e Compare October 12, 2023 01:48
@avamingli avamingli requested a review from foreyes October 16, 2023 03:54
Copy link
Contributor

@my-ship-it my-ship-it left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@avamingli avamingli merged commit fed40c7 into apache:main Oct 17, 2023
@avamingli avamingli deleted the make_replicated_locus_join branch October 17, 2023 02:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Writeable CTE on replicated table join with others crash.

3 participants