On Sat, Jan 27, 2024 at 8:26 AM vignesh C <vignes...@gmail.com> wrote: > > On Tue, 31 Oct 2023 at 10:48, Ashutosh Bapat > <ashutosh.bapat....@gmail.com> wrote: > > > > On Thu, Sep 14, 2023 at 4:39 PM Ashutosh Bapat > > <ashutosh.bapat....@gmail.com> wrote: > > > > > > The patch set is thus > > > 0001 - patch used to measure memory used during planning > > > > > > 0002 - Patch to free intermediate Relids computed by > > > adjust_child_relids_multilevel(). I didn't test memory consumption for > > > multi-level partitioning. But this is clear improvement. In that > > > function we free the AppendInfos array which as many pointers long as > > > the number of relids. So it doesn't make sense not to free the Relids > > > which can be {largest relid}/8 bytes long at least. > > > > > > 0003 - patch to save and reuse commuted RestrictInfo. This patch by > > > itself shows a small memory saving (3%) in the query below where the > > > same clause is commuted twice. The query does not contain any > > > partitioned tables. > > > create table t2 (a int primary key, b int, c int); > > > create index t2_a_b on t2(a, b); > > > select * from t2 where 10 = a > > > Memory used without patch: 13696 bytes > > > Memory used with patch: 13264 bytes > > > > > > 0004 - Patch which implements the hash table of hash table described > > > above and also code to avoid repeated RestrictInfo list translations. > > > > > > I will add this patchset to next commitfest. > > > > > > -- > > > Best Wishes, > > > Ashutosh Bapat > > > > PFA rebased patches. Nothing changes in 0002, 0003 and 0004. 0001 is > > the squashed version of the latest patch set at > > https://www.postgresql.org/message-id/CAExHW5sCJX7696sF-OnugAiaXS=Ag95=-m1csrjcmyyj8pd...@mail.gmail.com. > > CFBot shows that the patch does not apply anymore as in [1]: > === Applying patches on top of PostgreSQL commit ID > 7014c9a4bba2d1b67d60687afb5b2091c1d07f73 === > === applying patch > ./0001-Report-memory-used-for-planning-a-query-in--20231031.patch > ... > patching file src/test/regress/expected/explain.out > Hunk #5 FAILED at 290. > Hunk #6 succeeded at 545 (offset 4 lines). > 1 out of 6 hunks FAILED -- saving rejects to file > src/test/regress/expected/explain.out.rej > patching file src/tools/pgindent/typedefs.list > Hunk #1 succeeded at 1562 (offset 18 lines). > > Please post an updated version for the same. > > [1] - http://cfbot.cputube.org/patch_46_4564.log > > Regards, > Vignesh
Thanks Vignesh for the notification. PFA rebased patches. 0001 in earlier patch-set is now removed. -- Best Wishes, Ashutosh Bapat
From 996664e4b4d7e65449b813828e81d0924af64e2b Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Mon, 4 Sep 2023 14:56:21 +0530 Subject: [PATCH 3/4] Save commuted RestrictInfo for later use A commuted RestrictInfo may be produced as many times as the number of indexes it is used for. It's the same RestrictInfo always. Save some CPU and memory by saving the result in the original RestrictInfo. Ashutosh Bapat --- src/backend/optimizer/util/appendinfo.c | 6 ++++++ src/backend/optimizer/util/restrictinfo.c | 22 +++++++++++++++++++++- src/include/nodes/pathnodes.h | 9 +++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c index dd4251be80..cb9a6d2d95 100644 --- a/src/backend/optimizer/util/appendinfo.c +++ b/src/backend/optimizer/util/appendinfo.c @@ -492,6 +492,12 @@ adjust_appendrel_attrs_mutator(Node *node, newinfo->left_mcvfreq = -1; newinfo->right_mcvfreq = -1; + /* + * Wipe out commuted parent RestrictInfo. The caller will compute + * commuted clause if required. + */ + newinfo->comm_rinfo = NULL; + return (Node *) newinfo; } diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 0b406e9334..48f8b38258 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -246,6 +246,8 @@ make_restrictinfo_internal(PlannerInfo *root, restrictinfo->left_hasheqoperator = InvalidOid; restrictinfo->right_hasheqoperator = InvalidOid; + restrictinfo->comm_rinfo = NULL; + restrictinfo->is_commuted = false; return restrictinfo; } @@ -354,14 +356,27 @@ make_sub_restrictinfos(PlannerInfo *root, * be hazardous if the source is subject to change. Also notice that we * assume without checking that the commutator op is a member of the same * btree and hash opclasses as the original op. + * + * If a commuted RestrictInfo is already available it is returned. */ RestrictInfo * commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op) { RestrictInfo *result; OpExpr *newclause; - OpExpr *clause = castNode(OpExpr, rinfo->clause); + OpExpr *clause; + + if (rinfo->comm_rinfo) + { + result = rinfo->comm_rinfo; + newclause = castNode(OpExpr, result->clause); + Assert(list_length(newclause->args) == 2); + Assert(newclause->opno == comm_op); + return result; + } + + clause = castNode(OpExpr, rinfo->clause); Assert(list_length(clause->args) == 2); /* flat-copy all the fields of clause ... */ @@ -403,6 +418,11 @@ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op) result->right_mcvfreq = rinfo->left_mcvfreq; result->left_hasheqoperator = InvalidOid; result->right_hasheqoperator = InvalidOid; + result->is_commuted = !rinfo->is_commuted; + result->comm_rinfo = rinfo; + + /* Save the commuted RestrictInfo for later use. */ + rinfo->comm_rinfo = result; return result; } diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 534692bee1..e4420acded 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2529,6 +2529,13 @@ typedef struct LimitPath * * parent_ec, left_ec, right_ec are not printed, lest it lead to infinite * recursion in plan tree dump. + * + * A RestrictInfo may get commuted as many times as the number of indexes it is + * used for. The commuted clause is cached in the original RestrictInfo as + * comm_rinfo and vice versa. Both the RestrictInfos are commuted versions of + * each other. is_commuted flag is false for the first one to appear and is + * true in the other. The order doesn't matter. The flag just differentiate + * between the commuted version. */ typedef struct RestrictInfo @@ -2683,6 +2690,8 @@ typedef struct RestrictInfo /* hash equality operators used for memoize nodes, else InvalidOid */ Oid left_hasheqoperator pg_node_attr(equal_ignore); Oid right_hasheqoperator pg_node_attr(equal_ignore); + struct RestrictInfo *comm_rinfo pg_node_attr(equal_ignore); + bool is_commuted pg_node_attr(equal_ignore); } RestrictInfo; /* -- 2.25.1
From 204d7f3f290364fb0beff7151dfb8301c0b10700 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Thu, 31 Aug 2023 19:02:23 +0530 Subject: [PATCH 2/4] Free intermediate Relids created in adjust_child_relids_multilevel() adjust_child_relids_multilevel() creates Relids for all the intermediate stages in a partition hierarchy. These relid sets are not required finally. Relids or Bitmapset take reasonably large space when thousands of partitions are invovled. Hence free these intermediate relid sets. Ashutosh Bapat --- src/backend/optimizer/util/appendinfo.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c index 51fdeace7d..dd4251be80 100644 --- a/src/backend/optimizer/util/appendinfo.c +++ b/src/backend/optimizer/util/appendinfo.c @@ -591,6 +591,8 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, { AppendRelInfo **appinfos; int nappinfos; + Relids tmp_relids = NULL; + Relids child_relids; /* * If the given relids set doesn't contain any of the parent relids, it @@ -599,13 +601,14 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, if (!bms_overlap(relids, parentrel->relids)) return relids; + tmp_relids = relids; /* Recurse if immediate parent is not the top parent. */ if (childrel->parent != parentrel) { if (childrel->parent) - relids = adjust_child_relids_multilevel(root, relids, - childrel->parent, - parentrel); + tmp_relids = adjust_child_relids_multilevel(root, relids, + childrel->parent, + parentrel); else elog(ERROR, "childrel is not a child of parentrel"); } @@ -613,11 +616,15 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, /* Now translate for this child. */ appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos); - relids = adjust_child_relids(relids, nappinfos, appinfos); + child_relids = adjust_child_relids(tmp_relids, nappinfos, appinfos); + + /* Free any intermediate Relids created. */ + if (tmp_relids != relids) + bms_free(tmp_relids); pfree(appinfos); - return relids; + return child_relids; } /* -- 2.25.1
From c9e656c7887e069dae5cc57302640f246c9debe4 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat <ashutosh.ba...@enterprisedb.com> Date: Thu, 22 Jun 2023 14:27:14 +0530 Subject: [PATCH 4/4] Avoid translating RestrictInfo repeatedly RestrictInfo to be applied to the child rels (including the child join rels) are obtained by translating RestrictInfo applicable to the parent rel. Since these translations are not tracked, the same RestrictInfo may get translated multiple times for the same parent and child pair. When using partitionwise join this can happen as many times as the number of possible join orders between the partitioned tables or as many times a parent path is reparameterized for a child if a clause is used in such a path. Repeated translations are avoided by saving the translations in a hash table. PlannerInfo maintains a hash table of hash tables to store and retrieve translated RestrictInfos. The first level hash table is referenced by RestrictInfo::rinfo_serial and the second level hash table contains translated RestrictInfos referenced by RestrictInfo::required_relids. Two function find_child_rinfo() and add_child_rinfo() are used to search a RestrictInfo for a given tanslation and add a child RestrictInfo respectively. Translations of commuted clauses need further tracking. When creating a join clause using an equivalence class, if the child clause to be created is commuted version of the parent clause mark the child clause so and also add it to the hash table containing translations. When building a child clause from parent clause, commute the child clause if the parent clause is commuted version. Ashutosh Bapat --- src/backend/optimizer/path/equivclass.c | 26 +++-- src/backend/optimizer/path/joinrels.c | 76 ++++++++++++- src/backend/optimizer/util/pathnode.c | 123 ++++++++++++++++++++-- src/backend/optimizer/util/relnode.c | 7 +- src/backend/optimizer/util/restrictinfo.c | 123 ++++++++++++++++++++++ src/include/nodes/pathnodes.h | 4 +- src/include/optimizer/paths.h | 2 + src/include/optimizer/restrictinfo.h | 5 + src/tools/pgindent/typedefs.list | 2 + 9 files changed, 347 insertions(+), 21 deletions(-) diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 4bd60a09c6..853b9533f2 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1827,7 +1827,7 @@ create_join_clause(PlannerInfo *root, EquivalenceMember *rightem, EquivalenceClass *parent_ec) { - RestrictInfo *rinfo; + RestrictInfo *rinfo = NULL; RestrictInfo *parent_rinfo = NULL; ListCell *lc; MemoryContext oldcontext; @@ -1895,11 +1895,6 @@ create_join_clause(PlannerInfo *root, bms_union(leftem->em_relids, rightem->em_relids), ec->ec_min_security); - - /* If it's a child clause, copy the parent's rinfo_serial */ - if (parent_rinfo) - rinfo->rinfo_serial = parent_rinfo->rinfo_serial; - /* Mark the clause as redundant, or not */ rinfo->parent_ec = parent_ec; @@ -1918,6 +1913,25 @@ create_join_clause(PlannerInfo *root, MemoryContextSwitchTo(oldcontext); + /* We have obtained a translation through EC machinery, save it. */ + if (parent_rinfo) + { + rinfo->rinfo_serial = parent_rinfo->rinfo_serial; + + /* + * If the child clause is commuted translation of the parent clause, + * mark it accordingly. + */ + if ((leftem->em_parent && + leftem->em_parent == parent_rinfo->right_em) || + (rightem->em_parent && + rightem->em_parent == parent_rinfo->left_em)) + rinfo->is_commuted = !parent_rinfo->is_commuted; + else + rinfo->is_commuted = parent_rinfo->is_commuted; + add_child_rinfo(root, parent_rinfo, rinfo); + } + return rinfo; } diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 4750579b0a..c19025e890 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -19,6 +19,7 @@ #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/restrictinfo.h" #include "partitioning/partbounds.h" #include "utils/memutils.h" @@ -1436,6 +1437,76 @@ restriction_is_constant_false(List *restrictlist, return false; } +/* + * Build the child RestrictInfo by translating the given parent RestrictInfo. If + * the translation is already available use it. Otherwise translate the parent + * clause and add to the available translations. + */ +static RestrictInfo * +build_child_clause(PlannerInfo *root, + RestrictInfo *parent_rinfo, + int nappinfos, AppendRelInfo **appinfos) +{ + Relids child_req_relids = + adjust_child_relids(parent_rinfo->required_relids, + nappinfos, appinfos); + RestrictInfo *child_rinfo; + + /* If no child relids are involved no translation is required. */ + if (bms_equal(child_req_relids, parent_rinfo->required_relids)) + return parent_rinfo; + + child_rinfo = find_child_rinfo(root, parent_rinfo, child_req_relids); + if (!child_rinfo) + { + child_rinfo = castNode(RestrictInfo, + adjust_appendrel_attrs(root, + (Node *) parent_rinfo, + nappinfos, + appinfos)); + add_child_rinfo(root, parent_rinfo, child_rinfo); + } + bms_free(child_req_relids); + + /* If the parent clause is commuted, return commuted child clause. */ + if (parent_rinfo->is_commuted != child_rinfo->is_commuted) + { + if (!child_rinfo->comm_rinfo) + { + OpExpr *parent_clause = castNode(OpExpr, parent_rinfo->clause); + + commute_restrictinfo(child_rinfo, parent_clause->opno); + } + child_rinfo = child_rinfo->comm_rinfo; + } + + Assert(parent_rinfo->is_commuted == child_rinfo->is_commuted); + return child_rinfo; +} + +/* + * Build the child RestrictInfo list by translating the given parent + * RestrictInfo list. + */ +List * +build_child_clauses(PlannerInfo *root, List *parent_clauselist, + int nappinfos, AppendRelInfo **appinfos) +{ + ListCell *lc; + List *child_clauselist = NIL; + + foreach(lc, parent_clauselist) + { + RestrictInfo *parent_rinfo = lfirst_node(RestrictInfo, lc); + RestrictInfo *child_rinfo = build_child_clause(root, parent_rinfo, + nappinfos, appinfos); + + child_clauselist = lappend(child_clauselist, child_rinfo); + } + + return child_clauselist; +} + /* * Assess whether join between given two partitioned relations can be broken * down into joins between matching partitions; a technique called @@ -1631,9 +1702,8 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, * applicable to the parent join. */ child_restrictlist = - (List *) adjust_appendrel_attrs(root, - (Node *) parent_restrictlist, - nappinfos, appinfos); + build_child_clauses(root, parent_restrictlist, nappinfos, + appinfos); /* Find or construct the child join's RelOptInfo */ child_joinrel = joinrel->part_rels[cnt_parts]; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 8dbf790e89..683b8f47f3 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -4062,6 +4062,89 @@ reparameterize_path(PlannerInfo *root, Path *path, return NULL; } +/* + * build_child_clauses_multilevel + * Similar to build_child_clauses() but for translations through multiple levels of inheritance (actually partitioning) hierarchy. + * + * In a way this is similar to adjust_appendrel_attrs_multilevel() except that this function uses a translated RestrictInfo if it's available. + */ +static List * +build_child_clauses_multilevel(PlannerInfo *root, List *parent_clauselist, + RelOptInfo *child_rel, + RelOptInfo *top_parent) +{ + AppendRelInfo **appinfos; + int nappinfos; + List *tmp_clauselist = parent_clauselist; + List *child_clauselist; + + if (child_rel->parent != top_parent) + { + if (child_rel->parent) + { + tmp_clauselist = build_child_clauses_multilevel(root, + parent_clauselist, + child_rel->parent, + top_parent); + } + else + elog(ERROR, "child_rel is not a child of top_parent"); + } + + appinfos = find_appinfos_by_relids(root, child_rel->relids, &nappinfos); + child_clauselist = build_child_clauses(root, tmp_clauselist, + nappinfos, appinfos); + pfree(appinfos); + /* Build any intermediate clauselist this function built. */ + if (tmp_clauselist != parent_clauselist) + list_free(tmp_clauselist); + + return child_clauselist; +} + +/* + * build_child_iclauses_multilevel + * Translate IndexClause list to be used for the + * given top parent relation for given child relation through the hierarchy of inheritance (actually partitioning). + * + * This is similar to adjust_appendrel_attrs_multilevel() for IndexClause except that it uses translated RestrictInfos if already available. + */ +static List * +build_child_iclauses_multilevel(PlannerInfo *root, + List *parent_iclauselist, + RelOptInfo *childrel, + RelOptInfo *top_parent) +{ + ListCell *lc; + List *child_iclauses = NIL; + + foreach(lc, parent_iclauselist) + { + IndexClause *iclause = lfirst_node(IndexClause, lc); + List *rinfo_list = NIL; + List *child_rinfo_list; + IndexClause *child_iclause = makeNode(IndexClause); + + /* + * Collect the RestrictInfos to be translated. That's all what needs + * to be translated right now. + */ + rinfo_list = lappend(rinfo_list, iclause->rinfo); + rinfo_list = list_concat(rinfo_list, iclause->indexquals); + + child_rinfo_list = build_child_clauses_multilevel(root, rinfo_list, + childrel, top_parent); + memcpy(child_iclause, iclause, sizeof(IndexClause)); + child_iclause->rinfo = linitial(child_rinfo_list); + child_iclause->indexquals = list_delete_first(child_rinfo_list); + + list_free(rinfo_list); + child_iclauses = lappend(child_iclauses, child_iclause); + } + + return child_iclauses; +} + /* * reparameterize_path_by_child * Given a path parameterized by the parent of the given child relation, @@ -4150,7 +4233,10 @@ do { \ IndexPath *ipath; FLAT_COPY_PATH(ipath, path, IndexPath); - ADJUST_CHILD_ATTRS(ipath->indexclauses); + ipath->indexclauses = + build_child_iclauses_multilevel(root, + ipath->indexclauses, + child_rel, child_rel->top_parent); new_path = (Path *) ipath; } break; @@ -4234,7 +4320,11 @@ do { \ jpath = (JoinPath *) npath; REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath); REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath); - ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo); + jpath->joinrestrictinfo = + build_child_clauses_multilevel(root, + jpath->joinrestrictinfo, + child_rel, + child_rel->top_parent); new_path = (Path *) npath; } break; @@ -4249,8 +4339,16 @@ do { \ jpath = (JoinPath *) mpath; REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath); REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath); - ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo); - ADJUST_CHILD_ATTRS(mpath->path_mergeclauses); + jpath->joinrestrictinfo = + build_child_clauses_multilevel(root, + jpath->joinrestrictinfo, + child_rel, + child_rel->top_parent); + mpath->path_mergeclauses = + build_child_clauses_multilevel(root, + mpath->path_mergeclauses, + child_rel, + child_rel->top_parent); new_path = (Path *) mpath; } break; @@ -4265,8 +4363,16 @@ do { \ jpath = (JoinPath *) hpath; REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath); REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath); - ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo); - ADJUST_CHILD_ATTRS(hpath->path_hashclauses); + jpath->joinrestrictinfo = + build_child_clauses_multilevel(root, + jpath->joinrestrictinfo, + child_rel, + child_rel->top_parent); + hpath->path_hashclauses = + build_child_clauses_multilevel(root, + hpath->path_hashclauses, + child_rel, + child_rel->top_parent); new_path = (Path *) hpath; } break; @@ -4348,7 +4454,10 @@ do { \ new_ppi->ppi_req_outer = bms_copy(required_outer); new_ppi->ppi_rows = old_ppi->ppi_rows; new_ppi->ppi_clauses = old_ppi->ppi_clauses; - ADJUST_CHILD_ATTRS(new_ppi->ppi_clauses); + new_ppi->ppi_clauses = + build_child_clauses_multilevel(root, + new_ppi->ppi_clauses, child_rel, + child_rel->top_parent); new_ppi->ppi_serials = bms_copy(old_ppi->ppi_serials); rel->ppilist = lappend(rel->ppilist, new_ppi); diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index e5f4062bfb..2d22dc7e11 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -964,10 +964,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, nappinfos, appinfos); /* Construct joininfo list. */ - joinrel->joininfo = (List *) adjust_appendrel_attrs(root, - (Node *) parent_joinrel->joininfo, - nappinfos, - appinfos); + joinrel->joininfo = build_child_clauses(root, + parent_joinrel->joininfo, + nappinfos, appinfos); /* * Lateral relids referred in child join will be same as that referred in diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 48f8b38258..8b98891478 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -20,6 +20,19 @@ #include "optimizer/optimizer.h" #include "optimizer/restrictinfo.h" +typedef struct RinfoHashTabEntry +{ + int rinfo_serial; /* Key, must be the first one. */ + HTAB *hash_tab; /* Hash table containing child RestrictInfo + * for RestrictInfo indicated by given + * rinfo_serial. */ +} RinfoHashTabEntry; + +typedef struct ChildRinfoTabEntry +{ + Relids required_relids; /* Key, must be the first one */ + RestrictInfo *child_rinfo; /* Child RestrictInfo for required_relids. */ +} ChildRinfoTabEntry; static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root, Expr *clause, @@ -42,6 +55,8 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root, Relids required_relids, Relids incompatible_relids, Relids outer_relids); +static HTAB *get_child_rinfo_htab(PlannerInfo *root, + RestrictInfo *parent_rinfo); /* @@ -705,3 +720,111 @@ join_clause_is_movable_into(RestrictInfo *rinfo, return true; } + +/* + * find_child_rinfo + * Find the translation of the given parent RestrictInfo for the given + * child relids. + * + * The given parent RestrictInfo need not be the top parent relations's + * RestrictInfo. We use RestrictInfo::rinfo_serial to access all the + * translations of the top parent relation's RestrictInfo. + * + * PlannerInfo maintains a hash table of hash tables to store and search + * translated RestrictInfos. The first level hash table is referenced by + * RestrictInfo::rinfo_serial and the second level hash table contains + * translated RestrictInfos referenced by RestrictInfo::required_relids. + */ +RestrictInfo * +find_child_rinfo(PlannerInfo *root, RestrictInfo *parent_rinfo, + Relids child_required_relids) +{ + HTAB *rinfo_hash = get_child_rinfo_htab(root, parent_rinfo); + ChildRinfoTabEntry *child_rinfo_entry; + + child_rinfo_entry = hash_search(rinfo_hash, &child_required_relids, + HASH_FIND, + NULL); + return (child_rinfo_entry ? child_rinfo_entry->child_rinfo : NULL); +} + +/* + * add_child_rinfo + * Save the given child RestrictInfo in the hash table containing translations of the given parent RestrictInfo. + * + * The parent RestrictInfo need not be the top parent RestrictInfo. We use + * RestrictInfo::rinfo_serial to identify the set of RestrictInfos. + * + * The function assumes that the given child RestrictInfo is not already + * available in the hash table. + */ +extern void +add_child_rinfo(PlannerInfo *root, RestrictInfo *parent_rinfo, + RestrictInfo *child_rinfo) +{ + HTAB *rinfo_hash = get_child_rinfo_htab(root, parent_rinfo); + ChildRinfoTabEntry *child_rinfo_entry; + bool found; + + Assert(child_rinfo->rinfo_serial == parent_rinfo->rinfo_serial); + + child_rinfo_entry = hash_search(rinfo_hash, &(child_rinfo->required_relids), + HASH_ENTER, &found); + + /* + * A child restrictinfo should be translated only once and thus is + * required to be added only once. + */ + Assert(!found); + child_rinfo_entry->child_rinfo = child_rinfo; +} + +/* + * get_child_rinfo_htab + * Given a parent RestrictInfo, return the hash table containing all its child RestrictInfos. + * + * A helper function to access the RestrictInfo translation hash tables. The function creates the required hash table if they are not already available. + */ +static HTAB * +get_child_rinfo_htab(PlannerInfo *root, RestrictInfo *parent_rinfo) +{ + RinfoHashTabEntry *rinfo_tab_entry; + bool found_htab; + + if (!root->child_rinfo_hash) + { + HASHCTL hash_ctl = {0}; + + Assert(sizeof(int) == sizeof(parent_rinfo->rinfo_serial)); + hash_ctl.keysize = sizeof(int); + hash_ctl.entrysize = sizeof(RinfoHashTabEntry); + hash_ctl.hcxt = root->planner_cxt; + + root->child_rinfo_hash = hash_create("hash of child rinfo hash", + root->last_rinfo_serial, + &hash_ctl, + HASH_ELEM | HASH_BLOBS | + HASH_CONTEXT); + } + + rinfo_tab_entry = hash_search(root->child_rinfo_hash, + &(parent_rinfo->rinfo_serial), HASH_ENTER, + &found_htab); + if (!found_htab) + { + HASHCTL hash_ctl = {0}; + + hash_ctl.keysize = sizeof(Relids); + hash_ctl.entrysize = sizeof(ChildRinfoTabEntry); + hash_ctl.hash = bitmap_hash; + hash_ctl.match = bitmap_match; + hash_ctl.hcxt = root->planner_cxt; + + rinfo_tab_entry->hash_tab = hash_create("child rinfo hash", 100, + &hash_ctl, + HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | + HASH_CONTEXT); + } + + return rinfo_tab_entry->hash_tab; +} diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index e4420acded..5b36e98bf3 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -338,6 +338,7 @@ struct PlannerInfo /* counter for assigning RestrictInfo serial numbers */ int last_rinfo_serial; + struct HTAB *child_rinfo_hash pg_node_attr(read_write_ignore); /* * all_result_relids is empty for SELECT, otherwise it contains at least @@ -2535,7 +2536,8 @@ typedef struct LimitPath * comm_rinfo and vice versa. Both the RestrictInfos are commuted versions of * each other. is_commuted flag is false for the first one to appear and is * true in the other. The order doesn't matter. The flag just differentiate - * between the commuted version. + * between the commuted version. The child RestrictInfos inherit this flag from + * their respective parent RestrictInfo. */ typedef struct RestrictInfo diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 0e8a9c94ba..9086cef19b 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -112,6 +112,8 @@ extern bool have_join_order_restriction(PlannerInfo *root, extern bool have_dangerous_phv(PlannerInfo *root, Relids outer_relids, Relids inner_params); extern void mark_dummy_rel(RelOptInfo *rel); +extern List *build_child_clauses(PlannerInfo *root, List *parent_clauselist, + int nappinfos, AppendRelInfo **appinfos); /* * equivclass.c diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 1b42c832c5..f997d38bfe 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -47,5 +47,10 @@ extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel); extern bool join_clause_is_movable_into(RestrictInfo *rinfo, Relids currentrelids, Relids current_and_outer); +extern RestrictInfo *find_child_rinfo(PlannerInfo *root, + RestrictInfo *parent_rinfo, + Bitmapset *child_required_relids); +extern void add_child_rinfo(PlannerInfo *root, RestrictInfo *parent_rinfo, + RestrictInfo *child_rinfo); #endif /* RESTRICTINFO_H */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 90b37b919c..a2bf040e74 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -379,6 +379,7 @@ CheckPointStmt CheckpointStatsData CheckpointerRequest CheckpointerShmemStruct +ChildRinfoTabEntry Chromosome CkptSortItem CkptTsStatus @@ -2395,6 +2396,7 @@ RewriteMappingDataEntry RewriteMappingFile RewriteRule RewriteState +RinfoHashTabEntry RmgrData RmgrDescData RmgrId -- 2.25.1