Ejemplo n.º 1
0
    Status EOFRunner::getInfo(TypeExplain** explain,
                              PlanInfo** planInfo) const {
        if (NULL != explain) {
            *explain = new TypeExplain;

            // Fill in mandatory fields.
            (*explain)->setN(0);
            (*explain)->setNScannedObjects(0);
            (*explain)->setNScanned(0);

            // Fill in all the main fields that don't have a default in the explain data structure.
            (*explain)->setCursor("BasicCursor");
            (*explain)->setScanAndOrder(false);
            (*explain)->setIsMultiKey(false);
            (*explain)->setIndexOnly(false);
            (*explain)->setNYields(0);
            (*explain)->setNChunkSkips(0);

            TypeExplain* allPlans = new TypeExplain;
            allPlans->setCursor("BasicCursor");
            (*explain)->addToAllPlans(allPlans); // ownership xfer

            (*explain)->setNScannedObjectsAllPlans(0);
            (*explain)->setNScannedAllPlans(0);
        }
        else if (NULL != planInfo) {
            *planInfo = new PlanInfo();
            (*planInfo)->planSummary = "EOF";
        }

        return Status::OK();
    }
Ejemplo n.º 2
0
    Status EOFRunner::getExplainPlan(TypeExplain** explain) const {
        *explain = new TypeExplain;

        // Fill in mandatory fields.
        (*explain)->setN(0);
        (*explain)->setNScannedObjects(0);
        (*explain)->setNScanned(0);

        // Fill in all the main fields that don't have a default in the explain data structure.
        (*explain)->setCursor("BasicCursor");
        (*explain)->setScanAndOrder(false);
        (*explain)->setIsMultiKey(false);
        (*explain)->setIndexOnly(false);
        (*explain)->setNYields(0);
        (*explain)->setNChunkSkips(0);

        TypeExplain* allPlans = new TypeExplain;
        allPlans->setCursor("BasicCursor");
        (*explain)->addToAllPlans(allPlans); // ownership xfer

        (*explain)->setNScannedObjectsAllPlans(0);
        (*explain)->setNScannedAllPlans(0);

        return Status::OK();
    }
Ejemplo n.º 3
0
    Status explainIntersectPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) {
        auto_ptr<TypeExplain> res(new TypeExplain);
        res->setCursor("Complex Plan");
        res->setN(stats.common.advanced);

        // Sum the various counters at the leaves.
        vector<const PlanStageStats*> leaves;
        getLeafNodes(stats, &leaves);

        long long nScanned = 0;
        long long nScannedObjects = 0;
        for (size_t i = 0; i < leaves.size(); ++i) {
            TypeExplain* leafExplain;
            explainPlan(*leaves[i], &leafExplain, false);
            nScanned += leafExplain->getNScanned();
            nScannedObjects += leafExplain->getNScannedObjects();
            delete leafExplain;
        }

        res->setNScanned(nScanned);
        // XXX: this isn't exactly "correct" -- for ixscans we have to find out if it's part of a
        // subtree rooted at a fetch, etc. etc.  do we want to just add the # of advances of a
        // fetch node minus the number of alreadyHasObj for those nodes?
        res->setNScannedObjects(nScannedObjects);

        uint64_t chunkSkips = 0;
        const PlanStageStats* shardFilter = findNode(&stats, STAGE_SHARDING_FILTER);
        if (NULL != shardFilter) {
            const ShardingFilterStats* sfs
                = static_cast<const ShardingFilterStats*>(shardFilter->specific.get());
            chunkSkips = sfs->chunkSkips;
        }

        res->setNChunkSkips(chunkSkips);

        if (fullDetails) {
            res->setNYields(stats.common.yields);
            BSONObjBuilder bob;
            statsToBSON(stats, &bob);
            res->stats = bob.obj();
        }

        *explainOut = res.release();
        return Status::OK();
    }
