From f0bc7c5c336d8082fc680d6fbd5aade485dcd278 Mon Sep 17 00:00:00 2001 From: tnayak Date: Mon, 13 Nov 2023 11:48:14 -0800 Subject: [PATCH] [BACKPORT 2.18.5] [#19642, #19946] YSQL: Refactor batched/unbatched path param requirement computation Summary: Original commit: 8ac82f424701adecbf5a2537e7ca95eff4126dae / D30216 PG computes the required parameters of a path via the macro PATH_REQ_OUTER to read the param_info->ppi_req_outer field of a path. This change refactors reads to param_info->yb_ppi_req_outer_batched to be computed by the macro YB_PATH_REQ_OUTER_BATCHED. This change also gets rid of the field yb_ppi_req_outer_unbatched in ParamPathInfo and computes unbatched relids via the difference between the total required params of a path and its batched params. Doing so simplifies logic and fixes the following bug: Consider a Nested Loop join between B and C where B is parameterized by A but A is not a batched parameter. If the access path on B was not generated by yb_get_batched_index_paths, the yb_ppi_req_outer_unbatched field of B's param_info would not record A as one might expect. As a result, the param_info of the NestedLoop join would fail to register in its yb_ppi_req_outer_unbatched field that A needs to not be a batched parameter. With this change, we release the need to explicitly keep track of what relations cannot be batched parameters. As long as the set of batchable parameters is accurately maintained, we can easily determine its complement. NOTE: Original commit had tests that involved the usage of YbBatchedNL/NoYbBatchedNL hints that are unavailable in 2.18.5. Those tests have been replaced in this backport. Jira: DB-8424, DB-8910 Test Plan: Jenkins: Urgent ./yb_build.sh --java-test 'org.yb.pgsql.TestPgRegressJoin' Reviewers: mtakahara Reviewed By: mtakahara Subscribers: yql Tags: #jenkins-ready Differential Revision: https://phorge.dev.yugabyte.com/D31396 --- .../src/backend/optimizer/path/allpaths.c | 4 + .../src/backend/optimizer/path/indxpath.c | 3 - .../src/backend/optimizer/path/joinpath.c | 57 +++------ .../src/backend/optimizer/plan/createplan.c | 13 ++- .../src/backend/optimizer/util/pathnode.c | 22 ++-- .../src/backend/optimizer/util/relnode.c | 110 +++++------------- src/postgres/src/include/nodes/relation.h | 12 +- src/postgres/src/include/optimizer/pathnode.h | 6 +- .../regress/expected/yb_join_batching.out | 46 ++++++++ .../src/test/regress/sql/yb_join_batching.sql | 32 +++++ 10 files changed, 161 insertions(+), 144 deletions(-) diff --git a/src/postgres/src/backend/optimizer/path/allpaths.c b/src/postgres/src/backend/optimizer/path/allpaths.c index d3ce4d545918..b8453d5032e4 100644 --- a/src/postgres/src/backend/optimizer/path/allpaths.c +++ b/src/postgres/src/backend/optimizer/path/allpaths.c @@ -1848,11 +1848,15 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, accumulate_append_subpath(subpath, &subpaths, NULL); } + subpaths_valid &= yb_has_same_batching_reqs(subpaths); + if (subpaths_valid) add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, required_outer, 0, false, partitioned_rels, -1)); + else + break; } } diff --git a/src/postgres/src/backend/optimizer/path/indxpath.c b/src/postgres/src/backend/optimizer/path/indxpath.c index 695d092c9ac5..285e2e19fcd6 100644 --- a/src/postgres/src/backend/optimizer/path/indxpath.c +++ b/src/postgres/src/backend/optimizer/path/indxpath.c @@ -716,9 +716,7 @@ yb_get_batched_index_paths(PlannerInfo *root, RelOptInfo *rel, batchedrelids = bms_difference(batchedrelids, unbatchablerelids); Assert(!root->yb_cur_batched_relids); - Assert(!root->yb_cur_unbatched_relids); root->yb_cur_batched_relids = batchedrelids; - root->yb_cur_unbatched_relids = unbatchablerelids; /* * Build simple index paths using the clauses. Allow ScalarArrayOpExpr @@ -775,7 +773,6 @@ yb_get_batched_index_paths(PlannerInfo *root, RelOptInfo *rel, } root->yb_cur_batched_relids = NULL; - root->yb_cur_unbatched_relids = NULL; } /* diff --git a/src/postgres/src/backend/optimizer/path/joinpath.c b/src/postgres/src/backend/optimizer/path/joinpath.c index 7336bfdcc469..98ec1369d750 100644 --- a/src/postgres/src/backend/optimizer/path/joinpath.c +++ b/src/postgres/src/backend/optimizer/path/joinpath.c @@ -451,17 +451,16 @@ try_nestloop_path(PlannerInfo *root, } } - if (inner_path->param_info && - inner_path->param_info->yb_ppi_req_outer_batched) + if (IsYugaByteEnabled() && YB_PATH_NEEDS_BATCHED_RELS(inner_path)) { if (yb_has_non_evaluable_bnl_clauses(outer_path, - inner_path, - extra->restrictlist) || - (yb_has_non_evaluable_bnl_clauses(outer_path, - inner_path, - inner_path->param_info - ->ppi_clauses))) + inner_path, + extra->restrictlist) || + (yb_has_non_evaluable_bnl_clauses(outer_path, + inner_path, + inner_path->param_info + ->ppi_clauses))) { bms_free(required_outer); return; @@ -1328,52 +1327,30 @@ generate_mergejoin_paths(PlannerInfo *root, Relids yb_get_batched_relids(NestPath *nest) { - ParamPathInfo *innerppi = nest->innerjoinpath->param_info; - ParamPathInfo *outerppi = nest->outerjoinpath->param_info; + Relids inner_batched = YB_PATH_REQ_OUTER_BATCHED(nest->innerjoinpath); - Relids outer_unbatched = - outerppi ? outerppi->yb_ppi_req_outer_unbatched : NULL; - Relids inner_batched = innerppi ? innerppi->yb_ppi_req_outer_batched : NULL; - - /* Rels not in this join that can't be batched. */ - Relids param_unbatched = - nest->path.param_info ? - nest->path.param_info->yb_ppi_req_outer_unbatched : NULL; - - return bms_difference(bms_difference(inner_batched, outer_unbatched), - param_unbatched); + Relids outerrels = nest->outerjoinpath->parent->relids; + return bms_intersect(inner_batched, outerrels); } Relids yb_get_unbatched_relids(NestPath *nest) { - ParamPathInfo *innerppi = nest->innerjoinpath->param_info; - ParamPathInfo *outerppi = nest->outerjoinpath->param_info; - - Relids outer_unbatched = - outerppi ? outerppi->yb_ppi_req_outer_unbatched : NULL; - Relids inner_unbatched = - innerppi ? innerppi->yb_ppi_req_outer_unbatched : NULL; + Relids outer_unbatched = YB_PATH_REQ_OUTER_UNBATCHED(nest->outerjoinpath); + Relids inner_unbatched = YB_PATH_REQ_OUTER_UNBATCHED(nest->innerjoinpath); /* Rels not in this join that can't be batched. */ - Relids param_unbatched = - nest->path.param_info ? - nest->path.param_info->yb_ppi_req_outer_unbatched : NULL; + Relids param_unbatched = YB_PATH_REQ_OUTER_UNBATCHED(&nest->path); return bms_union(outer_unbatched, - bms_union(inner_unbatched, param_unbatched)); + bms_union(inner_unbatched, param_unbatched)); } bool yb_is_outer_inner_batched(Path *outer, Path *inner) { - ParamPathInfo *innerppi = inner->param_info; - ParamPathInfo *outerppi = outer->param_info; - - Relids outer_unbatched = - outerppi ? outerppi->yb_ppi_req_outer_unbatched : NULL; - Relids inner_batched = - innerppi ? innerppi->yb_ppi_req_outer_batched : NULL; + Relids outer_unbatched = YB_PATH_REQ_OUTER_UNBATCHED(outer); + Relids inner_batched = YB_PATH_REQ_OUTER_BATCHED(inner); return bms_overlap(outer->parent->relids, bms_difference(inner_batched, outer_unbatched)); @@ -2127,7 +2104,7 @@ yb_has_non_evaluable_bnl_clauses(Path *outer_path, Path *inner_path, List *rinfos) { ListCell *lc; - Relids req_batched_rels = inner_path->param_info->yb_ppi_req_outer_batched; + Relids req_batched_rels = YB_PATH_REQ_OUTER_BATCHED(inner_path); Relids outer_relids = outer_path->parent->relids; Relids inner_relids = inner_path->parent->relids; diff --git a/src/postgres/src/backend/optimizer/plan/createplan.c b/src/postgres/src/backend/optimizer/plan/createplan.c index 02faed4b974a..7894eb3784ec 100644 --- a/src/postgres/src/backend/optimizer/plan/createplan.c +++ b/src/postgres/src/backend/optimizer/plan/createplan.c @@ -4871,6 +4871,11 @@ create_nestloop_plan(PlannerInfo *root, if (yb_is_batched) { + /* No rels supplied to inner from outer should be unbatched. */ + Relids inner_unbatched = + YB_PATH_REQ_OUTER_UNBATCHED(best_path->innerjoinpath); + Assert(!bms_overlap(inner_unbatched, outerrelids)); + (void)inner_unbatched; /* Add the available batched outer rels. */ root->yb_availBatchedRelids = lcons(outerrelids, root->yb_availBatchedRelids); @@ -4906,14 +4911,14 @@ create_nestloop_plan(PlannerInfo *root, } if (rinfo->can_join && - OidIsValid(rinfo->hashjoinoperator) && - yb_can_batch_rinfo(rinfo, batched_outerrelids, inner_relids)) + OidIsValid(rinfo->hashjoinoperator) && + yb_can_batch_rinfo(rinfo, batched_outerrelids, inner_relids)) { /* if nlhash can process this */ Assert(is_opclause(rinfo->clause)); RestrictInfo *batched_rinfo = - yb_get_batched_restrictinfo(rinfo,batched_outerrelids, - inner_relids); + yb_get_batched_restrictinfo(rinfo, batched_outerrelids, + inner_relids); hashOpno = ((OpExpr *) batched_rinfo->clause)->opno; } diff --git a/src/postgres/src/backend/optimizer/util/pathnode.c b/src/postgres/src/backend/optimizer/util/pathnode.c index ab9d6fd09ac3..164ba3504615 100644 --- a/src/postgres/src/backend/optimizer/util/pathnode.c +++ b/src/postgres/src/backend/optimizer/util/pathnode.c @@ -1295,14 +1295,14 @@ create_append_path(PlannerInfo *root, if (partitioned_rels != NIL && root && rel->reloptkind == RELOPT_BASEREL) { /* YB: Accumulate batching info from subpaths for this "baserel". */ - Assert(root->yb_cur_batched_relids == NULL); - yb_accumulate_batching_info(subpaths, - &root->yb_cur_batched_relids, &root->yb_cur_unbatched_relids); + Assert(yb_has_same_batching_reqs(subpaths)); + + root->yb_cur_batched_relids = + YB_PATH_REQ_OUTER_BATCHED((Path *) linitial(subpaths)); pathnode->path.param_info = get_baserel_parampathinfo(root, rel, required_outer); root->yb_cur_batched_relids = NULL; - root->yb_cur_unbatched_relids = NULL; } else pathnode->path.param_info = get_appendrel_parampathinfo(rel, @@ -2263,17 +2263,13 @@ create_nestloop_path(PlannerInfo *root, * because the restrict_clauses list can affect the size and cost * estimates for this path. */ - ParamPathInfo *param_info = inner_path->param_info; - Relids inner_req_batched = param_info == NULL - ? NULL : param_info->yb_ppi_req_outer_batched; - - Relids outer_req_unbatched = outer_path->param_info ? - outer_path->param_info->yb_ppi_req_outer_unbatched : - NULL; + Relids inner_req_batched = YB_PATH_REQ_OUTER_BATCHED(inner_path); + + Relids outer_req_unbatched = YB_PATH_REQ_OUTER_UNBATCHED(outer_path); bool is_batched = bms_overlap(inner_req_batched, - outer_path->parent->relids) && - !bms_overlap(outer_req_unbatched, inner_req_batched); + outer_path->parent->relids) && + !bms_overlap(outer_req_unbatched, inner_req_batched); if (!is_batched && bms_overlap(inner_req_outer, outer_path->parent->relids)) { Relids inner_and_outer = bms_union(inner_path->parent->relids, diff --git a/src/postgres/src/backend/optimizer/util/relnode.c b/src/postgres/src/backend/optimizer/util/relnode.c index 1221707a4dd5..58e69c876309 100644 --- a/src/postgres/src/backend/optimizer/util/relnode.c +++ b/src/postgres/src/backend/optimizer/util/relnode.c @@ -1247,7 +1247,6 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, double rows; ListCell *lc; Relids batchedrelids = root->yb_cur_batched_relids; - Relids unbatchedrelids = root->yb_cur_unbatched_relids; /* If rel has LATERAL refs, every path for it should account for them */ Assert(bms_is_subset(baserel->lateral_relids, required_outer)); @@ -1263,9 +1262,8 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, if ((ppi = yb_find_batched_param_path_info(baserel, required_outer, - batchedrelids, - unbatchedrelids))) - return ppi; + batchedrelids))) + return ppi; } else { @@ -1308,7 +1306,6 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, ppi->ppi_rows = rows; ppi->ppi_clauses = pclauses; ppi->yb_ppi_req_outer_batched = batchedrelids; - ppi->yb_ppi_req_outer_unbatched = unbatchedrelids; baserel->ppilist = lappend(baserel->ppilist, ppi); @@ -1371,33 +1368,16 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, Assert(!bms_overlap(joinrel->relids, required_outer)); /* Compute batched and unbatched relids. */ - Relids outer_batchedrelids = outer_path->param_info - ? outer_path->param_info->yb_ppi_req_outer_batched - : NULL; - Relids inner_batchedrelids = inner_path->param_info - ? inner_path->param_info->yb_ppi_req_outer_batched - : NULL; - Relids outer_unbatchedrelids = outer_path->param_info - ? outer_path->param_info->yb_ppi_req_outer_unbatched - : NULL; - Relids inner_unbatchedrelids = inner_path->param_info - ? inner_path->param_info->yb_ppi_req_outer_unbatched - : NULL; - Assert(bms_is_subset(outer_batchedrelids, required_outer)); - - Relids req_batchedids = bms_union(outer_batchedrelids, - inner_batchedrelids); - req_batchedids = bms_difference(req_batchedids, - outer_path->parent->relids); + Relids req_batchedids = bms_union(YB_PATH_REQ_OUTER_BATCHED(outer_path), + YB_PATH_REQ_OUTER_BATCHED(inner_path)); - Relids req_unbatchedids = bms_union(outer_unbatchedrelids, - inner_unbatchedrelids); - - req_unbatchedids = bms_difference(req_unbatchedids, - outer_path->parent->relids); + Relids req_unbatchedids = + bms_union(YB_PATH_REQ_OUTER_UNBATCHED(outer_path), + YB_PATH_REQ_OUTER_UNBATCHED(inner_path)); req_batchedids = bms_difference(req_batchedids, - req_unbatchedids); + req_unbatchedids); + req_batchedids = bms_difference(req_batchedids, outer_path->parent->relids); /* * Identify all joinclauses that are movable to this join rel given this @@ -1535,9 +1515,7 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, } } - if (IsYugaByteEnabled() && - (!bms_is_empty(outer_batchedrelids) || - !bms_is_empty(inner_batchedrelids))) + if (IsYugaByteEnabled() && !bms_is_empty(req_batchedids)) { /* * YB: TODO: This can be omitted if we allow join filters to be batched @@ -1547,12 +1525,10 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, * This same logic can be applied to any mergejoinable qpqual within * a batched join context. */ - Relids outer_relids = outer_path->parent->relids; - Relids inner_relids = inner_path->parent->relids; foreach(lc, pclauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - /* + /* * YB: If outer/inner path has a relevant pclause that requires * external relids that are not in * outer/inner_batchedrelids @@ -1560,36 +1536,19 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, */ if (bms_is_subset(rinfo->clause_relids, joinrel->relids)) continue; - - Bitmapset *external_rels; - Bitmapset *unbatched_ext_rels; - if (bms_overlap(rinfo->clause_relids, outer_relids)) - { - external_rels = - bms_difference(rinfo->clause_relids, outer_relids); - unbatched_ext_rels = - bms_difference(external_rels, outer_batchedrelids); - req_unbatchedids = - bms_union(req_unbatchedids,unbatched_ext_rels); - } - else + if (bms_overlap(rinfo->clause_relids, req_batchedids)) { - Assert(bms_overlap(rinfo->clause_relids, inner_relids)); - external_rels = - bms_difference(rinfo->clause_relids, inner_relids); - unbatched_ext_rels = - bms_difference(external_rels, inner_batchedrelids); + Relids unbatched_ext_rels = + bms_difference(rinfo->clause_relids, joinrel->relids); req_unbatchedids = - bms_union(req_unbatchedids,unbatched_ext_rels); + bms_union(req_unbatchedids, unbatched_ext_rels); } - bms_free(external_rels); - bms_free(unbatched_ext_rels); } } - req_batchedids = bms_difference(req_batchedids, req_unbatchedids); + req_batchedids = bms_difference(req_batchedids, req_unbatchedids); /* * Now, attach the identified moved-down clauses to the caller's @@ -1601,8 +1560,7 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, /* If we already have a PPI for this parameterization, just return it */ if ((ppi = yb_find_batched_param_path_info(joinrel, required_outer, - req_batchedids, - req_unbatchedids))) + req_batchedids))) return ppi; /* Estimate the number of rows returned by the parameterized join */ @@ -1625,32 +1583,31 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, ppi->ppi_clauses = NIL; ppi->yb_ppi_req_outer_batched = req_batchedids; - ppi->yb_ppi_req_outer_unbatched = req_unbatchedids; joinrel->ppilist = lappend(joinrel->ppilist, ppi); return ppi; } -void -yb_accumulate_batching_info(List *paths, - Relids *batchedrelids, Relids *unbatchedrelids) +bool +yb_has_same_batching_reqs(List *paths) { + Path *first_path = (Path *) linitial(paths); + Relids first_req_outer_batch = YB_PATH_REQ_OUTER_BATCHED(first_path); + Relids first_req_outer = PATH_REQ_OUTER(first_path); ListCell *lc; foreach(lc, paths) { Path *path = (Path *) lfirst(lc); - ParamPathInfo *ppi = path->param_info; - if (ppi) - { - *batchedrelids = - bms_union(*batchedrelids, ppi->yb_ppi_req_outer_batched); - *unbatchedrelids = - bms_union(*unbatchedrelids, ppi->yb_ppi_req_outer_unbatched); - } + + if (!bms_equal(first_req_outer_batch, YB_PATH_REQ_OUTER_BATCHED(path))) + return false; + + if (!bms_equal(first_req_outer, PATH_REQ_OUTER(path))) + return false; } - *batchedrelids = bms_difference(*batchedrelids, *unbatchedrelids); + return true; } /* @@ -1693,8 +1650,7 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) ParamPathInfo * yb_find_batched_param_path_info(RelOptInfo *rel, Relids required_outer, - Relids yb_required_batched_outer, - Relids yb_required_unbatched_outer) + Relids yb_required_batched_outer) { ListCell *lc; @@ -1703,10 +1659,8 @@ yb_find_batched_param_path_info(RelOptInfo *rel, Relids required_outer, ParamPathInfo *ppi = (ParamPathInfo *) lfirst(lc); if (bms_equal(ppi->ppi_req_outer, required_outer) && - bms_equal(ppi->yb_ppi_req_outer_batched, - yb_required_batched_outer) && - bms_equal(ppi->yb_ppi_req_outer_unbatched, - yb_required_unbatched_outer)) + bms_equal(ppi->yb_ppi_req_outer_batched, + yb_required_batched_outer)) return ppi; } diff --git a/src/postgres/src/include/nodes/relation.h b/src/postgres/src/include/nodes/relation.h index 31978df5af27..fcb967986f3c 100644 --- a/src/postgres/src/include/nodes/relation.h +++ b/src/postgres/src/include/nodes/relation.h @@ -1080,8 +1080,6 @@ typedef struct ParamPathInfo double ppi_rows; /* estimated number of result tuples */ List *ppi_clauses; /* join clauses available from outer rels */ Relids yb_ppi_req_outer_batched; /* outer rels that can be batched */ - Relids yb_ppi_req_outer_unbatched; /* outer rels that cannot - * be batched */ } ParamPathInfo; @@ -1142,6 +1140,16 @@ typedef struct Path #define PATH_REQ_OUTER(path) \ ((path)->param_info ? (path)->param_info->ppi_req_outer : (Relids) NULL) +#define YB_PATH_REQ_OUTER_BATCHED(path) \ + ((path)->param_info ? ((path)->param_info->yb_ppi_req_outer_batched) : \ + (Relids) NULL) + +#define YB_PATH_NEEDS_BATCHED_RELS(path) \ + !bms_is_empty(YB_PATH_REQ_OUTER_BATCHED(path)) + +#define YB_PATH_REQ_OUTER_UNBATCHED(path) \ + (bms_difference(PATH_REQ_OUTER(path), YB_PATH_REQ_OUTER_BATCHED(path))) + /*---------- * IndexPath represents an index scan over a single index. * diff --git a/src/postgres/src/include/optimizer/pathnode.h b/src/postgres/src/include/optimizer/pathnode.h index 0bb8eb2a6fdd..5ef49c1c656f 100644 --- a/src/postgres/src/include/optimizer/pathnode.h +++ b/src/postgres/src/include/optimizer/pathnode.h @@ -289,8 +289,7 @@ extern ParamPathInfo *get_joinrel_parampathinfo(PlannerInfo *root, SpecialJoinInfo *sjinfo, Relids required_outer, List **restrict_clauses); -extern void yb_accumulate_batching_info(List *paths, - Relids *batchedrelids, Relids *unbatchedrelids); +extern bool yb_has_same_batching_reqs(List *paths); extern ParamPathInfo *get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer); extern ParamPathInfo *find_param_path_info(RelOptInfo *rel, @@ -298,8 +297,7 @@ extern ParamPathInfo *find_param_path_info(RelOptInfo *rel, extern ParamPathInfo *yb_find_batched_param_path_info( RelOptInfo *rel, Relids required_outer, - Relids yb_required_batched_outer, - Relids yb_required_unbatched_outer); + Relids yb_required_batched_outer); extern RelOptInfo *build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, RelOptInfo *inner_rel, RelOptInfo *parent_joinrel, List *restrictlist, diff --git a/src/postgres/src/test/regress/expected/yb_join_batching.out b/src/postgres/src/test/regress/expected/yb_join_batching.out index a2994cbbd250..d3015e1a8f5b 100644 --- a/src/postgres/src/test/regress/expected/yb_join_batching.out +++ b/src/postgres/src/test/regress/expected/yb_join_batching.out @@ -1454,6 +1454,52 @@ SELECT '' AS "xxx", t1.a, t2.e | 5 | -5 (7 rows) +CREATE TABLE x1 (a int PRIMARY KEY, b int); +CREATE INDEX i_x1_b ON x1 (b); +INSERT INTO x1 VALUES (1, 0), (2, 1); +CREATE TABLE x2 (a int PRIMARY KEY, b int); +CREATE INDEX i_x2_b ON x2 (b); +INSERT INTO x2 VALUES (1, 0), (2, 1); +CREATE TABLE x3 (a int PRIMARY KEY, b int); +CREATE INDEX i_x3_b ON x3 (b); +INSERT INTO x3 VALUES (1, 0), (2, 1); +EXPLAIN (COSTS OFF) SELECT * FROM x1 LEFT JOIN LATERAL ( + SELECT * FROM x2 LEFT JOIN LATERAL ( + SELECT x3.b FROM x3 WHERE x3.a = x1.a AND x3.b = x2.b LIMIT ALL + ) AS v1 ON true + WHERE x2.a = x1.a +) v2 ON true +ORDER BY x1.a ASC; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: x1.a + -> Nested Loop Left Join + -> Seq Scan on x1 + -> Nested Loop Left Join + -> Index Scan using x2_pkey on x2 + Index Cond: (a = x1.a) + -> Index Scan using x3_pkey on x3 + Index Cond: (a = x1.a) + Remote Filter: (b = x2.b) +(10 rows) + +SELECT * FROM x1 LEFT JOIN LATERAL ( + SELECT * FROM x2 LEFT JOIN LATERAL ( + SELECT x3.b FROM x3 WHERE x3.a = x1.a AND x3.b = x2.b LIMIT ALL + ) AS v1 ON true + WHERE x2.a = x1.a +) v2 ON true +ORDER BY x1.a ASC; + a | b | a | b | b +---+---+---+---+--- + 1 | 0 | 1 | 0 | 0 + 2 | 1 | 2 | 1 | 1 +(2 rows) + +DROP TABLE x1; +DROP TABLE x2; +DROP TABLE x3; -- -- -- Inner joins (equi-joins) diff --git a/src/postgres/src/test/regress/sql/yb_join_batching.sql b/src/postgres/src/test/regress/sql/yb_join_batching.sql index 4bd71871ee80..967ea3cda6b4 100644 --- a/src/postgres/src/test/regress/sql/yb_join_batching.sql +++ b/src/postgres/src/test/regress/sql/yb_join_batching.sql @@ -387,6 +387,38 @@ SELECT '' AS "xxx", t1.a, t2.e FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e) WHERE t1.a = t2.d order by 1, 2, 3; +CREATE TABLE x1 (a int PRIMARY KEY, b int); +CREATE INDEX i_x1_b ON x1 (b); +INSERT INTO x1 VALUES (1, 0), (2, 1); + +CREATE TABLE x2 (a int PRIMARY KEY, b int); +CREATE INDEX i_x2_b ON x2 (b); +INSERT INTO x2 VALUES (1, 0), (2, 1); + +CREATE TABLE x3 (a int PRIMARY KEY, b int); +CREATE INDEX i_x3_b ON x3 (b); +INSERT INTO x3 VALUES (1, 0), (2, 1); + +EXPLAIN (COSTS OFF) SELECT * FROM x1 LEFT JOIN LATERAL ( + SELECT * FROM x2 LEFT JOIN LATERAL ( + SELECT x3.b FROM x3 WHERE x3.a = x1.a AND x3.b = x2.b LIMIT ALL + ) AS v1 ON true + WHERE x2.a = x1.a +) v2 ON true +ORDER BY x1.a ASC; + +SELECT * FROM x1 LEFT JOIN LATERAL ( + SELECT * FROM x2 LEFT JOIN LATERAL ( + SELECT x3.b FROM x3 WHERE x3.a = x1.a AND x3.b = x2.b LIMIT ALL + ) AS v1 ON true + WHERE x2.a = x1.a +) v2 ON true +ORDER BY x1.a ASC; + +DROP TABLE x1; +DROP TABLE x2; +DROP TABLE x3; + -- -- -- Inner joins (equi-joins)