| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // 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. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ************************************************************************** |
| * |
| * File: PartReq.cpp |
| * Description: Partitioning Requirements |
| * Created: 11/16/1994 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ************************************************************************** |
| */ |
| |
| #include "PartReq.h" |
| #include "ItemColRef.h" |
| #include "ItemLog.h" |
| #include "ItemFunc.h" |
| #include "ReqGen.h" |
| #include "GroupAttr.h" |
| #include "RelScan.h" |
| #include "opt.h" |
| #include "str.h" |
| #include "NumericType.h" |
| #include "MiscType.h" |
| #include "NAFileSet.h" |
| |
| |
| // *********************************************************************** |
| // PartitioningRequirement |
| // *********************************************************************** |
| |
| PartitioningRequirement::~PartitioningRequirement() {} |
| |
| // --------------------------------------------------------------------- |
| // Methods for performing type-safe pointer casts. |
| // --------------------------------------------------------------------- |
| const FuzzyPartitioningRequirement* |
| PartitioningRequirement::castToFuzzyPartitioningRequirement() const |
| { return NULL; } |
| |
| const FullySpecifiedPartitioningRequirement* |
| PartitioningRequirement::castToFullySpecifiedPartitioningRequirement() const |
| { return NULL; } |
| |
| const RequireApproximatelyNPartitions* |
| PartitioningRequirement::castToRequireApproximatelyNPartitions() const |
| { return NULL; } |
| |
| const RequireExactlyOnePartition* |
| PartitioningRequirement::castToRequireExactlyOnePartition() const |
| { return NULL; } |
| |
| const RequireReplicateViaBroadcast* |
| PartitioningRequirement::castToRequireReplicateViaBroadcast() const |
| { return NULL; } |
| |
| const RequireReplicateNoBroadcast* |
| PartitioningRequirement::castToRequireReplicateNoBroadcast() const |
| { return NULL; } |
| |
| const RequireHash* |
| PartitioningRequirement::castToRequireHash() const |
| { return NULL; } |
| |
| const RequireHashDist* |
| PartitioningRequirement::castToRequireHashDist() const |
| { return NULL; } |
| |
| const RequireHash2* |
| PartitioningRequirement::castToRequireHash2() const |
| { return NULL; } |
| |
| const RequireSkewed* |
| PartitioningRequirement::castToRequireSkewed() const |
| { return NULL; } |
| |
| const RequireRange* |
| PartitioningRequirement::castToRequireRange() const |
| { return NULL; } |
| |
| const RequireRoundRobin* |
| PartitioningRequirement::castToRequireRoundRobin() const |
| { return NULL; } |
| |
| const RequireHive* |
| PartitioningRequirement::castToRequireHive() const |
| { return NULL; } |
| |
| |
| NABoolean PartitioningRequirement::partitioningKeyIsSpecified() const |
| { |
| ABORT("PartitioningRequirement::partitioningKeyIsSpecified() needs to be redefined"); |
| return FALSE; |
| } |
| |
| PartitioningFunction * PartitioningRequirement::realize( |
| const Context *myContext, |
| NABoolean useContextPartitioningRequirements, |
| const PartitioningRequirement * softRequirements) |
| { |
| ABORT("Redefine PartitioningRequirement::realize()"); |
| return NULL; |
| } |
| |
| const NAString PartitioningRequirement::getText() const |
| { |
| if (castToFullySpecifiedPartitioningRequirement()) |
| { |
| return castToFullySpecifiedPartitioningRequirement()-> |
| getPartitioningFunction()->getText(); |
| } |
| else |
| { |
| NAString result(CmpCommon::statementHeap()); |
| |
| if (getCountOfPartitions() != ANY_NUMBER_OF_PARTITIONS) |
| { |
| char numString[30]; |
| if (castToRequireApproximatelyNPartitions()) |
| { |
| Lng32 lo, hi; |
| hi = getCountOfPartitions(); |
| #pragma nowarn(1506) // warning elimination |
| lo = hi - (Lng32) (hi * castToRequireApproximatelyNPartitions()-> |
| getAllowedDeviation()); |
| #pragma warn(1506) // warning elimination |
| // if rounded down we may have to add one |
| if (NOT castToRequireApproximatelyNPartitions()-> |
| isPartitionCountWithinRange(lo) OR |
| lo == 0) |
| lo++; |
| sprintf(numString,"%d...%d partns. ",lo,hi); |
| result += numString; |
| } |
| else |
| { |
| sprintf(numString,"%d partns. ", getCountOfPartitions()); |
| result += numString; |
| } |
| } |
| |
| if (NOT getPartitioningKey().isEmpty()) |
| { |
| if (result.length()) |
| result += " on ("; |
| else |
| result += "part. on ("; |
| ((ValueIdSet &)(getPartitioningKey())).unparse(result, |
| DEFAULT_PHASE, |
| EXPLAIN_FORMAT); |
| result += ")"; |
| } |
| return result; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // PartitioningRequirement::copyAndRemap() |
| // ----------------------------------------------------------------------- |
| PartitioningRequirement* |
| PartitioningRequirement::copyAndRemap |
| (ValueIdMap& map, NABoolean mapItUp) const |
| { |
| if (getPartitioningKey().entries() == 0) |
| return (PartitioningRequirement*)this; |
| PartitioningRequirement* newPartReq = copy(); // virtual copy constructor |
| newPartReq->remapIt(this, map, mapItUp); |
| return newPartReq; |
| } // PartitioningRequirement::copyAndRemap() |
| |
| // ----------------------------------------------------------------------- |
| // PartitioningRequirement::remapIt() |
| // ----------------------------------------------------------------------- |
| void PartitioningRequirement::remapIt |
| (const PartitioningRequirement* opr, |
| ValueIdMap& map, NABoolean mapItUp) |
| { |
| ABORT("Redefine PartitioningRequirement::remapIt()"); |
| |
| } // PartitioningRequirement::remapIt() |
| |
| PartitioningRequirement* |
| PartitioningRequirement::copy() const |
| { |
| ABORT("Redefine PartitioningRequirement::copy()"); |
| return NULL; |
| } |
| |
| // LCOV_EXCL_START |
| void PartitioningRequirement::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| print(ofd, indent, title); |
| } |
| |
| |
| void PartitioningRequirement::display() const { print(); } |
| |
| Lng32 PartitioningRequirement::getCountOfPartitions() const |
| { |
| ABORT("Redefine PartitioningRequirement::getCountOfPartitions()"); |
| return 1; |
| } |
| |
| const ValueIdSet &PartitioningRequirement::getPartitioningKey() const |
| { |
| static ValueIdSet nothing; |
| ABORT("Redefine PartitioningRequirement::getPartitioningKey()"); |
| return nothing; |
| } |
| |
| NABoolean PartitioningRequirement::partReqAndFuncCompatible |
| (const PartitioningFunction* other) const |
| { |
| ABORT("Redefine PartitioningRequirement::partReqAndFuncCompatible()"); |
| return FALSE; |
| } |
| |
| COMPARE_RESULT PartitioningRequirement::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| ABORT("Redefine PartitioningRequirement::comparePartReqToReq()"); |
| return INCOMPATIBLE; |
| } |
| // LCOV_EXCL_STOP |
| |
| // *********************************************************************** |
| // FuzzyPartitioningRequirement |
| // *********************************************************************** |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const FuzzyPartitioningRequirement* |
| FuzzyPartitioningRequirement::castToFuzzyPartitioningRequirement() const |
| { return this; } |
| |
| NABoolean |
| FuzzyPartitioningRequirement::partReqAndFuncCompatible |
| (const PartitioningFunction* other) const |
| { |
| ABORT("Redefine FuzzyPartitioningRequirement::partReqAndFuncCompatible()"); |
| return FALSE; |
| |
| } // FuzzyPartitioningRequirement::partReqAndFuncCompatible() |
| |
| COMPARE_RESULT FuzzyPartitioningRequirement::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| ABORT("Redefine FuzzyPartitioningRequirement::comparePartReqToReq()"); |
| return INCOMPATIBLE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // FuzzyPartitioningRequirement::remapIt() |
| // ----------------------------------------------------------------------- |
| void FuzzyPartitioningRequirement::remapIt |
| (const PartitioningRequirement* opr, |
| ValueIdMap& map, NABoolean mapItUp) |
| { |
| // Clear because rewrite insists on it being so. |
| partitioningKeyColumns_.clear(); |
| |
| if (mapItUp) |
| map.rewriteValueIdSetUp(partitioningKeyColumns_, |
| opr->castToFuzzyPartitioningRequirement()-> |
| partitioningKeyColumns_); |
| else |
| map.rewriteValueIdSetDown(opr->castToFuzzyPartitioningRequirement()-> |
| partitioningKeyColumns_, |
| partitioningKeyColumns_); |
| |
| } // FuzzyPartitioningRequirement::remapIt() |
| |
| PartitioningRequirement* |
| FuzzyPartitioningRequirement::copy() const |
| { |
| ABORT("Redefine FuzzyPartitioningRequirement::copy()"); |
| return NULL; |
| } |
| |
| // LCOV_EXCL_START |
| void FuzzyPartitioningRequirement::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| PartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void FuzzyPartitioningRequirement::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // *********************************************************************** |
| // FullySpecifiedPartitioningRequirement |
| // *********************************************************************** |
| |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const FullySpecifiedPartitioningRequirement* |
| FullySpecifiedPartitioningRequirement:: |
| castToFullySpecifiedPartitioningRequirement() const |
| { return this; } |
| |
| // ----------------------------------------------------------------------- |
| // FullySpecifiedPartitioningRequirement::partReqAndFuncCompatible() |
| // Partitioning function comparison method for all fully specified |
| // partitioning requirements. |
| // "Other" must be a synthesized partitioning function. |
| // ----------------------------------------------------------------------- |
| NABoolean |
| FullySpecifiedPartitioningRequirement::partReqAndFuncCompatible |
| (const PartitioningFunction* other) const |
| { |
| PartitioningFunction* myPartFunc = getPartitioningFunction(); |
| |
| // If my requirement does not require a log phys part func, |
| // then if the part func is a log phys dig underneath to |
| // get the physical part func. We want to compare against |
| // the physical part func because if the synthesized part func |
| // is a log phys it means we are in dp2, and in dp2 all part |
| // requirements must be physical requirements. |
| if (other->isALogPhysPartitioningFunction() AND |
| NOT myPartFunc->isALogPhysPartitioningFunction()) |
| { |
| other = other->castToLogPhysPartitioningFunction() |
| ->getPhysPartitioningFunction(); |
| } |
| |
| // Call the method that compares two part funcs to do all the |
| // work in determining if the part req and func are compatible. |
| // This method has various virtual implementations for all types of |
| // part funcs so we avoid a lot of duplicated code by using it here. |
| return (myPartFunc->comparePartFuncToFunc(*other) == SAME); |
| |
| } // FullySpecifiedPartitioningRequirement::partReqAndFuncCompatible |
| |
| // ----------------------------------------------------------------------- |
| // Comparison method for comparing a fully specified partitioning |
| // requirement against another partitioning requirement. |
| // ----------------------------------------------------------------------- |
| COMPARE_RESULT |
| FullySpecifiedPartitioningRequirement::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| // Is other a fuzzy requirement? |
| if ( other->isRequirementFuzzy() ) |
| |
| { |
| // Yes. |
| CMPASSERT(other->isRequirementApproximatelyN()); |
| |
| ValueIdSet myPartKey = getPartitioningKey(); |
| ValueIdSet otherPartKey = other->getPartitioningKey(); |
| #pragma nowarn(1506) // warning elimination |
| Lng32 myKeyCount = myPartKey.entries(); |
| #pragma warn(1506) // warning elimination |
| #pragma nowarn(1506) // warning elimination |
| Lng32 otherKeyCount = otherPartKey.entries(); |
| #pragma warn(1506) // warning elimination |
| Lng32 myPartCount = getCountOfPartitions(); |
| Lng32 otherPartCount = other->getCountOfPartitions(); |
| float otherAllowedDeviation = |
| other->castToRequireApproximatelyNPartitions()-> |
| getAllowedDeviation(); |
| COMPARE_RESULT result; |
| |
| // A fully specified requirement and a fuzzy requirement can never be |
| // the SAME. At best, a fully specified requirement is MORE than a fuzzy |
| // requirement. |
| result = MORE; |
| |
| // Compare the required number of partitions. |
| if ( (otherPartCount == ANY_NUMBER_OF_PARTITIONS) OR |
| #pragma nowarn(1506) // warning elimination |
| ((myPartCount >= (otherPartCount - |
| #pragma warn(1506) // warning elimination |
| #pragma nowarn(1506) // warning elimination |
| (otherPartCount * otherAllowedDeviation))) AND |
| #pragma warn(1506) // warning elimination |
| (myPartCount <= otherPartCount)) ) |
| result = combine_compare_results(result,MORE); |
| else |
| result = combine_compare_results(result,INCOMPATIBLE); |
| |
| // Compare the required partitioning keys. |
| if (myKeyCount > 0) |
| { |
| // The fuzzy req. specified required partitioning key columns, so |
| // the required key columns from the fully specified requirement must |
| // be a subset of the fuzzy required key columns, in which case |
| // the fully specified requirement requires MORE. Otherwise, they |
| // are INCOMPATIBLE. Note we come here even if the fully specified |
| // requirement specified no key columns. This is ok - the empty |
| // set is a subset of anything, so the result will be MORE. |
| if (otherPartKey.contains(myPartKey)) |
| result = combine_compare_results(result,MORE); |
| else |
| result = combine_compare_results(result,INCOMPATIBLE); |
| } |
| // If the fuzzy requirement specifies no partitioning key columns, |
| // the fully specified requirement requires MORE than the fuzzy req. |
| else |
| result = combine_compare_results(result,MORE); |
| |
| return result; |
| } // end if other is a fuzzy requirement |
| else // both are fully specified requirements |
| { |
| |
| PartitioningFunction* myPartFunc = getPartitioningFunction(); |
| PartitioningFunction* otherPartFunc = |
| other->castToFullySpecifiedPartitioningRequirement() |
| ->getPartitioningFunction(); |
| |
| return myPartFunc->comparePartFuncToFunc(*otherPartFunc); |
| } // end if both are fully specified requirements |
| |
| } // FullySpecifiedPartitioningFunction::comparePartReqToReq |
| |
| PartitioningFunction * FullySpecifiedPartitioningRequirement::realize( |
| const Context *myContext, |
| NABoolean useContextPartitioningRequirements, |
| const PartitioningRequirement * softRequirements) |
| { |
| // --------------------------------------------------------------------- |
| // There is only one way to realize a fully specified partitioning |
| // function: to return the actual function. However, we must make sure |
| // that the actual function satisfies the requirements from the |
| // context. |
| // --------------------------------------------------------------------- |
| if (NOT useContextPartitioningRequirements OR |
| myContext->getReqdPhysicalProperty()-> |
| getPartitioningRequirement() == NULL OR |
| myContext->getReqdPhysicalProperty()->getPartitioningRequirement()-> |
| castToFullySpecifiedPartitioningRequirement() == this OR |
| myContext->getReqdPhysicalProperty()-> |
| getPartitioningRequirement()->partReqAndFuncCompatible( |
| partitioningFunction_) |
| ) |
| return partitioningFunction_; |
| else |
| return NULL; // context requirements conflict with partitioningFunction_ |
| } |
| |
| // ----------------------------------------------------------------------- |
| // FullySpecifiedPartitioningRequirement::remapIt() |
| // ----------------------------------------------------------------------- |
| void FullySpecifiedPartitioningRequirement::remapIt |
| (const PartitioningRequirement* opr, |
| ValueIdMap& map, NABoolean mapItUp) |
| { |
| CMPASSERT(opr->castToFullySpecifiedPartitioningRequirement()); // I am pretty sure this will die every time |
| partitioningFunction_ = opr->castToFullySpecifiedPartitioningRequirement()-> |
| partitioningFunction_->copyAndRemap(map,mapItUp); |
| |
| } // FullySpecifiedPartitioningRequirement::remapIt() |
| |
| PartitioningRequirement* |
| FullySpecifiedPartitioningRequirement::copy() const |
| { |
| ABORT("Redefine FullySpecifiedPartitioningRequirement::copy()"); |
| return NULL; |
| } |
| |
| // LCOV_EXCL_START |
| void FullySpecifiedPartitioningRequirement::print |
| (FILE* ofd, const char* indent, const char* title) const |
| { |
| PartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void FullySpecifiedPartitioningRequirement::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireApproximatelyNPartitions |
| // ----------------------------------------------------------------------- |
| RequireApproximatelyNPartitions::RequireApproximatelyNPartitions( |
| const ValueIdSet& partitioningKeyColumns, |
| float numOfPartsAllowedDeviation, |
| Lng32 numberOfPartitions, |
| NABoolean requireHash2Only |
| ,const skewProperty& sk |
| ) |
| : FuzzyPartitioningRequirement(APPROXIMATELY_N_PART_REQ, |
| partitioningKeyColumns, |
| numberOfPartitions |
| , sk |
| ), |
| numOfPartsAllowedDeviation_ (numOfPartsAllowedDeviation), |
| requireHash2Only_ (requireHash2Only) |
| { |
| |
| CMPASSERT(numOfPartsAllowedDeviation_ >= 0.0 AND |
| numOfPartsAllowedDeviation_ <= 1.0); |
| |
| CMPASSERT(numberOfPartitions > 0 OR |
| numberOfPartitions == ANY_NUMBER_OF_PARTITIONS); |
| } |
| |
| RequireApproximatelyNPartitions::RequireApproximatelyNPartitions( |
| float numOfPartsAllowedDeviation, |
| Lng32 numberOfPartitions, |
| NABoolean requireHash2Only |
| ,const skewProperty& sk |
| ) |
| : FuzzyPartitioningRequirement(APPROXIMATELY_N_PART_REQ, |
| numberOfPartitions, sk |
| ), |
| numOfPartsAllowedDeviation_ (numOfPartsAllowedDeviation), |
| requireHash2Only_ (requireHash2Only) |
| { |
| |
| CMPASSERT(numOfPartsAllowedDeviation_ >= 0.0 AND |
| numOfPartsAllowedDeviation_ <= 1.0); |
| |
| CMPASSERT(numberOfPartitions > 0 OR |
| numberOfPartitions == ANY_NUMBER_OF_PARTITIONS); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireApproximatelyNPartitions* |
| RequireApproximatelyNPartitions:: |
| castToRequireApproximatelyNPartitions() const |
| { return this; } |
| |
| // ----------------------------------------------------------------------- |
| // RequireApproximatelyNPartitions::partReqAndFuncCompatible() |
| // Partitioning function comparison method for comparing a partitioning |
| // requirement that specifies an allowable deviation for the number of |
| // partitions against a synthesized partitioning function. |
| // The number of partitions that the synthesized partitioning function |
| // exhibits must be equal to the required number of partitions, or |
| // a fewer number of partitions that is within the allowed deviation range. |
| // ----------------------------------------------------------------------- |
| NABoolean |
| RequireApproximatelyNPartitions::partReqAndFuncCompatible |
| (const PartitioningFunction* other) const |
| { |
| CMPASSERT(other != NULL); |
| |
| #pragma nowarn(1506) // warning elimination |
| Lng32 reqKeyCount = getPartitioningKey().entries(); |
| #pragma warn(1506) // warning elimination |
| Lng32 reqPartCount = getCountOfPartitions(); |
| Lng32 actualPartCount = other->getCountOfPartitions(); |
| float reqAllowedDeviation = getAllowedDeviation(); |
| |
| if (isRequireHash2Only() && !other->isAHash2PartitioningFunction()) |
| return FALSE; |
| |
| // Make sure actual number of partitions satisfies this requirement. |
| if ( NOT isPartitionCountWithinRange(actualPartCount) ) |
| { |
| return FALSE; |
| } |
| |
| // Check if the actual partitioning key columns are compatible |
| // with the required partitioning key columns. |
| if (reqKeyCount == 0) |
| // no required part key columns |
| return TRUE; |
| |
| if ( getSkewProperty().isAnySkew() ) { |
| // If no specific skew requirement is present, requiring the partkey has |
| // the complete containment relationship with the partition func. |
| return (getPartitioningKey().contains(other->getPartitioningKey())); |
| } else { |
| |
| // If the skew requirement is present, requiring that the skew property |
| // matches |
| if ((NOT other->isASkewedDataPartitioningFunction()) OR |
| (NOT (getSkewProperty() == |
| other->castToSkewedDataPartitioningFunction()->getSkewProperty())) |
| ) |
| return FALSE; |
| |
| return (getPartialPartitioningKey() == other->getPartialPartitioningKey()); |
| } |
| |
| return FALSE; |
| |
| } // RequireApproximatelyNPartitions::partReqAndFuncCompatible() |
| |
| // ----------------------------------------------------------------------- |
| // RequireApproximatelyNPartitions::comparePartReqToReq() |
| // Partitioning function comparison method for comparing a partitioning |
| // requirement that is not an actual partitioning function against |
| // another partitioning requirement. Used when comparing two different |
| // partitioning requirements from two different contexts. |
| // ----------------------------------------------------------------------- |
| COMPARE_RESULT |
| RequireApproximatelyNPartitions::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| CMPASSERT(other != NULL); |
| |
| ValueIdSet myPartKey = getPartitioningKey(); |
| ValueIdSet otherPartKey = other->getPartitioningKey(); |
| #pragma nowarn(1506) // warning elimination |
| Lng32 myKeyCount = myPartKey.entries(); |
| #pragma warn(1506) // warning elimination |
| #pragma nowarn(1506) // warning elimination |
| Lng32 otherKeyCount = otherPartKey.entries(); |
| #pragma warn(1506) // warning elimination |
| Lng32 myPartCount = getCountOfPartitions(); |
| Lng32 otherPartCount = other->getCountOfPartitions(); |
| float myAllowedDeviation = getAllowedDeviation(); |
| #pragma nowarn(1506) // warning elimination |
| float myLowerBound = myPartCount - (myPartCount * myAllowedDeviation); |
| #pragma warn(1506) // warning elimination |
| |
| COMPARE_RESULT result; |
| |
| // If other is a replication requirement, then the result must be |
| // INCOMPATIBLE, because replication requirements are only compatible |
| // with themselves. |
| if (other->isRequirementReplicateViaBroadcast() OR |
| other->isRequirementReplicateNoBroadcast()) |
| result = INCOMPATIBLE; |
| else if (other->isRequirementFullySpecified()) |
| { |
| // A fuzzy requirement and a fully specified requirement can never be |
| // the SAME. At best, a fuzzy requirement is LESS than a fully specified |
| // requirement. |
| result = LESS; |
| |
| // Compare the required number of partitions. |
| if ( (myPartCount == ANY_NUMBER_OF_PARTITIONS) OR |
| #pragma nowarn(1506) // warning elimination |
| ((otherPartCount >= myLowerBound) AND |
| #pragma warn(1506) // warning elimination |
| (otherPartCount <= myPartCount)) ) |
| result = combine_compare_results(result,LESS); |
| else |
| result = combine_compare_results(result,INCOMPATIBLE); |
| |
| // Compare the required partitioning keys. |
| if (myKeyCount > 0) |
| { |
| // The fuzzy requirement specified required partitioning key columns, |
| // so the required key columns from the fully specified requirement |
| // must be a subset of the fuzzy required key columns, in which case |
| // the fuzzy requirement requires LESS. Otherwise, they are |
| // INCOMPATIBLE. Note we come here even if the fully specified |
| // requirement specified no key columns. This is ok - the empty |
| // set is a subset of anything, so the result will be LESS. |
| if (myPartKey.contains(otherPartKey)) |
| result = combine_compare_results(result,LESS); |
| else |
| result = combine_compare_results(result,INCOMPATIBLE); |
| } |
| // If the fuzzy requirement specifies no partitioning key columns, |
| // the fuzzy requirement requires LESS than the fully specified req. |
| else |
| result = combine_compare_results(result,LESS); |
| |
| // specifically check when the other is a required Skew requirement |
| if ( other->isRequirementSkewed() AND |
| NOT (getSkewProperty() == |
| ((const RequireSkewed*)(other))->getSkewProperty() |
| ) |
| ) |
| result = combine_compare_results(result, INCOMPATIBLE); |
| |
| |
| // If the fuzzy requirement requires hash2 and the part func |
| // of the fully specified requirement is not hash2, then they |
| // are INCOMPATIBLE. |
| PartitioningFunction* opf = |
| other->castToFullySpecifiedPartitioningRequirement() |
| ->getPartitioningFunction(); |
| if (isRequireHash2Only() AND !opf->isAHash2PartitioningFunction()) |
| result = combine_compare_results(result, INCOMPATIBLE); |
| } |
| else // other requirement is also a fuzzy requirement |
| { |
| CMPASSERT(other->isRequirementApproximatelyN()); |
| |
| // Start off assuming the two are the SAME. |
| result = SAME; |
| |
| // The two must be the same type of fuzzy requirement. |
| // (Today there is only one kind). |
| if (getPartitioningRequirementType() != |
| other->castToFuzzyPartitioningRequirement() |
| ->getPartitioningRequirementType()) |
| result = INCOMPATIBLE; |
| |
| float otherAllowedDeviation = |
| other->castToRequireApproximatelyNPartitions()-> |
| getAllowedDeviation(); |
| float otherLowerBound = |
| #pragma nowarn(1506) // warning elimination |
| otherPartCount - (otherPartCount * otherAllowedDeviation); |
| #pragma warn(1506) // warning elimination |
| |
| if (myPartCount == otherPartCount) |
| { |
| if ((myPartCount == ANY_NUMBER_OF_PARTITIONS) OR |
| (myAllowedDeviation == otherAllowedDeviation)) |
| result = combine_compare_results(result,SAME); |
| else if (myAllowedDeviation < otherAllowedDeviation) |
| result = combine_compare_results(result, MORE); |
| else // (my AllowedDeviation > otherAllowedDeviation) |
| result = combine_compare_results(result, LESS); |
| } |
| else if (myPartCount == ANY_NUMBER_OF_PARTITIONS) |
| result = combine_compare_results(result, LESS); |
| else if (otherPartCount == ANY_NUMBER_OF_PARTITIONS) |
| result = combine_compare_results(result, MORE); |
| else // # of parts are different and neither said they didn't care |
| { |
| if (myPartCount > otherPartCount) // my # of parts is largest |
| { |
| #pragma nowarn(1506) // warning elimination |
| if (otherPartCount >= myLowerBound) //overlap |
| #pragma warn(1506) // warning elimination |
| { |
| // Only if other's lower bound is greater than my lower bound |
| // can we definitely say LESS. |
| // Otherwise, it might be possible for a plan for other to not |
| // satisfy our requirements, if the # of parts for that plan |
| // was not in the overlap region. |
| if (otherLowerBound >= myLowerBound) |
| result = combine_compare_results(result, LESS); |
| else |
| // Other's plan may be in the overlap region, or may not. |
| // We don't know - so the result is UNDEFINED. |
| result = combine_compare_results(result, UNDEFINED); |
| } |
| else |
| // No overlap - so they are INCOMPATIBLE. |
| result = combine_compare_results(result, INCOMPATIBLE); |
| } |
| else // other's # of parts is larger |
| { |
| #pragma nowarn(1506) // warning elimination |
| if (myPartCount >= otherLowerBound) //overlap |
| #pragma warn(1506) // warning elimination |
| { |
| // Only if my lower bound is greater than other's lower bound |
| // can we definitely say MORE. |
| // Otherwise, it might be possible for a plan for me to not |
| // satisfy other's requirements, if the # of parts for my plan |
| // was not in the overlap region. |
| if (myLowerBound >= otherLowerBound) |
| result = combine_compare_results(result, MORE); |
| else |
| // My plan may be in the overlap region, or may not. |
| // We don't know - so the result is UNDEFINED. |
| result = combine_compare_results(result, UNDEFINED); |
| } |
| else |
| // No overlap - so they are INCOMPATIBLE. |
| result = combine_compare_results(result, INCOMPATIBLE); |
| } |
| } // end if # of parts are different |
| |
| // Check if the required partitioning key columns are compatible. |
| if ((myKeyCount > 0) AND (otherKeyCount > 0)) |
| // both partitioning requirements have non-empty required part keys |
| { |
| if (myPartKey.contains(otherPartKey) AND |
| otherPartKey.contains(myPartKey)) |
| // Other is a subset of me, and I am a subset of other, |
| // so we contain the exact same columns (although not necessarily |
| // in the same order). So we both require the same thing. |
| result = combine_compare_results(result,SAME); |
| else if (myPartKey.contains(otherPartKey)) |
| // Other is a subset of me, so I require less |
| result = combine_compare_results(result,LESS); |
| else if (otherPartKey.contains(myPartKey)) |
| // I am a subset of other, so I require more |
| result = combine_compare_results(result,MORE); |
| else |
| { |
| myPartKey.intersectSet(otherPartKey); |
| if (NOT myPartKey.isEmpty() OR |
| (((myPartCount == ANY_NUMBER_OF_PARTITIONS) OR |
| (myLowerBound <= 1)) AND |
| ((otherPartCount == ANY_NUMBER_OF_PARTITIONS) OR |
| (otherLowerBound <= 1)) |
| ) |
| ) |
| // Overlapping part keys, or the part keys are disjoint but |
| // both requirements allowed a part. func. of 1 partition. |
| // There is a chance that the synthesized partitioning |
| // function for other will be 1 part, which will satisfy this |
| // requirement. We don't know for sure - so the results |
| // are undefined. |
| result = combine_compare_results(result,UNDEFINED); |
| else |
| // Disjoint part keys, and the # of parts required by at least |
| // one of the requirements excluded a single partition part func. |
| // So the solution for other will definitely not satisfy the |
| // current requirement. |
| result = combine_compare_results(result,INCOMPATIBLE); |
| } // end if part keys are overlapping or disjoint |
| } // end if both specified part keys |
| else if ((myKeyCount == 0) AND (otherKeyCount == 0)) |
| // Both partitioning requirements have empty required part keys - |
| // so both require the same thing. |
| result = combine_compare_results(result,SAME); |
| else if (myKeyCount == 0) |
| // I have no required part key columns, but the other does - |
| // so I require less. |
| result = combine_compare_results(result,LESS); |
| else if (otherKeyCount == 0) |
| // I have required part key columns, but the other does not - |
| // so I require more. |
| result = combine_compare_results(result,MORE); |
| |
| if ( NOT (getSkewProperty() == |
| ((const FuzzyPartitioningRequirement*)(other))->getSkewProperty() |
| ) |
| ) |
| result = combine_compare_results(result, INCOMPATIBLE); |
| |
| |
| // The other's requireHash2Only_ flag. |
| NABoolean isOtherRequireHash2Only = |
| other->castToRequireApproximatelyNPartitions()->isRequireHash2Only(); |
| |
| // If requireHash2Only_ flag is same for both, then we require SAME. |
| if (isRequireHash2Only() == isOtherRequireHash2Only) |
| result = combine_compare_results(result, SAME); |
| // I dont require hash2 only and the other does, so I require LESS. |
| else if (!isRequireHash2Only() AND isOtherRequireHash2Only) |
| result = combine_compare_results(result, LESS); |
| // I require hash2 only and the other doesn't, so I require MORE. |
| else // if (isRequireHash2Only() AND !isOtherRequireHash2Only) |
| result = combine_compare_results(result, MORE); |
| |
| } // end if both are fuzzy requirements |
| |
| return result; |
| |
| } // RequireApproximatelyNPartitions::comparePartReqToReq() |
| |
| //<pb> |
| //============================================================================== |
| // Determine if a specified number of partitions satisfies this partitioning |
| // requirement. |
| // |
| // INPUT: |
| // numOfParts -- specified number of partitions. |
| // |
| // OUTPUT: |
| // none |
| // |
| // RETURN: |
| // TRUE if specified number of partitions satisfies this requirement; |
| // FALSE otherwise. |
| // |
| //============================================================================== |
| NABoolean |
| RequireApproximatelyNPartitions::isPartitionCountWithinRange(Lng32 numOfParts) |
| const |
| { |
| |
| Lng32 reqPartCount = getCountOfPartitions(); |
| |
| //-------------------------------------------------------------------- |
| // Any number of partitions satisfy this requirement, so return TRUE. |
| //-------------------------------------------------------------------- |
| if (reqPartCount == ANY_NUMBER_OF_PARTITIONS) |
| { |
| return TRUE; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // See if specified number of partitions fails a bounds test. The upper |
| // bound is the required partition count. The lower bound is the upper bound |
| // less a given percentage deviation of that upper bound. The specified |
| // partition must fall between the lower and upper bound (inclusive). |
| //--------------------------------------------------------------------------- |
| #pragma nowarn(1506) // warning elimination |
| float lowerBound = reqPartCount - (reqPartCount * getAllowedDeviation()); |
| #pragma warn(1506) // warning elimination |
| if ( numOfParts > reqPartCount |
| #pragma nowarn(1506) // warning elimination |
| OR numOfParts < lowerBound ) |
| #pragma warn(1506) // warning elimination |
| { |
| return FALSE; |
| } |
| |
| //-------------------------------------- |
| // Passed bounds check, so return TRUE. |
| //-------------------------------------- |
| return TRUE; |
| |
| } // RequireApproximatelyNPartitions::isPartitionCountWithinRange() |
| |
| |
| // accesor method for the lower bound of the range for the |
| // allowed number of partitions |
| Lng32 RequireApproximatelyNPartitions::getCountOfPartitionsLowBound() const |
| { |
| Lng32 numberOfPartitions = getCountOfPartitions(); |
| if ( numberOfPartitions > 0) |
| { |
| // this formula should be consistent with the way allowed |
| // deviation is calculated. In the first version I used |
| // (numberOfPartitions * (1-getAllowedDeviation()) which |
| // could be a little smaller but this low bond could be |
| // out of range, for example, previous formula would give 8 |
| // partitions which does not satisfy 9..N fuzzy requirement |
| Lng32 numOfPartsLowBound = MINOF(numberOfPartitions, |
| (Lng32)ceil(numberOfPartitions * (1.001-getAllowedDeviation()))); |
| // make numOfPartitions as available level of parallelism |
| // 16*N, 8*N, 4*N,..., N,1 where N is the number of segments |
| Lng32 i = CURRSTMT_OPTDEFAULTS->getMaximumDegreeOfParallelism(); |
| Lng32 MinParallelism = |
| MAXOF(CURRSTMT_OPTDEFAULTS->getMinimumESPParallelism(),numOfPartsLowBound); |
| while(i > MinParallelism) |
| i/=2; |
| numOfPartsLowBound = (i<MinParallelism) ? i*2 : i; |
| |
| return numOfPartsLowBound; |
| } |
| else |
| return numberOfPartitions; |
| |
| } //RequireApproximatelyNPartitions::getCountOfPartitionsLowBound() |
| |
| PartitioningFunction * RequireApproximatelyNPartitions::realize( |
| const Context *myContext, |
| NABoolean useContextPartitioningRequirements, |
| const PartitioningRequirement * softRequirements) |
| { |
| // --------------------------------------------------------------------- |
| // combine "this" and the partitioning requirement from the |
| // context, if this is required |
| // --------------------------------------------------------------------- |
| if (useContextPartitioningRequirements AND |
| myContext->requiresPartitioning()) |
| { |
| // We have multiple partitioning requirements, "this" and |
| // the additional requirement from the context. |
| |
| // add partitioning requirements of context to "this" |
| RequirementGenerator rg(ExprGroupId(myContext->getGroupId())); |
| |
| rg.addPartRequirement(this); |
| rg.addPartRequirement(myContext->getReqdPhysicalProperty()-> |
| getPartitioningRequirement()); |
| |
| // did the two requirements have a conflict? |
| if (rg.checkFeasibility()) |
| { |
| // no, call realize() recursively for the combined requirement |
| ReqdPhysicalProperty *rpp = rg.produceRequirement(); |
| return rpp->getPartitioningRequirement()->realize(myContext, |
| FALSE, |
| softRequirements); |
| } |
| else |
| { |
| // yes, conflicting requirements, return no actual part. func. |
| return NULL; |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // Realize a fuzzy partitioning requirement by deciding on the |
| // partitioning key, the number of partitions, and the means to |
| // achieve that partitioning (hash, range, ...). Ok, to be honest, |
| // we only consider hash for now... |
| // --------------------------------------------------------------------- |
| Lng32 numOfParts = |
| ( (CmpCommon::getDefault(COMP_BOOL_127) == DF_ON) AND |
| (CURRSTMT_OPTDEFAULTS->attemptESPParallelism() == DF_SYSTEM) |
| ) ? getCountOfPartitionsLowBound() |
| : getCountOfPartitions(); |
| |
| ValueIdSet partKey(getPartitioningKey()); |
| |
| // --------------------------------------------------------------------- |
| // Set numOfParts from "this" if specified, otherwise |
| // take it from the soft requirement, if specified there, |
| // otherwise return a SinglePartitionPartitioningFunction. |
| // --------------------------------------------------------------------- |
| if (numOfParts < 1 AND softRequirements) |
| numOfParts = softRequirements->getCountOfPartitions(); |
| |
| if (numOfParts <= 1) |
| return new(CmpCommon::statementHeap()) |
| SinglePartitionPartitioningFunction(); |
| |
| // --------------------------------------------------------------------- |
| // Set partKey from "this" if specified, otherwise set |
| // it from the soft requirement, if specified there, |
| // otherwise return a hash partitioning scheme with |
| // numOfParts partitions on arbitrary columns of the |
| // characteristic outputs of the group (could also do |
| // round robin but that would need executor work). |
| // --------------------------------------------------------------------- |
| |
| // try to fulfill both our and the "soft" requirements |
| if (NOT partitioningKeyIsSpecified()) |
| { |
| // Take the partitioning key from the soft requirements if the |
| // soft requirement partitioning key is not empty. |
| if (softRequirements AND |
| NOT softRequirements->getPartitioningKey().isEmpty()) |
| { |
| partKey = softRequirements->getPartitioningKey(); |
| } |
| else |
| { |
| // Nobody says anything about the partitioning key, now we |
| // are free to choose anything we want. To get the best |
| // possible distribution use a random number. |
| // |
| ItemExpr *randNum = |
| new(CmpCommon::statementHeap()) RandomNum(NULL, TRUE); |
| randNum->synthTypeAndValueId(); |
| partKey.insert(randNum->getValueId()); |
| } |
| } |
| |
| // We have decided how many partitions and what to partition on, |
| // now decide whether we want to use hash or range partitioning. |
| // Try to do something that is similar to the soft requirements, |
| // even if it doesn't really satisfy the soft requirements. We |
| // need to make sure that if we can produce a grouping of the soft |
| // requirements that we actually do it. |
| |
| // if (softRequirements AND |
| // softRequirements->castToFullySpecifiedPartitioningRequirement() AND |
| // partKey == softRequirements->getPartitioningKey()) |
| |
| if (softRequirements AND |
| softRequirements->castToFullySpecifiedPartitioningRequirement() |
| AND |
| ( |
| (getSkewProperty().isAnySkew() AND |
| partKey.contains(softRequirements->getPartitioningKey())) |
| || |
| (!getSkewProperty().isAnySkew() AND |
| partKey == softRequirements->getPartitioningKey()) |
| ) |
| ) |
| { |
| // try to use the (fully specified) partitioning function of the |
| // soft requirements or a scaled version of it |
| PartitioningFunction *spf = softRequirements-> |
| castToFullySpecifiedPartitioningRequirement()-> |
| getPartitioningFunction(); |
| |
| Lng32 softPartCount = spf->getCountOfPartitions(); |
| |
| // This flag is added for "better parallelism project". Otherwise |
| // softPartCount will in most cases satisfy the required range and |
| // we would not try to scale to numOfParts. |
| NABoolean shouldTryScaling = (softPartCount != numOfParts) AND |
| (CURRSTMT_OPTDEFAULTS->attemptESPParallelism() == DF_SYSTEM); |
| |
| // See if we will be grouping based on the number of active partitions |
| // and, if so, get the number of active partitions |
| Lng32 numActivePartitions = 1; |
| NABoolean baseNumPartsOnAP = FALSE; |
| if ((CmpCommon::getDefault(BASE_NUM_PAS_ON_ACTIVE_PARTS) == DF_ON) AND |
| (spf->castToRangePartitioningFunction() != NULL)) |
| { |
| baseNumPartsOnAP = TRUE; |
| CostScalar activePartitions = |
| ((NodeMap *)(spf->getNodeMap()))->getNumActivePartitions(); |
| numActivePartitions = (Lng32)activePartitions.getValue(); |
| } |
| |
| // Return if the number of partitions from the soft part reqs. |
| // satisfies the requirement and we are not trusting the number of |
| // active partitions estimate, or we are but the number of |
| // active partitions is the same as that from the soft requirement. |
| // The idea is that if we are using the number of active partitions, |
| // then we want to group the number of logical partitions down |
| // so there are not more logical partitions then active physical |
| // partitions, even if the number of physical partitions (from |
| // the soft requirements) is within range. This is so we do not |
| // end up with a lot of logical partitions (ESPs) that do nothing. |
| if (isPartitionCountWithinRange(softPartCount) AND |
| (!isRequireHash2Only() || spf->isAHash2PartitioningFunction() || |
| spf->isASinglePartitionPartitioningFunction()) AND |
| NOT shouldTryScaling AND |
| ((NOT baseNumPartsOnAP) OR (numActivePartitions == softPartCount))) |
| { |
| // allright, the fully specified soft part reqs. will do the job |
| |
| // If skew requirement is present, make sure the spf can deal with it |
| // or spf is a singlePartFunc. Otherwise do not use the spf. |
| if (NOT getSkewProperty().isAnySkew()) { |
| if (spf->canHandleSkew() AND !isRequireHash2Only()) { |
| spf = new (STMTHEAP) SkewedDataPartitioningFunction( |
| spf->copy(), |
| getSkewProperty() |
| ); |
| return spf; |
| } |
| |
| if (spf->isASinglePartitionPartitioningFunction()) |
| return spf; |
| } else { |
| spf = spf -> copy(); |
| return spf; |
| } |
| } |
| |
| // we can still try to scale the number of partitions |
| // (make sure not to trash the passed-in object and our part count) |
| Lng32 sNumOfParts = numOfParts; |
| // If we are grouping based on the number of active partitions, and |
| // there won't be enough active partitions to go around, then reduce |
| // the number of groups to the number of active partitions. Then check |
| // after the scaling to see if the resultant number of groups meets |
| // the requirement. The resultant number of partitions should meet the |
| // requirement unless the number of active partitions was too small, |
| // or the scaling failed because we were grouping based on the |
| // number of physical partitions and there were not enough physical |
| // partitions. |
| if (baseNumPartsOnAP AND |
| (sNumOfParts > numActivePartitions)) |
| sNumOfParts = numActivePartitions; |
| |
| spf = spf->copy(); |
| spf = spf->scaleNumberOfPartitions(sNumOfParts); |
| if (isPartitionCountWithinRange(sNumOfParts) AND |
| (!isRequireHash2Only() || spf->isAHash2PartitioningFunction() || |
| spf->isASinglePartitionPartitioningFunction())) |
| { |
| |
| if (NOT getSkewProperty().isAnySkew()) { |
| if (spf->canHandleSkew() AND !isRequireHash2Only()) { |
| spf = new (STMTHEAP) SkewedDataPartitioningFunction( |
| spf, |
| getSkewProperty()); |
| return spf; |
| } |
| |
| if (spf->isASinglePartitionPartitioningFunction()) |
| return spf; |
| } else |
| return spf; |
| } |
| } |
| |
| // If we come here we did not have luck with the approach to modify |
| // the soft requirements. |
| // Generate a hash partitioning function with the given key and |
| // number of partitions (or a single partition for some cases). |
| // Since a singlePartFunc can deal with skew, there is no need to check |
| // skew requirement here. |
| if (partKey.isEmpty() OR |
| numOfParts == 1 OR |
| numOfParts == ANY_NUMBER_OF_PARTITIONS) |
| return new(CmpCommon::statementHeap()) |
| SinglePartitionPartitioningFunction(); |
| |
| // At this point, we use the CQD "SOFT_REQ_HASH_TYPE" to |
| // determine which hash partitioning scheme to use. The historical |
| // method is to use modulo hash, which is used by the |
| // HashPartitioningFunction. If Hash2 seems to produce better |
| // plans with Hash2 tables, then this section of code may be |
| // changed in the future. For now a CQD is available to make |
| // testing of this possible. |
| // |
| // All these hash partfuncs can deal with skew. |
| Lng32 hash_type = CmpCommon::getDefaultLong(SOFT_REQ_HASH_TYPE); |
| PartitioningFunction* newPartFunc = NULL; |
| |
| if ( NOT (getSkewProperty().isAnySkew()) AND !isRequireHash2Only() ) { |
| |
| // Exclusively use hash2 for skew buster. This is necessary because |
| // for CHAR typed join column, we compute an surrogate representation |
| // for each skew character literal and stored it in the skew list. This |
| // reprentation is computed by using the hash2 hashing function. Without |
| // the exclusive use of hash2, we need to compute three reprentations. |
| // Besides, hash2 is always better than hash0 and hash1. |
| |
| newPartFunc = new(CmpCommon::statementHeap()) |
| Hash2PartitioningFunction(partKey, partKey, numOfParts); |
| |
| newPartFunc = new (STMTHEAP) |
| SkewedDataPartitioningFunction(newPartFunc, getSkewProperty()); |
| } else { |
| |
| |
| |
| // Check if requireHash2Only_ flag is set AND hash_type is 2 i.e. hash2. |
| // If not, return NULL; |
| if (isRequireHash2Only() AND hash_type != 2) |
| return NULL; |
| switch (hash_type) |
| { |
| case 0: |
| newPartFunc = new(CmpCommon::statementHeap()) |
| HashPartitioningFunction(partKey, partKey, numOfParts); |
| break; |
| |
| case 1: |
| newPartFunc = new(CmpCommon::statementHeap()) |
| HashDistPartitioningFunction(partKey, partKey, numOfParts); |
| break; |
| |
| case 2: |
| |
| /* |
| if (CmpCommon::getDefault(HIVE_USE_HASH2_AS_PARTFUNCION) == DF_OFF ) |
| newPartFunc = new(CmpCommon::statementHeap()) |
| HivePartitioningFunction(partKey, partKey, numOfParts); |
| else |
| */ |
| newPartFunc = new(CmpCommon::statementHeap()) |
| Hash2PartitioningFunction(partKey, partKey, numOfParts); |
| |
| break; |
| |
| default: |
| CMPASSERT(hash_type >= 0 && hash_type <= 3); |
| return NULL; // Required to prevent a compiler warning. |
| } |
| } |
| |
| return newPartFunc; |
| |
| } // RequireApproximatelyNPartitions::realize |
| |
| PartitioningRequirement* |
| RequireApproximatelyNPartitions::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireApproximatelyNPartitions(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireApproximatelyNPartitions::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FuzzyPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireApproximatelyNPartitions::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| |
| // ----------------------------------------------------------------------- |
| // RequireExactlyOnePartition |
| // ----------------------------------------------------------------------- |
| |
| RequireExactlyOnePartition::RequireExactlyOnePartition |
| (NABoolean withNodeMap) : FullySpecifiedPartitioningRequirement() |
| { |
| NodeMap *nodemap = !withNodeMap ? NULL : new(CmpCommon::statementHeap()) |
| NodeMap(CmpCommon::statementHeap(), 1, NodeMapEntry::ACTIVE); |
| PartitioningFunction* partFunc = new(CmpCommon::statementHeap()) |
| SinglePartitionPartitioningFunction(nodemap); |
| setPartitioningFunction(partFunc); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireExactlyOnePartition* |
| RequireExactlyOnePartition::castToRequireExactlyOnePartition() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireExactlyOnePartition::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireExactlyOnePartition(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireExactlyOnePartition::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireExactlyOnePartition::display() const { print(); } |
| // LCOV_EXCL_STOP |
| // ----------------------------------------------------------------------- |
| // RequireReplicateViaBroadcast |
| // ----------------------------------------------------------------------- |
| |
| RequireReplicateViaBroadcast::RequireReplicateViaBroadcast ( |
| Lng32 numOfReplicas) |
| : FullySpecifiedPartitioningRequirement() |
| { |
| PartitioningFunction* partFunc = |
| new(CmpCommon::statementHeap()) |
| ReplicateViaBroadcastPartitioningFunction(numOfReplicas); |
| |
| setPartitioningFunction(partFunc); |
| } |
| |
| // RequireReplicateViaBroadcast constructor used by HashJoin to |
| // have right child use left child's partitioning function's nodemap |
| RequireReplicateViaBroadcast::RequireReplicateViaBroadcast |
| (PartitioningFunction *childPF, NABoolean useChildsNodeMap) |
| : FullySpecifiedPartitioningRequirement() |
| { |
| PartitioningFunction* partFunc = new(CmpCommon::statementHeap()) |
| ReplicateViaBroadcastPartitioningFunction |
| (childPF->getCountOfPartitions(), childPF->getNodeMap()->copy()); |
| |
| setPartitioningFunction(partFunc); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Comparison method for comparing a replicate via broadcast |
| // requirement against another partitioning requirement. |
| // ----------------------------------------------------------------------- |
| COMPARE_RESULT |
| RequireReplicateViaBroadcast::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| if ( NOT other->isRequirementFullySpecified() ) |
| return INCOMPATIBLE; |
| |
| PartitioningFunction* myPartFunc = getPartitioningFunction(); |
| PartitioningFunction* otherPartFunc = |
| other->castToFullySpecifiedPartitioningRequirement() |
| ->getPartitioningFunction(); |
| |
| return myPartFunc->comparePartFuncToFunc(*otherPartFunc); |
| |
| } //RequireReplicateViaBroadcast::comparePartReqToReq |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireReplicateViaBroadcast* |
| RequireReplicateViaBroadcast::castToRequireReplicateViaBroadcast() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireReplicateViaBroadcast::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireReplicateViaBroadcast(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireReplicateViaBroadcast::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireReplicateViaBroadcast::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireReplicateNoBroadcast |
| // ----------------------------------------------------------------------- |
| |
| // RequireReplicateNoBroadcast constructor used by NestedJoin to |
| // have right child use parent's partitioning function's nodemap |
| RequireReplicateNoBroadcast::RequireReplicateNoBroadcast |
| (PartitioningFunction *parentPF, NABoolean useParentsNodeMap) |
| : FullySpecifiedPartitioningRequirement(), |
| parentPartFunc_(parentPF) |
| { |
| PartitioningFunction* partFunc; |
| |
| if(useParentsNodeMap) { |
| partFunc = new(CmpCommon::statementHeap()) |
| ReplicateNoBroadcastPartitioningFunction(parentPF->getCountOfPartitions(), |
| parentPF->getNodeMap()->copy()); |
| } else { |
| partFunc = new(CmpCommon::statementHeap()) |
| ReplicateNoBroadcastPartitioningFunction(parentPF->getCountOfPartitions()); |
| } |
| setPartitioningFunction(partFunc); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Comparison method for comparing a replicate with no broadcast |
| // requirement against another partitioning requirement. |
| // ----------------------------------------------------------------------- |
| COMPARE_RESULT |
| RequireReplicateNoBroadcast::comparePartReqToReq |
| (const PartitioningRequirement* other) const |
| { |
| if ( NOT other->isRequirementFullySpecified() ) |
| return INCOMPATIBLE; |
| |
| PartitioningFunction* myPartFunc = getPartitioningFunction(); |
| PartitioningFunction* otherPartFunc = |
| other->castToFullySpecifiedPartitioningRequirement() |
| ->getPartitioningFunction(); |
| |
| return myPartFunc->comparePartFuncToFunc(*otherPartFunc); |
| |
| } //RequireReplicateNoBroadcast::comparePartReqToReq |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireReplicateNoBroadcast* |
| RequireReplicateNoBroadcast::castToRequireReplicateNoBroadcast() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireReplicateNoBroadcast::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireReplicateNoBroadcast(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireReplicateNoBroadcast::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireReplicateNoBroadcast::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireHash |
| // ----------------------------------------------------------------------- |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| // LCOV_EXCL_START |
| const RequireHash* |
| RequireHash::castToRequireHash() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireHash::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireHash(*this); |
| } |
| |
| void RequireHash::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireHash::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireHashDist |
| // ----------------------------------------------------------------------- |
| |
| NABoolean |
| RequireHashDist::partReqAndFuncCompatible |
| (const PartitioningFunction* other) const |
| { |
| const HashDistPartitioningFunction *partFunc = |
| getPartitioningFunction()->castToHashDistPartitioningFunction(); |
| |
| CMPASSERT(partFunc); |
| |
| if(partFunc->comparePartFuncToFunc(*other) == SAME) |
| return TRUE; |
| |
| return FALSE; |
| |
| } // RequireHashDist::partReqAndFuncCompatible() |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireHashDist* |
| RequireHashDist::castToRequireHashDist() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireHashDist::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireHashDist(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireHashDist::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireHashDist::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireHash2 |
| // ----------------------------------------------------------------------- |
| |
| NABoolean |
| RequireHash2::partReqAndFuncCompatible(const PartitioningFunction* other) const |
| { |
| const Hash2PartitioningFunction *partFunc = |
| getPartitioningFunction()->castToHash2PartitioningFunction(); |
| |
| CMPASSERT(partFunc); |
| |
| // If my requirement does not require a log phys part func, |
| // then if the part func is a log phys dig underneath to |
| // get the physical part func. We want to compare against |
| // the physical part func because if the synthesized part func |
| // is a log phys it means we are in dp2, and in dp2 all part |
| // requirements must be physical requirements. |
| |
| if (other->isALogPhysPartitioningFunction()) |
| { |
| other = other->castToLogPhysPartitioningFunction() |
| ->getPhysPartitioningFunction(); |
| } |
| |
| return (partFunc->comparePartFuncToFunc(*other) == SAME); |
| |
| } // RequireHash2::partReqAndFuncCompatible() |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireHash2* |
| RequireHash2::castToRequireHash2() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireHash2::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireHash2(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireHash2::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireHash2::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| //=========== |
| // ----------------------------------------------------------------------- |
| // RequireSkewed |
| // ----------------------------------------------------------------------- |
| const ValueIdSet& RequireSkewed::getPartialPartitioningKey() const |
| { |
| return getPartitioningFunction() -> |
| castToSkewedDataPartitioningFunction()-> |
| getPartialPartitioningFunction()->getPartitioningKey(); |
| } |
| |
| COMPARE_RESULT |
| RequireSkewed::comparePartReqToReq(const PartitioningRequirement* other) const |
| { |
| COMPARE_RESULT result = |
| FullySpecifiedPartitioningRequirement::comparePartReqToReq(other); |
| |
| if ( other -> isRequirementSkewed() ) { |
| if ( NOT (getSkewProperty() == |
| other->castToRequireSkewed()->getSkewProperty()) ) |
| result = combine_compare_results(result,INCOMPATIBLE); |
| } else { |
| if ( NOT (other -> isRequirementFuzzy()) OR |
| (NOT (getSkewProperty() == |
| other->castToFuzzyPartitioningRequirement()->getSkewProperty())) |
| ) |
| result = combine_compare_results(result,INCOMPATIBLE); |
| } |
| |
| return result; |
| } |
| |
| const skewProperty& RequireSkewed::getSkewProperty() const |
| { |
| return getPartitioningFunction()-> |
| castToSkewedDataPartitioningFunction()-> |
| getSkewProperty(); |
| } |
| |
| NABoolean |
| RequireSkewed::partReqAndFuncCompatible(const PartitioningFunction* other) const |
| { |
| const PartitioningFunction *thisPartFunc = getPartitioningFunction(); |
| CMPASSERT(thisPartFunc->castToSkewedDataPartitioningFunction()); |
| return (thisPartFunc->comparePartFuncToFunc(*other) == SAME ); |
| } // RequireSkewed::partReqAndFuncCompatible() |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireSkewed* |
| RequireSkewed::castToRequireSkewed() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireSkewed::copy() const |
| { |
| return new (CmpCommon::statementHeap()) RequireSkewed(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireSkewed::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireSkewed::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| |
| // ----------------------------------------------------------------------- |
| // RequireRange |
| // ----------------------------------------------------------------------- |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireRange* |
| RequireRange::castToRequireRange() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireRange::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireRange(*this); |
| } |
| |
| // LCOV_EXCL_START |
| void RequireRange::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireRange::display() const { print(); } |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // RequireRoundRobin |
| // ----------------------------------------------------------------------- |
| |
| // LCOV_EXCL_START |
| NABoolean |
| RequireRoundRobin:: |
| partReqAndFuncCompatible(const PartitioningFunction* other) const |
| { |
| const RoundRobinPartitioningFunction *partFunc = |
| getPartitioningFunction()->castToRoundRobinPartitioningFunction(); |
| |
| CMPASSERT(partFunc); |
| |
| if(partFunc->comparePartFuncToFunc(*other) == SAME) |
| return TRUE; |
| |
| return FALSE; |
| |
| } // RequireHashDist::partReqAndFuncCompatible() |
| |
| // ----------------------------------------------------------------------- |
| // Method for performing a pointer type cast |
| // ----------------------------------------------------------------------- |
| const RequireRoundRobin* |
| RequireRoundRobin::castToRequireRoundRobin() const |
| { return this; } |
| |
| PartitioningRequirement* |
| RequireRoundRobin::copy() const |
| { |
| return |
| new (CmpCommon::statementHeap()) RequireRoundRobin(*this); |
| } |
| |
| void RequireRoundRobin::print(FILE* ofd, const char* indent, |
| const char* title) const |
| { |
| FullySpecifiedPartitioningRequirement::print(ofd, indent, title); |
| } |
| |
| void RequireRoundRobin::display() const { print(); } |
| |
| // LCOV_EXCL_STOP |
| |
| // ----------------------------------------------------------------------- |
| // Methods for class LogicalPartitioningRequirement |
| // ----------------------------------------------------------------------- |
| |
| COMPARE_RESULT LogicalPartitioningRequirement::compareLogPartRequirements( |
| const LogicalPartitioningRequirement &other) const |
| { |
| if (logPartType_ != other.logPartType_) |
| return INCOMPATIBLE; |
| |
| if (numClients_ != other.numClients_) |
| return INCOMPATIBLE; |
| |
| if (logPartReq_ AND other.logPartReq_) |
| return logPartReq_->comparePartReqToReq(other.logPartReq_); |
| else if (logPartReq_) |
| return MORE; |
| else if (other.logPartReq_) |
| return LESS; |
| else |
| return SAME; |
| } |
| |
| NABoolean LogicalPartitioningRequirement::satisfied( |
| const RelExpr * const physExpr, |
| const PhysicalProperty * const physProp) const |
| { |
| const PartitioningFunction *actualPartFunc = |
| physProp->getPartitioningFunction(); |
| const LogPhysPartitioningFunction *lpf = |
| actualPartFunc->castToLogPhysPartitioningFunction(); |
| |
| if (lpf == NULL) |
| { |
| // if the RelExpr does not have a LogPhysPartitioningFunction |
| // then make sure that we required only the simple properties |
| // that another partitioning function can provide |
| return ((logPartType_ == LogPhysPartitioningFunction::PA_PARTITION_GROUPING OR |
| logPartType_ == LogPhysPartitioningFunction::ANY_LOGICAL_PARTITIONING) |
| AND |
| (numClients_ == ANY_NUMBER_OF_PARTITIONS OR numClients_ == 1) |
| AND |
| (logPartReq_ == NULL OR |
| logPartReq_->partReqAndFuncCompatible(actualPartFunc) |
| ) |
| ); |
| } |
| |
| // if we required a specific type of logical partitioning, then check |
| // for it here |
| if (logPartType_ != |
| LogPhysPartitioningFunction::ANY_LOGICAL_PARTITIONING AND |
| lpf->getLogPartType() != logPartType_) |
| return FALSE; |
| |
| // don't check # of clients, it is just a suggestion |
| |
| // check forced use of PAPA |
| if (mustUsePapa_ AND |
| NOT lpf->getUsePapa()) |
| return FALSE; |
| |
| // check the logical partitioning requirement |
| if (logPartReq_ AND |
| NOT logPartReq_->partReqAndFuncCompatible( |
| lpf->getLogPartitioningFunction()) |
| ) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| NABoolean |
| FullySpecifiedPartitioningRequirement::isRequirementSkewBusterBroadcast() const |
| { |
| const SkewedDataPartitioningFunction *skpf = |
| getPartitioningFunction()->castToSkewedDataPartitioningFunction(); |
| |
| return (skpf AND skpf->getSkewProperty().isBroadcasted()); |
| } |
| |