Ejemplo n.º 4
0
    Status explainMultiPlan(const PlanStageStats& stats,
                            const std::vector<PlanStageStats*>& candidateStats,
                            QuerySolution* solution,
                            TypeExplain** explain) {
        invariant(explain);

        Status status = explainPlan(stats, explain, true /* full details */);
        if (!status.isOK()) {
            return status;
        }

        // TODO Hook the cached plan if there was one.
        // (*explain)->setOldPlan(???);

        //
        // Alternative plans' explains
        //
        // We get information about all the plans considered and hook them up the the main
        // explain structure. If we fail to get any of them, we still return the main explain.
        // Make sure we initialize the "*AllPlans" fields with the plan that was chose.
        //

        TypeExplain* chosenPlan = NULL;
        status = explainPlan(stats, &chosenPlan, false /* no full details */);
        if (!status.isOK()) {
            return status;
        }

        (*explain)->addToAllPlans(chosenPlan); // ownership xfer

        size_t nScannedObjectsAllPlans = chosenPlan->getNScannedObjects();
        size_t nScannedAllPlans = chosenPlan->getNScanned();
        for (std::vector<PlanStageStats*>::const_iterator it = candidateStats.begin();
             it != candidateStats.end();
             ++it) {

            TypeExplain* candidateExplain = NULL;
            status = explainPlan(**it, &candidateExplain, false /* no full details */);
            if (status != Status::OK()) {
                continue;
            }

            (*explain)->addToAllPlans(candidateExplain); // ownership xfer

            nScannedObjectsAllPlans += candidateExplain->getNScannedObjects();
            nScannedAllPlans += candidateExplain->getNScanned();
        }

        (*explain)->setNScannedObjectsAllPlans(nScannedObjectsAllPlans);
        (*explain)->setNScannedAllPlans(nScannedAllPlans);

        if (NULL != solution) {
            (*explain)->setIndexFilterApplied(solution->indexFilterApplied);
        }

        return Status::OK();
    }
Ejemplo n.º 5
0
    // TODO: This is temporary and should get deleted. There are a few small ways in which
    // this differs from 2.6 explain, but I'm not too worried because this entire format is
    // going away soon:
    //  1) 'indexBounds' field excluded from idhack explain.
    //  2) 'filterSet' field (for index filters) excluded.
    Status Explain::legacyExplain(PlanExecutor* exec, TypeExplain** explain) {
        invariant(exec);
        invariant(explain);

        scoped_ptr<PlanStageStats> stats(exec->getStats());
        if (NULL == stats.get()) {
            return Status(ErrorCodes::InternalError, "no stats available to explain plan");
        }

        // Special explain format for EOF.
        if (STAGE_EOF == stats->stageType) {
            *explain = new TypeExplain();

            // Fill in mandatory fields.
            (*explain)->setN(0);
            (*explain)->setNScannedObjects(0);
            (*explain)->setNScanned(0);

            // Fill in all the main fields that don't have a default in the explain data structure.
            (*explain)->setCursor("BasicCursor");
            (*explain)->setScanAndOrder(false);
            (*explain)->setIsMultiKey(false);
            (*explain)->setIndexOnly(false);
            (*explain)->setNYields(0);
            (*explain)->setNChunkSkips(0);

            TypeExplain* allPlans = new TypeExplain;
            allPlans->setCursor("BasicCursor");
            (*explain)->addToAllPlans(allPlans); // ownership xfer

            (*explain)->setNScannedObjectsAllPlans(0);
            (*explain)->setNScannedAllPlans(0);

            return Status::OK();
        }

        // Special explain format for idhack.
        vector<PlanStageStats*> statNodes;
        flattenStatsTree(stats.get(), &statNodes);
        PlanStageStats* idhack = NULL;
        for (size_t i = 0; i < statNodes.size(); i++) {
            if (STAGE_IDHACK == statNodes[i]->stageType) {
                idhack = statNodes[i];
                break;
            }
        }

        if (NULL != idhack) {
            // Explain format does not match 2.4 and is intended
            // to indicate clearly that the ID hack has been applied.
            *explain = new TypeExplain();

            IDHackStats* idhackStats = static_cast<IDHackStats*>(idhack->specific.get());

            (*explain)->setCursor("IDCursor");
            (*explain)->setIDHack(true);
            (*explain)->setN(stats->common.advanced);
            (*explain)->setNScanned(idhackStats->keysExamined);
            (*explain)->setNScannedObjects(idhackStats->docsExamined);

            return Status::OK();
        }

        Status status = explainPlan(*stats, explain, true /* full details */);
        if (!status.isOK()) {
            return status;
        }

        // Fill in explain fields that are accounted by on the runner level.
        TypeExplain* chosenPlan = NULL;
        explainPlan(*stats, &chosenPlan, false /* no full details */);
        if (chosenPlan) {
            (*explain)->addToAllPlans(chosenPlan);
        }
        (*explain)->setNScannedObjectsAllPlans((*explain)->getNScannedObjects());
        (*explain)->setNScannedAllPlans((*explain)->getNScanned());

        return Status::OK();
    }
