PlanStage::StageState AndHashStage::work(WorkingSetID* out) { ++_commonStats.works; if (isEOF()) { return PlanStage::IS_EOF; } // An AND is either reading the first child into the hash table, probing against the hash // table with subsequent children, or returning results. // We read the first child into our hash table. if (_shouldScanChildren && (0 == _currentChild)) { return readFirstChild(out); } // Probing into our hash table with other children. if (_shouldScanChildren) { return hashOtherChildren(out); } // Returning results. verify(!_shouldScanChildren); // Keep the thing we're returning so we can remove it from our internal map later. DataMap::iterator returnedIt = _resultIterator; ++_resultIterator; WorkingSetID idToReturn = returnedIt->second; _dataMap.erase(returnedIt); WorkingSetMember* member = _ws->get(idToReturn); // We should check for matching at the end so the matcher can use information in the // indices of all our children. if (Filter::passes(member, _filter)) { *out = idToReturn; ++_commonStats.advanced; return PlanStage::ADVANCED; } else { _ws->free(idToReturn); // Skip over the non-matching thing we currently point at. ++_commonStats.needTime; return PlanStage::NEED_TIME; } }
PlanStage::StageState AndHashStage::work(WorkingSetID* out) { ++_commonStats.works; if (isEOF()) { return PlanStage::IS_EOF; } // An AND is either reading the first child into the hash table, probing against the hash // table with subsequent children, or checking the last child's results to see if they're // in the hash table. // We read the first child into our hash table. if (_hashingChildren) { if (0 == _currentChild) { return readFirstChild(out); } else if (_currentChild < _children.size() - 1) { return hashOtherChildren(out); } else { _hashingChildren = false; // We don't hash our last child. Instead, we probe the table created from the // previous children, returning results in the order of the last child. // Fall through to below. } } // Returning results. We read from the last child and return the results that are in our // hash map. // We should be EOF if we're not hashing results and the dataMap is empty. verify(!_dataMap.empty()); // We probe _dataMap with the last child. verify(_currentChild == _children.size() - 1); // Work the last child. StageState childStatus = _children[_children.size() - 1]->work(out); if (PlanStage::ADVANCED != childStatus) { return childStatus; } // We know that we've ADVANCED. See if the WSM is in our table. WorkingSetMember* member = _ws->get(*out); // Maybe the child had an invalidation. We intersect DiskLoc(s) so we can't do anything // with this WSM. if (!member->hasLoc()) { _ws->flagForReview(*out); return PlanStage::NEED_TIME; } DataMap::iterator it = _dataMap.find(member->loc); if (_dataMap.end() == it) { // Child's output wasn't in every previous child. Throw it out. _ws->free(*out); ++_commonStats.needTime; return PlanStage::NEED_TIME; } else { // Child's output was in every previous child. Merge any key data in // the child's output and free the child's just-outputted WSM. WorkingSetID hashID = it->second; _dataMap.erase(it); WorkingSetMember* olderMember = _ws->get(hashID); AndCommon::mergeFrom(olderMember, *member); _ws->free(*out); // We should check for matching at the end so the matcher can use information in the // indices of all our children. if (Filter::passes(olderMember, _filter)) { *out = hashID; ++_commonStats.advanced; return PlanStage::ADVANCED; } else { _ws->free(hashID); ++_commonStats.needTime; return PlanStage::NEED_TIME; } } }
PlanStage::StageState AndHashStage::work(WorkingSetID* out) { ++_commonStats.works; if (isEOF()) { return PlanStage::IS_EOF; } // Fast-path for one of our children being EOF immediately. We work each child a few times. // If it hits EOF, the AND cannot output anything. If it produces a result, we stash that // result in _lookAheadResults. if (_lookAheadResults.empty()) { // INVALID_ID means that the child didn't produce a valid result. _lookAheadResults.resize(_children.size(), WorkingSet::INVALID_ID); // Work each child some number of times until it's either EOF or produces // a result. If it's EOF this whole stage will be EOF. If it produces a // result we cache it for later. for (size_t i = 0; i < _children.size(); ++i) { PlanStage* child = _children[i]; for (size_t j = 0; j < kLookAheadWorks; ++j) { StageState childStatus = child->work(&_lookAheadResults[i]); if (PlanStage::IS_EOF == childStatus || PlanStage::DEAD == childStatus || PlanStage::FAILURE == childStatus) { // A child went right to EOF. Bail out. _hashingChildren = false; _dataMap.clear(); return PlanStage::IS_EOF; } else if (PlanStage::ADVANCED == childStatus) { // We have a result cached in _lookAheadResults[i]. Stop looking at this // child. break; } // We ignore NEED_TIME. TODO: What do we want to do if the child provides // NEED_FETCH? } } // We did a bunch of work above, return NEED_TIME to be fair. return PlanStage::NEED_TIME; } // An AND is either reading the first child into the hash table, probing against the hash // table with subsequent children, or checking the last child's results to see if they're // in the hash table. // We read the first child into our hash table. if (_hashingChildren) { if (0 == _currentChild) { return readFirstChild(out); } else if (_currentChild < _children.size() - 1) { return hashOtherChildren(out); } else { _hashingChildren = false; // We don't hash our last child. Instead, we probe the table created from the // previous children, returning results in the order of the last child. // Fall through to below. } } // Returning results. We read from the last child and return the results that are in our // hash map. // We should be EOF if we're not hashing results and the dataMap is empty. verify(!_dataMap.empty()); // We probe _dataMap with the last child. verify(_currentChild == _children.size() - 1); // Get the next result for the (_children.size() - 1)-th child. StageState childStatus = workChild(_children.size() - 1, out); if (PlanStage::ADVANCED != childStatus) { return childStatus; } // We know that we've ADVANCED. See if the WSM is in our table. WorkingSetMember* member = _ws->get(*out); // Maybe the child had an invalidation. We intersect DiskLoc(s) so we can't do anything // with this WSM. if (!member->hasLoc()) { _ws->flagForReview(*out); return PlanStage::NEED_TIME; } DataMap::iterator it = _dataMap.find(member->loc); if (_dataMap.end() == it) { // Child's output wasn't in every previous child. Throw it out. _ws->free(*out); ++_commonStats.needTime; return PlanStage::NEED_TIME; } else { // Child's output was in every previous child. Merge any key data in // the child's output and free the child's just-outputted WSM. WorkingSetID hashID = it->second; _dataMap.erase(it); WorkingSetMember* olderMember = _ws->get(hashID); AndCommon::mergeFrom(olderMember, *member); _ws->free(*out); // We should check for matching at the end so the matcher can use information in the // indices of all our children. if (Filter::passes(olderMember, _filter)) { *out = hashID; ++_commonStats.advanced; return PlanStage::ADVANCED; } else { _ws->free(hashID); ++_commonStats.needTime; return PlanStage::NEED_TIME; } } }