| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * cdbpathlocus.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #include "cdb/cdbcat.h" /* GpPolicy */ |
| #include "cdb/cdbdef.h" /* CdbSwap() */ |
| #include "cdb/cdbpullup.h" /* cdbpullup_missing_var_walker() */ |
| #include "nodes/makefuncs.h" /* makeVar() */ |
| #include "nodes/plannodes.h" /* Plan */ |
| #include "nodes/relation.h" /* RelOptInfo */ |
| #include "optimizer/pathnode.h" /* Path */ |
| #include "optimizer/paths.h" /* cdb_build_distribution_pathkeys() */ |
| #include "optimizer/tlist.h" /* tlist_member() */ |
| #include "parser/parse_expr.h" /* exprType() and exprTypmod() */ |
| |
| #include "cdb/cdbvars.h" |
| #include "cdb/cdbpathlocus.h" /* me */ |
| #include "cdb/cdbdatalocality.h" |
| |
| #ifdef __cplusplus |
| } /* extern "C" */ |
| #endif |
| |
| static List * |
| cdb_build_distribution_pathkeys(PlannerInfo *root, |
| RelOptInfo *rel, |
| int nattrs, |
| AttrNumber *attrs); |
| |
| |
| /* |
| * This flag controls the policy type returned from |
| * cdbpathlocus_from_baserel() for non-partitioned tables. |
| * It's a kludge put in to allow us to do |
| * distributed queries on catalog tables, like pg_class |
| * and pg_statistic. |
| * To use it, simply set it to true before running a catalog query, then set |
| * it back to false. |
| */ |
| bool cdbpathlocus_querysegmentcatalogs = false; |
| |
| |
| /* |
| * cdbpathlocus_compare |
| * |
| * - Returns false if locus a or locus b is "strewn", meaning that it |
| * cannot be determined whether it is equal to another partitioned |
| * distribution. |
| * |
| * - Returns true if a and b have the same 'locustype' and 'partkey'. |
| * |
| * - Returns true if both a and b are hashed and the set of possible |
| * m-tuples of expressions (e1, e2, ..., em) produced by a's partkey |
| * is equal to (if op == CdbPathLocus_Equal) or a superset of (if |
| * op == CdbPathLocus_Contains) the set produced by b's partkey. |
| * |
| * - Returns false otherwise. |
| */ |
| bool |
| cdbpathlocus_compare(CdbPathLocus_Comparison op, |
| CdbPathLocus a, |
| CdbPathLocus b) |
| { |
| ListCell *acell; |
| ListCell *bcell; |
| ListCell *aequivpathkeycell; |
| ListCell *bequivpathkeycell; |
| |
| Assert(op == CdbPathLocus_Comparison_Equal || |
| op == CdbPathLocus_Comparison_Contains); |
| |
| if (CdbPathLocus_IsStrewn(a) || |
| CdbPathLocus_IsStrewn(b)) |
| return false; |
| |
| if (CdbPathLocus_IsEqual(a, b)) |
| return true; |
| |
| if (!a.partkey || |
| !b.partkey || |
| CdbPathLocus_Degree(a) != CdbPathLocus_Degree(b)) |
| return false; |
| |
| if (a.locustype == b.locustype) |
| { |
| if (CdbPathLocus_IsHashed(a)) |
| { |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *apathkey = (List *)lfirst(acell); |
| List *bpathkey = (List *)lfirst(bcell); |
| |
| if (apathkey != bpathkey) |
| return false; |
| } |
| return true; |
| } |
| |
| if (CdbPathLocus_IsHashedOJ(a)) |
| { |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *aequivpathkeylist = (List *)lfirst(acell); |
| List *bequivpathkeylist = (List *)lfirst(bcell); |
| |
| foreach(bequivpathkeycell, bequivpathkeylist) |
| { |
| List *bpathkey = (List *)lfirst(bequivpathkeycell); |
| |
| if (!list_member_ptr(aequivpathkeylist, bpathkey)) |
| return false; |
| } |
| if (op == CdbPathLocus_Comparison_Equal) |
| { |
| foreach(aequivpathkeycell, aequivpathkeylist) |
| { |
| List *apathkey = (List *)lfirst(aequivpathkeycell); |
| |
| if (!list_member_ptr(bequivpathkeylist, apathkey)) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| } |
| |
| if (CdbPathLocus_IsHashedOJ(a) && |
| CdbPathLocus_IsHashed(b)) |
| { |
| if (op == CdbPathLocus_Comparison_Equal) |
| CdbSwap(CdbPathLocus, a, b); |
| else |
| { |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *aequivpathkeylist = (List *)lfirst(acell); |
| List *bpathkey = (List *)lfirst(bcell); |
| |
| if (!list_member_ptr(aequivpathkeylist, bpathkey)) |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| if (CdbPathLocus_IsHashed(a) && |
| CdbPathLocus_IsHashedOJ(b)) |
| { |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *apathkey = (List *)lfirst(acell); |
| List *bequivpathkeylist = (List *)lfirst(bcell); |
| |
| foreach(bequivpathkeycell, bequivpathkeylist) |
| { |
| List *bpathkey = (List *)lfirst(bequivpathkeycell); |
| |
| if (apathkey != bpathkey) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| Assert(false); |
| return false; |
| } /* cdbpathlocus_compare */ |
| |
| /* |
| * cdb_build_distribution_pathkeys |
| * Build canonicalized pathkeys list for given columns of rel. |
| * |
| * Returns a List, of length 'nattrs': each of its members is |
| * a List of one or more PathKeyItem nodes. The returned List |
| * might contain duplicate entries: this occurs when the |
| * corresponding columns are constrained to be equal. |
| * |
| * The caller receives ownership of the returned List, freshly |
| * palloc'ed in the caller's context. The members of the returned |
| * List are shared and might belong to the caller's context or |
| * other contexts. |
| */ |
| static List * |
| cdb_build_distribution_pathkeys(PlannerInfo *root, |
| RelOptInfo *rel, |
| int nattrs, |
| AttrNumber *attrs) |
| { |
| List *retval = NIL; |
| List *eq = list_make1(makeString("=")); |
| int i; |
| bool isAppendChildRelation = false; |
| |
| isAppendChildRelation = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL); |
| |
| for (i = 0; i < nattrs; ++i) |
| { |
| List *cpathkey = NIL; |
| |
| /* Find or create a Var node that references the specified column. */ |
| Var *expr = find_indexkey_var(root, rel, attrs[i]); |
| Assert(expr); |
| |
| /* |
| * Find or create a pathkey. We distinguish two cases for performance reasons: |
| * 1) If the relation in question is a child relation under an append node, we don't care |
| * about ensuring that we return a canonicalized version of its pathkey item. |
| * Co-location of joins/group-bys happens at the append relation level. |
| * In create_append_path(), the call to cdbpathlocus_pull_above_projection() ensures |
| * that canonicalized pathkeys are created at the append relation level. |
| * (see MPP-3536). |
| * |
| * 2) For regular relations, we create a canonical pathkey so that we may identify |
| * co-location for joins/group-bys. |
| */ |
| if (isAppendChildRelation) |
| { |
| /** |
| * Append child relation. |
| */ |
| PathKeyItem *item = NULL; |
| #ifdef DISTRIBUTION_PATHKEYS_DEBUG |
| List *canonicalPathKeyList = NIL; |
| canonicalPathKeyList = cdb_make_pathkey_for_expr(root, (Node *)expr, eq); |
| /* |
| * This assert ensures that we should not really find any equivalent keys |
| * during canonicalization for append child relations. |
| */ |
| Assert(list_length(canonicalPathKeyList) == 1); |
| #endif |
| item = cdb_make_pathkey_for_expr_non_canonical(root, (Node *)expr, eq); |
| Assert(item); |
| cpathkey = list_make1(item); |
| } |
| else |
| { |
| /** |
| * Regular relation. |
| */ |
| cpathkey = cdb_make_pathkey_for_expr(root, (Node *)expr, eq); |
| } |
| Assert(cpathkey); |
| |
| /* Append to list of pathkeys. */ |
| retval = lappend(retval, cpathkey); |
| } |
| list_free_deep(eq); |
| return retval; |
| } /* cdb_build_distribution_pathkeys */ |
| |
| /* |
| * cdbpathlocus_from_baserel |
| * |
| * Returns a locus describing the distribution of a base relation. |
| */ |
| CdbPathLocus |
| cdbpathlocus_from_baserel(struct PlannerInfo *root, |
| struct RelOptInfo *rel) |
| { |
| CdbPathLocus result; |
| GpPolicy *policy = rel->cdbpolicy; |
| bool allocatedResource = (root->glob->resource != NULL); |
| |
| if ( Gp_role != GP_ROLE_DISPATCH ) |
| { |
| CdbPathLocus_MakeEntry(&result); |
| return result; |
| } |
| |
| if (policy && policy->ptype == POLICYTYPE_PARTITIONED) |
| { |
| /* Are the rows distributed by hashing on specified columns? */ |
| bool isRelationRuntimeHash = true; |
| if (allocatedResource && root->glob->relsType != NIL) { |
| List* relsType = root->glob->relsType; |
| Oid baseRelOid = 0; |
| ListCell *lc; |
| int pindex = 1; |
| foreach(lc, root->parse->rtable) |
| { |
| if (rel->relid == pindex) { |
| RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); |
| baseRelOid = rte->relid; |
| break; |
| } |
| pindex++; |
| } |
| foreach(lc, relsType) |
| { |
| CurrentRelType *relType = (CurrentRelType *) lfirst(lc); |
| if (relType->relid == baseRelOid) { |
| isRelationRuntimeHash = relType->isHash; |
| } |
| } |
| } |
| /* we determine the runtime table distribution type here*/ |
| if (isRelationRuntimeHash && (policy->nattrs > 0) && |
| ((allocatedResource && list_length(root->glob->resource->segments) == policy->bucketnum) || !allocatedResource)) |
| { |
| List *partkey = cdb_build_distribution_pathkeys(root,rel, policy->nattrs, policy->attrs); |
| CdbPathLocus_MakeHashed(&result, partkey); |
| } |
| /* Rows are distributed on an unknown criterion (uniformly, we hope!) */ |
| else |
| CdbPathLocus_MakeStrewn(&result); |
| } |
| |
| /* Kludge used internally for querying catalogs on segment dbs */ |
| else if (cdbpathlocus_querysegmentcatalogs) |
| CdbPathLocus_MakeStrewn(&result); |
| |
| /* Normal catalog access */ |
| else |
| CdbPathLocus_MakeEntry(&result); |
| |
| return result; |
| } /* cdbpathlocus_from_baserel */ |
| |
| |
| /* |
| * cdbpathlocus_from_exprs |
| * |
| * Returns a locus specifying hashed distribution on a list of exprs. |
| */ |
| CdbPathLocus |
| cdbpathlocus_from_exprs(struct PlannerInfo *root, |
| List *hash_on_exprs) |
| { |
| CdbPathLocus locus; |
| List *partkey = NIL; |
| List *eq = list_make1(makeString("=")); |
| ListCell *cell; |
| |
| foreach(cell, hash_on_exprs) |
| { |
| Node *expr = (Node *)lfirst(cell); |
| List *pathkey; |
| |
| pathkey = cdb_make_pathkey_for_expr(root, expr, eq); |
| partkey = lappend(partkey, pathkey); |
| } |
| |
| CdbPathLocus_MakeHashed(&locus, partkey); |
| list_free_deep(eq); |
| return locus; |
| } /* cdbpathlocus_from_exprs */ |
| |
| |
| /* |
| * cdbpathlocus_from_subquery |
| * |
| * Returns a locus describing the distribution of a subquery. |
| * The subquery plan should have been generated already. |
| * |
| * 'subqplan' is the subquery plan. |
| * 'subqrelid' is the subquery RTE index in the current query level, for |
| * building Var nodes that reference the subquery's result columns. |
| */ |
| CdbPathLocus |
| cdbpathlocus_from_subquery(struct PlannerInfo *root, |
| struct Plan *subqplan, |
| Index subqrelid) |
| { |
| CdbPathLocus locus; |
| Flow *flow = subqplan->flow; |
| |
| Insist(flow); |
| |
| /* Flow node was made from CdbPathLocus by cdbpathtoplan_create_flow() */ |
| switch (flow->flotype) |
| { |
| case FLOW_SINGLETON: |
| if (flow->segindex == -1) |
| CdbPathLocus_MakeEntry(&locus); |
| else |
| CdbPathLocus_MakeSingleQE(&locus); |
| break; |
| case FLOW_REPLICATED: |
| CdbPathLocus_MakeReplicated(&locus); |
| break; |
| case FLOW_PARTITIONED: |
| { |
| List *partkey = NIL; |
| ListCell *hashexprcell; |
| List *eq = list_make1(makeString("=")); |
| foreach(hashexprcell, flow->hashExpr) |
| { |
| Expr *expr = (Expr *)lfirst(hashexprcell); |
| TargetEntry *tle; |
| Var *var; |
| List *pathkey; |
| |
| /* Look for hash key expr among the subquery result columns. */ |
| tle = tlist_member_ignoring_RelabelType(expr, subqplan->targetlist); |
| if (!tle) |
| break; |
| |
| Assert(tle->resno >= 1); |
| var = makeVar(subqrelid, |
| tle->resno, |
| exprType((Node *) tle->expr), |
| exprTypmod((Node *) tle->expr), |
| 0); |
| pathkey = cdb_make_pathkey_for_expr(root, (Node *)var, eq); |
| partkey = lappend(partkey, pathkey); |
| } |
| if (partkey && |
| !hashexprcell) |
| CdbPathLocus_MakeHashed(&locus, partkey); |
| else |
| CdbPathLocus_MakeStrewn(&locus); |
| list_free_deep(eq); |
| break; |
| } |
| default: |
| CdbPathLocus_MakeNull(&locus); |
| Insist(0); |
| } |
| return locus; |
| } /* cdbpathlocus_from_subquery */ |
| |
| |
| /* |
| * cdbpathlocus_get_partkey_exprs |
| * |
| * Returns a List with one Expr for each partkey column. Each item either is |
| * in the given targetlist, or has no Var nodes that are not in the targetlist; |
| * and uses only rels in the given set of relids. Returns NIL if the |
| * partkey cannot be expressed in terms of the given relids and targetlist. |
| */ |
| List * |
| cdbpathlocus_get_partkey_exprs(CdbPathLocus locus, |
| Bitmapset *relids, |
| List *targetlist) |
| { |
| List *result = NIL; |
| ListCell *partkeycell; |
| |
| Assert(cdbpathlocus_is_valid(locus)); |
| foreach(partkeycell, locus.partkey) |
| { |
| List *pathkey; |
| PathKeyItem *item = NULL; |
| |
| if (CdbPathLocus_IsHashed(locus)) |
| { |
| pathkey = (List *)lfirst(partkeycell); |
| item = cdbpullup_findPathKeyItemInTargetList(pathkey, |
| relids, |
| targetlist, |
| NULL); |
| } |
| else if (CdbPathLocus_IsHashedOJ(locus)) |
| { |
| List *pathkeylist = (List *)lfirst(partkeycell); |
| ListCell *pathkeylistcell; |
| foreach(pathkeylistcell, pathkeylist) |
| { |
| pathkey = (List *)lfirst(pathkeylistcell); |
| item = cdbpullup_findPathKeyItemInTargetList(pathkey, |
| relids, |
| targetlist, |
| NULL); |
| if (item) |
| break; |
| } |
| } |
| else |
| Assert(0); |
| |
| /* Fail if can't evaluate partkey in the context of this targetlist. */ |
| if (!item) |
| return NIL; |
| |
| result = lappend(result, copyObject(item->key)); |
| } |
| return result; |
| } /* cdbpathlocus_get_partkey_exprs */ |
| |
| |
| /* |
| * cdbpathlocus_pull_above_projection |
| * |
| * Given a targetlist, and a locus evaluable before the projection is |
| * applied, returns an equivalent locus evaluable after the projection. |
| * Returns a strewn locus if the necessary inputs are not projected. |
| * |
| * 'relids' is the set of relids that may occur in the targetlist exprs. |
| * 'targetlist' specifies the projection. It is a List of TargetEntry |
| * or merely a List of Expr. |
| * 'newvarlist' is an optional List of Expr, in 1-1 correspondence with |
| * 'targetlist'. If specified, instead of creating a Var node to |
| * reference a targetlist item, we plug in a copy of the corresponding |
| * newvarlist item. |
| * 'newrelid' is the RTE index of the projected result, for finding or |
| * building Var nodes that reference the projected columns. |
| * Ignored if 'newvarlist' is specified. |
| */ |
| CdbPathLocus |
| cdbpathlocus_pull_above_projection(struct PlannerInfo *root, |
| CdbPathLocus locus, |
| Bitmapset *relids, |
| List *targetlist, |
| List *newvarlist, |
| Index newrelid) |
| { |
| CdbPathLocus newlocus; |
| ListCell *partkeycell; |
| List *newpartkey = NIL; |
| |
| Assert(cdbpathlocus_is_valid(locus)); |
| |
| if (!CdbPathLocus_IsHashed(locus) && |
| !CdbPathLocus_IsHashedOJ(locus)) |
| return locus; |
| |
| /* For each column of the partitioning key... */ |
| foreach(partkeycell, locus.partkey) |
| { |
| List *oldpathkey; |
| List *newpathkey = NIL; |
| |
| /* Get pathkey for key expr rewritten in terms of projection cols. */ |
| if (CdbPathLocus_IsHashed(locus)) |
| { |
| oldpathkey = (List *)lfirst(partkeycell); |
| newpathkey = cdb_pull_up_pathkey(root, |
| oldpathkey, |
| relids, |
| targetlist, |
| newvarlist, |
| newrelid); |
| } |
| |
| else if (CdbPathLocus_IsHashedOJ(locus)) |
| { |
| List *pathkeylist = (List *)lfirst(partkeycell); |
| ListCell *pathkeylistcell; |
| foreach(pathkeylistcell, pathkeylist) |
| { |
| oldpathkey = (List *)lfirst(pathkeylistcell); |
| newpathkey = cdb_pull_up_pathkey(root, |
| oldpathkey, |
| relids, |
| targetlist, |
| newvarlist, |
| newrelid); |
| if (newpathkey) |
| break; |
| } |
| /* |
| * NB: Targetlist might include columns from both sides of |
| * outer join "=" comparison, in which case cdb_pull_up_pathkey |
| * might succeed on pathkeys from more than one pathkeylist. |
| * The pulled-up locus could then be a HashedOJ locus, perhaps |
| * saving a Motion when an outer join is followed by UNION ALL |
| * followed by a join or aggregate. For now, don't bother. |
| */ |
| } |
| else |
| Assert(0); |
| |
| /* Fail if can't evaluate partkey in the context of this targetlist. */ |
| if (!newpathkey) |
| { |
| CdbPathLocus_MakeStrewn(&newlocus); |
| return newlocus; |
| } |
| |
| /* Assemble new partkey. */ |
| newpartkey = lappend(newpartkey, newpathkey); |
| } |
| |
| /* Build new locus. */ |
| CdbPathLocus_MakeHashed(&newlocus, newpartkey); |
| return newlocus; |
| } /* cdbpathlocus_pull_above_projection */ |
| |
| |
| /* |
| * cdbpathlocus_join |
| * |
| * Determine locus to describe the result of a join. Any necessary Motion has |
| * already been applied to the sources. |
| */ |
| CdbPathLocus |
| cdbpathlocus_join(CdbPathLocus a, CdbPathLocus b) |
| { |
| ListCell *acell; |
| ListCell *bcell; |
| List *equivpathkeylist; |
| CdbPathLocus ojlocus; |
| |
| Assert(cdbpathlocus_is_valid(a) && |
| cdbpathlocus_is_valid(b)); |
| |
| /* Do both input rels have same locus? */ |
| if (cdbpathlocus_compare(CdbPathLocus_Comparison_Equal, a, b)) |
| return a; |
| |
| /* If one rel is general or replicated, result stays with the other rel. */ |
| if (CdbPathLocus_IsGeneral(a) || |
| CdbPathLocus_IsReplicated(a)) |
| return b; |
| if (CdbPathLocus_IsGeneral(b) || |
| CdbPathLocus_IsReplicated(b)) |
| return a; |
| |
| /* This is an outer join, or one or both inputs are outer join results. */ |
| |
| Assert(CdbPathLocus_Degree(a) > 0 && |
| CdbPathLocus_Degree(a) == CdbPathLocus_Degree(b)); |
| |
| if (CdbPathLocus_IsHashed(a) && |
| CdbPathLocus_IsHashed(b)) |
| { |
| /* Zip the two pathkey lists together to make a HashedOJ locus. */ |
| CdbPathLocus_MakeHashedOJ(&ojlocus, NIL); |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *apathkey = (List *)lfirst(acell); |
| List *bpathkey = (List *)lfirst(bcell); |
| |
| equivpathkeylist = list_make2(apathkey, bpathkey); |
| ojlocus.partkey = lappend(ojlocus.partkey, equivpathkeylist); |
| } |
| Assert(cdbpathlocus_is_valid(ojlocus)); |
| return ojlocus; |
| } |
| |
| if (!CdbPathLocus_IsHashedOJ(a)) |
| CdbSwap(CdbPathLocus, a, b); |
| |
| Assert(CdbPathLocus_IsHashedOJ(a)); |
| Assert(CdbPathLocus_IsHashed(b) || |
| CdbPathLocus_IsHashedOJ(b)); |
| |
| CdbPathLocus_MakeHashedOJ(&ojlocus, NIL); |
| forboth(acell, a.partkey, bcell, b.partkey) |
| { |
| List *aequivpathkeylist = (List *)lfirst(acell); |
| |
| if (CdbPathLocus_IsHashed(b)) |
| { |
| List *bpathkey = (List *)lfirst(bcell); |
| |
| equivpathkeylist = lappend(list_copy(aequivpathkeylist), bpathkey); |
| } |
| else |
| { |
| List *bequivpathkeylist = (List *)lfirst(bcell); |
| |
| equivpathkeylist = list_union_ptr(aequivpathkeylist, |
| bequivpathkeylist); |
| } |
| ojlocus.partkey = lappend(ojlocus.partkey, equivpathkeylist); |
| } |
| Assert(cdbpathlocus_is_valid(ojlocus)); |
| return ojlocus; |
| } /* cdbpathlocus_join */ |
| |
| |
| /* |
| * cdbpathlocus_is_hashed_on_exprs |
| * |
| * This function tests whether grouping on a given set of exprs can be done |
| * in place without motion. |
| * |
| * For a hashed locus, returns false if the partkey has a column whose |
| * equivalence class contains no expr belonging to the given list. |
| */ |
| bool |
| cdbpathlocus_is_hashed_on_exprs(CdbPathLocus locus, List *exprlist) |
| { |
| ListCell *partkeycell; |
| ListCell *pathkeycell; |
| List *pathkey; |
| PathKeyItem *item; |
| |
| Assert(cdbpathlocus_is_valid(locus)); |
| |
| if (!CdbPathLocus_IsHashed(locus) && |
| !CdbPathLocus_IsHashedOJ(locus)) |
| return !CdbPathLocus_IsStrewn(locus); |
| |
| foreach(partkeycell, locus.partkey) |
| { |
| if (CdbPathLocus_IsHashed(locus)) |
| { |
| /* Does pathkey have an expr that is equal() to one in exprlist? */ |
| pathkey = (List *)lfirst(partkeycell); |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| Assert(IsA(item, PathKeyItem)); |
| if (list_member(exprlist, item->key)) |
| break; |
| } |
| if (!pathkeycell) |
| return false; |
| } |
| else /* CdbPathLocus_IsHashedOJ */ |
| { |
| List *pathkeylist = (List *)lfirst(partkeycell); |
| ListCell *pathkeylistcell; |
| foreach(pathkeylistcell, pathkeylist) |
| { |
| /* Does some expr in pathkey match some item in exprlist? */ |
| pathkey = (List *)lfirst(pathkeylistcell); |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| Assert(IsA(item, PathKeyItem)); |
| if (list_member(exprlist, item->key)) |
| break; |
| } |
| if (pathkeycell) |
| break; |
| } |
| if (!pathkeylistcell) |
| return false; |
| } |
| } |
| |
| /* Every column of the partkey contains an expr in exprlist. */ |
| return true; |
| } /* cdbpathlocus_is_hashed_on_exprs */ |
| |
| |
| /* |
| * cdbpathlocus_is_hashed_on_relids |
| * |
| * Used when a subquery predicate (such as "x IN (SELECT ...)") has been |
| * flattened into a join with the tables of the current query. A row of |
| * the cross-product of the current query's tables might join with more |
| * than one row from the subquery and thus be duplicated. Removing such |
| * duplicates after the join is called deduping. If a row's duplicates |
| * might occur in more than one partition, a Motion operator will be |
| * needed to bring them together. This function tests whether the join |
| * result (whose locus is given) can be deduped in place without motion. |
| * |
| * For a hashed locus, returns false if the partkey has a column whose |
| * equivalence class contains no Var node belonging to the given set of |
| * relids. Caller should specify the relids of the non-subquery tables. |
| */ |
| bool |
| cdbpathlocus_is_hashed_on_relids(CdbPathLocus locus, Bitmapset *relids) |
| { |
| ListCell *partkeycell; |
| ListCell *pathkeycell; |
| List *pathkey; |
| PathKeyItem *item; |
| |
| Assert(cdbpathlocus_is_valid(locus)); |
| |
| if (!CdbPathLocus_IsHashed(locus) && |
| !CdbPathLocus_IsHashedOJ(locus)) |
| return !CdbPathLocus_IsStrewn(locus); |
| |
| foreach(partkeycell, locus.partkey) |
| { |
| if (CdbPathLocus_IsHashed(locus)) |
| { |
| /* Does pathkey contain a Var whose varno is in relids? */ |
| pathkey = (List *)lfirst(partkeycell); |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| Assert(IsA(item, PathKeyItem)); |
| if (IsA(item->key, Var) && |
| bms_is_subset(item->cdb_key_relids, relids)) |
| break; |
| } |
| if (!pathkeycell) |
| return false; |
| } |
| else /* CdbPathLocus_IsHashedOJ */ |
| { |
| List *pathkeylist = (List *)lfirst(partkeycell); |
| ListCell *pathkeylistcell; |
| foreach(pathkeylistcell, pathkeylist) |
| { |
| /* Does pathkey contain a Var whose varno is in relids? */ |
| pathkey = (List *)lfirst(pathkeylistcell); |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| Assert(IsA(item, PathKeyItem)); |
| if (IsA(item->key, Var) && |
| bms_is_subset(item->cdb_key_relids, relids)) |
| break; |
| } |
| if (pathkeycell) |
| break; |
| } |
| if (!pathkeylistcell) |
| return false; |
| } |
| } |
| |
| /* Every column of the partkey contains a Var whose varno is in relids. */ |
| return true; |
| } /* cdbpathlocus_is_hashed_on_relids */ |
| |
| |
| /* |
| * cdbpathlocus_is_valid |
| * |
| * Returns true if locus appears structurally valid. |
| */ |
| bool |
| cdbpathlocus_is_valid(CdbPathLocus locus) |
| { |
| ListCell *partkeycell; |
| ListCell *pathkeycell; |
| ListCell *pathkeylistcell; |
| List *pathkey; |
| List *pathkeylist; |
| PathKeyItem *item; |
| |
| if (!CdbPathLocus_IsHashed(locus) && |
| !CdbPathLocus_IsHashedOJ(locus)) |
| { |
| if (!CdbLocusType_IsValid(locus.locustype) || |
| locus.partkey != NULL) |
| goto bad; |
| return true; |
| } |
| |
| if (!locus.partkey || |
| !IsA(locus.partkey, List)) |
| goto bad; |
| |
| foreach(partkeycell, locus.partkey) |
| { |
| if (CdbPathLocus_IsHashed(locus)) |
| { |
| pathkey = (List *)lfirst(partkeycell); |
| if (!pathkey || |
| !IsA(pathkey, List)) |
| goto bad; |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| if (!item || |
| !IsA(item, PathKeyItem)) |
| goto bad; |
| } |
| } |
| else /* CdbPathLocus_IsHashedOJ */ |
| { |
| pathkeylist = (List *)lfirst(partkeycell); |
| if (!pathkeylist || |
| !IsA(pathkeylist, List)) |
| goto bad; |
| foreach(pathkeylistcell, pathkeylist) |
| { |
| pathkey = (List *)lfirst(pathkeylistcell); |
| if (!pathkey || |
| !IsA(pathkey, List)) |
| goto bad; |
| foreach(pathkeycell, pathkey) |
| { |
| item = (PathKeyItem *)lfirst(pathkeycell); |
| if (!item || |
| !IsA(item, PathKeyItem)) |
| goto bad; |
| } |
| } |
| } |
| } |
| return true; |
| |
| bad: |
| return false; |
| } /* cdbpathlocus_is_valid */ |
| |