Ejemplo n.º 6
0
    Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) {
        //
        // Temporary explain for index intersection
        //

        if (isIntersectPlan(stats)) {
            return explainIntersectPlan(stats, explainOut, fullDetails);
        }

        //
        // Legacy explain implementation
        //

        // Descend the plan looking for structural properties:
        // + Are there any OR clauses?  If so, explain each branch.
        // + What type(s) are the leaf nodes and what are their properties?
        // + Did we need a sort?

        bool covered = true;
        bool sortPresent = false;
        size_t chunkSkips = 0;

        const PlanStageStats* orStage = NULL;
        const PlanStageStats* root = &stats;
        const PlanStageStats* leaf = root;

        while (leaf->children.size() > 0) {
            // We shouldn't be here if there are any ANDs
            if (leaf->children.size() > 1) {
                verify(isOrStage(leaf->stageType));
            }

            if (isOrStage(leaf->stageType)) {
                orStage = leaf;
                break;
            }

            if (leaf->stageType == STAGE_FETCH) {
                covered = false;
            }

            if (leaf->stageType == STAGE_SORT) {
                sortPresent = true;
            }

            if (STAGE_SHARDING_FILTER == leaf->stageType) {
                const ShardingFilterStats* sfs
                    = static_cast<const ShardingFilterStats*>(leaf->specific.get());
                chunkSkips = sfs->chunkSkips;
            }

            leaf = leaf->children[0];
        }

        auto_ptr<TypeExplain> res(new TypeExplain);

        // Accounting for 'nscanned' and 'nscannedObjects' is specific to the kind of leaf:
        //
        // + on collection scan, both are the same; all the documents retrieved were
        //   fetched in practice. To get how many documents were retrieved, one simply
        //   looks at the number of 'advanced' in the stats.
        //
        // + on an index scan, we'd neeed to look into the index scan cursor to extract the
        //   number of keys that cursor retrieved, and into the stage's stats 'advanced' for
        //   nscannedObjects', which would be the number of keys that survived the IXSCAN
        //   filter. Those keys would have been FETCH-ed, if a fetch is present.

        if (orStage != NULL) {
            size_t nScanned = 0;
            size_t nScannedObjects = 0;
            const std::vector<PlanStageStats*>& children = orStage->children;
            for (std::vector<PlanStageStats*>::const_iterator it = children.begin();
                 it != children.end();
                 ++it) {
                TypeExplain* childExplain = NULL;
                explainPlan(**it, &childExplain, false /* no full details */);
                if (childExplain) {
                    // 'res' takes ownership of 'childExplain'.
                    res->addToClauses(childExplain);
                    nScanned += childExplain->getNScanned();

                    // We don't necessarilly fetch on a branch, but the old query framework
                    // did. We're still emulating the number it would have produced.
                    nScannedObjects += childExplain->getNScanned();
                }
            }
            res->setNScanned(nScanned);
            res->setNScannedObjects(nScannedObjects);
        }
        else if (leaf->stageType == STAGE_COLLSCAN) {
            CollectionScanStats* csStats = static_cast<CollectionScanStats*>(leaf->specific.get());
            res->setCursor("BasicCursor");
            res->setNScanned(csStats->docsTested);
            res->setNScannedObjects(csStats->docsTested);
            res->setIndexOnly(false);
            res->setIsMultiKey(false);
        }
        else if (leaf->stageType == STAGE_GEO_2D) {
            // Cursor name depends on type of GeoBrowse.
            // TODO: We could omit the shape from the cursor name.
            TwoDStats* nStats = static_cast<TwoDStats*>(leaf->specific.get());
            res->setCursor("GeoBrowse-" + nStats->type);
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // XXX: adding empty index bounds for backwards compatibility.
            res->setIndexBounds(BSONObj());
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2DSPHERE) {
            // TODO: This is kind of a lie for STAGE_GEO_NEAR_2DSPHERE.
            res->setCursor("S2NearCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // XXX: adding empty index bounds for backwards compatibility.
            res->setIndexBounds(BSONObj());
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2D) {
            TwoDNearStats* nStats = static_cast<TwoDNearStats*>(leaf->specific.get());
            res->setCursor("GeoSearchCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(nStats->nscanned);
            res->setNScannedObjects(nStats->objectsLoaded);
            // XXX: adding empty index bounds for backwards compatibility.
            res->setIndexBounds(BSONObj());
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_TEXT) {
            TextStats* tStats = static_cast<TextStats*>(leaf->specific.get());
            res->setCursor("TextCursor");
            res->setNScanned(tStats->keysExamined);
            res->setNScannedObjects(tStats->fetches);
        }
        else if (leaf->stageType == STAGE_IXSCAN) {
            IndexScanStats* indexStats = static_cast<IndexScanStats*>(leaf->specific.get());
            verify(indexStats);
            string direction = indexStats->direction > 0 ? "" : " reverse";
            res->setCursor(indexStats->indexType + " " + indexStats->indexName + direction);
            res->setNScanned(indexStats->keysExamined);

            // If we're covered, that is, no FETCH is present, then, by definition,
            // nScannedObject would be zero because no full document would have been fetched
            // from disk.
            res->setNScannedObjects(covered ? 0 : leaf->common.advanced);

            res->setIndexBounds(indexStats->indexBounds);
            res->setIsMultiKey(indexStats->isMultiKey);
            res->setIndexOnly(covered);
        }
        else if (leaf->stageType == STAGE_DISTINCT) {
            DistinctScanStats* dss = static_cast<DistinctScanStats*>(leaf->specific.get());
            verify(dss);
            res->setCursor("DistinctCursor");
            res->setN(dss->keysExamined);
            res->setNScanned(dss->keysExamined);
            // Distinct hack stage is fully covered.
            res->setNScannedObjects(0);
        }
        else {
            return Status(ErrorCodes::InternalError, "cannot interpret execution plan");
        }

        // How many documents did the query return?
        res->setN(root->common.advanced);
        res->setScanAndOrder(sortPresent);
        res->setNChunkSkips(chunkSkips);

        // Statistics for the plan (appear only in a detailed mode)
        // TODO: if we can get this from the runner, we can kill "detailed mode"
        if (fullDetails) {
            res->setNYields(root->common.yields);
            BSONObjBuilder bob;
            statsToBSON(*root, &bob);
            res->stats = bob.obj();
        }

        *explainOut = res.release();
        return Status::OK();
    }
Ejemplo n.º 7
0
    Status explainPlan(const PlanStageStats& stats, TypeExplain** explain, bool fullDetails) {
        auto_ptr<TypeExplain> res(new TypeExplain);

        // Descend the plan looking for structural properties:
        // + is there any 'or's (TODO ands)? if so, prepare to explain each branch recursively
        // + is is a collection scan or a an index scan?
        // + if the latter, was it covered?
        // + was a sort necessary?
        //
        // TODO: For now, we assume that at most one index is used in a plan
        bool covered = true;
        bool sortPresent = false;
        const PlanStageStats* logicalStage = NULL;
        const PlanStageStats* root = &stats;
        const PlanStageStats* leaf = root;
        while (leaf->children.size() > 0) {

            // We're failing a plan with multiple children other than OR.
            // TODO: explain richer plans.
            if (leaf->children.size() > 1 && !isLogicalStage(leaf->stageType)) {
                res->setCursor("Complex Plan");
                res->setNScanned(0);
                res->setNScannedObjects(0);
                *explain = res.release();
                return Status::OK();
            }

            if (isLogicalStage(leaf->stageType)) {
                logicalStage = leaf;
                break;
            }
            if (leaf->stageType == STAGE_FETCH) {
                covered = false;
            }
            if (leaf->stageType == STAGE_SORT) {
                sortPresent = true;
            }
            leaf = leaf->children[0];
        }

        // How many documents did the query return?
        res->setN(root->common.advanced);

        // Accounting for 'nscanned' and 'nscannedObjects' is specific to the kind of leaf:
        //
        // + on collection scan, both are the same; all the documents retrieved were
        //   fetched in practice. To get how many documents were retrieved, one simply
        //   looks at the number of 'advanced' in the stats.
        //
        // + on an index scan, we'd neeed to look into the index scan cursor to extract the
        //   number of keys that cursor retrieved, and into the stage's stats 'advanced' for
        //   nscannedObjects', which would be the number of keys that survived the IXSCAN
        //   filter. Those keys would have been FETCH-ed, if a fetch is present.

        if (logicalStage != NULL) {
            uint64_t nScanned = 0;
            uint64_t nScannedObjects = 0;
            bool isMultiKey = false;
            bool isIndexOnly = covered;
            const std::vector<PlanStageStats*>& children = logicalStage->children;
            for (std::vector<PlanStageStats*>::const_iterator it = children.begin();
                 it != children.end();
                 ++it) {
                TypeExplain* childExplain = NULL;
                explainPlan(**it, &childExplain, false /* no full details */);
                if (childExplain) {
                    res->addToClauses(childExplain);
                    nScanned += childExplain->getNScanned();

                    // We don't necessarilly fetch on a branch, but the old query framework
                    // did. We're still emulating the number it would have produced.
                    nScannedObjects += childExplain->getNScanned();

                    isMultiKey |= childExplain->getIsMultiKey();
                    isIndexOnly &= childExplain->getIndexOnly();
                }
            }
            res->setNScanned(nScanned);
            res->setNScannedObjects(nScannedObjects);
        }
        else if (leaf->stageType == STAGE_COLLSCAN) {
            CollectionScanStats* csStats = static_cast<CollectionScanStats*>(leaf->specific.get());
            res->setCursor("BasicCursor");
            res->setNScanned(csStats->docsTested);
            res->setNScannedObjects(csStats->docsTested);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2DSPHERE) {
            // TODO: This is kind of a lie for STAGE_GEO_NEAR_2DSPHERE.
            res->setCursor("S2NearCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2D) {
            // TODO: This is kind of a lie.
            res->setCursor("GeoSearchCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_IXSCAN) {
            IndexScanStats* indexStats = static_cast<IndexScanStats*>(leaf->specific.get());
            dassert(indexStats);
            string direction = indexStats > 0 ? "" : " reverse";
            res->setCursor(indexStats->indexType + " " + indexStats->indexName + direction);
            res->setNScanned(indexStats->keysExamined);

            // If we're covered, that is, no FETCH is present, then, by definition,
            // nScannedObject would be zero because no full document would have been fetched
            // from disk.
            res->setNScannedObjects(covered ? 0 : leaf->common.advanced);

            res->setIndexBounds(indexStats->indexBounds);
            res->setIsMultiKey(indexStats->isMultiKey);
            res->setIndexOnly(covered);
        }
        else {
            return Status(ErrorCodes::InternalError, "cannot interpret execution plan");
        }

        res->setScanAndOrder(sortPresent);

        // Statistics for the plan (appear only in a detailed mode)
        // TODO: if we can get this from the runner, we can kill "detailed mode"
        if (fullDetails) {
            res->setNYields(root->common.yields);
        }

        *explain = res.release();
        return Status::OK();
    }