// static (this one is used for Cached and MultiPlanStage)
 bool StageBuilder::build(OperationContext* txn,
                          Collection* collection,
                          const QuerySolution& solution,
                          WorkingSet* wsIn,
                          PlanStage** rootOut) {
     if (NULL == wsIn || NULL == rootOut) { return false; }
     QuerySolutionNode* solutionNode = solution.root.get();
     if (NULL == solutionNode) { return false; }
     return NULL != (*rootOut = buildStages(txn, collection, solution, solutionNode, wsIn));
 }
    // static
    bool StageBuilder::build(const QuerySolution& solution, PlanStage** rootOut,
                             WorkingSet** wsOut, bool use_chronos) {
        QuerySolutionNode* root = solution.root.get();
        if (NULL == root) { return false; }

        auto_ptr<WorkingSet> ws(new WorkingSet());
        PlanStage* stageRoot = buildStages(solution, root, ws.get(), use_chronos);

        if (NULL != stageRoot) {
            *rootOut = stageRoot;
            *wsOut = ws.release();
            return true;
        }
        else {
            return false;
        }
    }
    PlanStage* buildStages(OperationContext* txn,
                           Collection* collection,
                           const QuerySolution& qsol,
                           const QuerySolutionNode* root,
                           WorkingSet* ws) {
        if (STAGE_COLLSCAN == root->getType()) {
            const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(root);
            CollectionScanParams params;
            params.collection = collection;
            params.tailable = csn->tailable;
            params.direction = (csn->direction == 1) ? CollectionScanParams::FORWARD
                                                     : CollectionScanParams::BACKWARD;
            params.maxScan = csn->maxScan;
            return new CollectionScan(txn, params, ws, csn->filter.get());
        }
        else if (STAGE_IXSCAN == root->getType()) {
            const IndexScanNode* ixn = static_cast<const IndexScanNode*>(root);

            if (NULL == collection) {
                warning() << "Can't ixscan null namespace";
                return NULL;
            }

            IndexScanParams params;

            params.descriptor =
                collection->getIndexCatalog()->findIndexByKeyPattern( txn, ixn->indexKeyPattern );
            if ( params.descriptor == NULL ) {
                warning() << "Can't find index " << ixn->indexKeyPattern.toString()
                          << "in namespace " << collection->ns() << endl;
                return NULL;
            }

            params.bounds = ixn->bounds;
            params.direction = ixn->direction;
            params.maxScan = ixn->maxScan;
            params.addKeyMetadata = ixn->addKeyMetadata;
            return new IndexScan(txn, params, ws, ixn->filter.get());
        }
        else if (STAGE_FETCH == root->getType()) {
            const FetchNode* fn = static_cast<const FetchNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new FetchStage(txn, ws, childStage, fn->filter.get(), collection);
        }
        else if (STAGE_SORT == root->getType()) {
            const SortNode* sn = static_cast<const SortNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            SortStageParams params;
            params.collection = collection;
            params.pattern = sn->pattern;
            params.query = sn->query;
            params.limit = sn->limit;
            return new SortStage(txn, params, ws, childStage);
        }
        else if (STAGE_PROJECTION == root->getType()) {
            const ProjectionNode* pn = static_cast<const ProjectionNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, pn->children[0], ws);
            if (NULL == childStage) { return NULL; }

            ProjectionStageParams params(WhereCallbackReal(txn, collection->ns().db()));
            params.projObj = pn->projection;

            // Stuff the right data into the params depending on what proj impl we use.
            if (ProjectionNode::DEFAULT == pn->projType) {
                params.fullExpression = pn->fullExpression;
                params.projImpl = ProjectionStageParams::NO_FAST_PATH;
            }
            else if (ProjectionNode::COVERED_ONE_INDEX == pn->projType) {
                params.projImpl = ProjectionStageParams::COVERED_ONE_INDEX;
                params.coveredKeyObj = pn->coveredKeyObj;
                invariant(!pn->coveredKeyObj.isEmpty());
            }
            else {
                invariant(ProjectionNode::SIMPLE_DOC == pn->projType);
                params.projImpl = ProjectionStageParams::SIMPLE_DOC;
            }

            return new ProjectionStage(params, ws, childStage);
        }
        else if (STAGE_LIMIT == root->getType()) {
            const LimitNode* ln = static_cast<const LimitNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, ln->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new LimitStage(ln->limit, ws, childStage);
        }
        else if (STAGE_SKIP == root->getType()) {
            const SkipNode* sn = static_cast<const SkipNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new SkipStage(sn->skip, ws, childStage);
        }
        else if (STAGE_AND_HASH == root->getType()) {
            const AndHashNode* ahn = static_cast<const AndHashNode*>(root);
            auto_ptr<AndHashStage> ret(new AndHashStage(txn, ws, ahn->filter.get(), collection));
            for (size_t i = 0; i < ahn->children.size(); ++i) {
                PlanStage* childStage = buildStages(txn, collection, qsol, ahn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_OR == root->getType()) {
            const OrNode * orn = static_cast<const OrNode*>(root);
            auto_ptr<OrStage> ret(new OrStage(ws, orn->dedup, orn->filter.get()));
            for (size_t i = 0; i < orn->children.size(); ++i) {
                PlanStage* childStage = buildStages(txn, collection, qsol, orn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_AND_SORTED == root->getType()) {
            const AndSortedNode* asn = static_cast<const AndSortedNode*>(root);
            auto_ptr<AndSortedStage> ret(new AndSortedStage(txn, ws, asn->filter.get(), collection));
            for (size_t i = 0; i < asn->children.size(); ++i) {
                PlanStage* childStage = buildStages(txn, collection, qsol, asn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_SORT_MERGE == root->getType()) {
            const MergeSortNode* msn = static_cast<const MergeSortNode*>(root);
            MergeSortStageParams params;
            params.dedup = msn->dedup;
            params.pattern = msn->sort;
            auto_ptr<MergeSortStage> ret(new MergeSortStage(txn, params, ws, collection));
            for (size_t i = 0; i < msn->children.size(); ++i) {
                PlanStage* childStage = buildStages(txn, collection, qsol, msn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_GEO_NEAR_2D == root->getType()) {
            const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(root);

            GeoNearParams params;
            params.nearQuery = node->nq;
            params.baseBounds = node->baseBounds;
            params.filter = node->filter.get();
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;

            IndexDescriptor* twoDIndex = collection->getIndexCatalog()->findIndexByKeyPattern(txn,
                                                                                              node->indexKeyPattern);

            if (twoDIndex == NULL) {
                warning() << "Can't find 2D index " << node->indexKeyPattern.toString()
                          << "in namespace " << collection->ns() << endl;
                return NULL;
            }

            GeoNear2DStage* nearStage = new GeoNear2DStage(params, txn, ws, collection, twoDIndex);

            return nearStage;
        }
        else if (STAGE_GEO_NEAR_2DSPHERE == root->getType()) {
            const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(root);

            GeoNearParams params;
            params.nearQuery = node->nq;
            params.baseBounds = node->baseBounds;
            params.filter = node->filter.get();
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;

            IndexDescriptor* s2Index = collection->getIndexCatalog()->findIndexByKeyPattern(txn,
                                                                                            node->indexKeyPattern);

            if (s2Index == NULL) {
                warning() << "Can't find 2DSphere index " << node->indexKeyPattern.toString()
                          << "in namespace " << collection->ns() << endl;
                return NULL;
            }

            return new GeoNear2DSphereStage(params, txn, ws, collection, s2Index);
        }
        else if (STAGE_TEXT == root->getType()) {
            const TextNode* node = static_cast<const TextNode*>(root);

            if (NULL == collection) {
                warning() << "Null collection for text";
                return NULL;
            }
            vector<IndexDescriptor*> idxMatches;
            collection->getIndexCatalog()->findIndexByType(txn, "text", idxMatches);
            if (1 != idxMatches.size()) {
                warning() << "No text index, or more than one text index";
                return NULL;
            }
            IndexDescriptor* index = idxMatches[0];
            const FTSAccessMethod* fam =
                static_cast<FTSAccessMethod*>( collection->getIndexCatalog()->getIndex( index ) );
            TextStageParams params(fam->getSpec());

            //params.collection = collection;
            params.index = index;
            params.spec = fam->getSpec();
            params.indexPrefix = node->indexPrefix;

            const std::string& language = ("" == node->language
                                           ? fam->getSpec().defaultLanguage().str()
                                           : node->language);

            Status parseStatus = params.query.parse(node->query, language,
                                                    fam->getSpec().getTextIndexVersion());
            if (!parseStatus.isOK()) {
                warning() << "Can't parse text search query";
                return NULL;
            }

            return new TextStage(txn, params, ws, node->filter.get());
        }
        else if (STAGE_SHARDING_FILTER == root->getType()) {
            const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new ShardFilterStage(shardingState.getCollectionMetadata(collection->ns()),
                                        ws, childStage);
        }
        else if (STAGE_KEEP_MUTATIONS == root->getType()) {
            const KeepMutationsNode* km = static_cast<const KeepMutationsNode*>(root);
            PlanStage* childStage = buildStages(txn, collection, qsol, km->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new KeepMutationsStage(km->filter.get(), ws, childStage);
        }
        else if (STAGE_DISTINCT == root->getType()) {
            const DistinctNode* dn = static_cast<const DistinctNode*>(root);

            if (NULL == collection) {
                warning() << "Can't distinct-scan null namespace";
                return NULL;
            }

            DistinctParams params;

            params.descriptor =
                collection->getIndexCatalog()->findIndexByKeyPattern(txn, dn->indexKeyPattern);
            params.direction = dn->direction;
            params.bounds = dn->bounds;
            params.fieldNo = dn->fieldNo;
            return new DistinctScan(txn, params, ws);
        }
        else if (STAGE_COUNT_SCAN == root->getType()) {
            const CountNode* cn = static_cast<const CountNode*>(root);

            if (NULL == collection) {
                warning() << "Can't fast-count null namespace (collection null)";
                return NULL;
            }

            CountScanParams params;

            params.descriptor =
                collection->getIndexCatalog()->findIndexByKeyPattern(txn, cn->indexKeyPattern);
            params.startKey = cn->startKey;
            params.startKeyInclusive = cn->startKeyInclusive;
            params.endKey = cn->endKey;
            params.endKeyInclusive = cn->endKeyInclusive;

            return new CountScan(txn, params, ws);
        }
        else {
            mongoutils::str::stream ss;
            root->appendToString(&ss, 0);
            string nodeStr(ss);
            warning() << "Can't build exec tree for node " << nodeStr << endl;
            return NULL;
        }
    }
    PlanStage* buildStages(const QuerySolution& qsol, const QuerySolutionNode* root, WorkingSet* ws, bool use_chronos = false) {
        if (STAGE_COLLSCAN == root->getType()) {
            const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(root);
            CollectionScanParams params;
            params.ns = csn->name;
            params.tailable = csn->tailable;
            params.direction = (csn->direction == 1) ? CollectionScanParams::FORWARD
                                                     : CollectionScanParams::BACKWARD;
            params.maxScan = csn->maxScan;
            return new CollectionScan(params, ws, csn->filter.get(), use_chronos);
        }
        else if (STAGE_IXSCAN == root->getType()) {
            const IndexScanNode* ixn = static_cast<const IndexScanNode*>(root);
            //
            // XXX XXX
            // Given that this grabs data from the catalog, we must do this inside of a lock.
            // We should change this to take a (ns, index key pattern) pair so that the params
            // don't involve any on-disk data, just descriptions thereof.
            // XXX XXX
            //
            Database* db = cc().database();
            Collection* collection = db ? db->getCollection(qsol.ns) : NULL;
            if (NULL == collection) {
                warning() << "Can't ixscan null ns " << qsol.ns << endl;
                return NULL;
            }
            NamespaceDetails* nsd = collection->details();
            int idxNo = nsd->findIndexByKeyPattern(ixn->indexKeyPattern);
            if (-1 == idxNo) {
                warning() << "Can't find idx " << ixn->indexKeyPattern.toString()
                          << "in ns " << qsol.ns << endl;
                return NULL;
            }
            IndexScanParams params;
            params.descriptor = collection->getIndexCatalog()->getDescriptor( idxNo );
            params.bounds = ixn->bounds;
            params.direction = ixn->direction;
            params.limit = ixn->limit;
            params.maxScan = ixn->maxScan;
            params.addKeyMetadata = ixn->addKeyMetadata;
            params.ns = qsol.ns;
            return new IndexScan(params, ws, ixn->filter.get(), use_chronos);
        }
        else if (STAGE_FETCH == root->getType()) {
            const FetchNode* fn = static_cast<const FetchNode*>(root);
            PlanStage* childStage = buildStages(qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new FetchStage(ws, childStage, fn->filter.get());
        }
        else if (STAGE_SORT == root->getType()) {
            const SortNode* sn = static_cast<const SortNode*>(root);
            PlanStage* childStage = buildStages(qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            SortStageParams params;
            params.pattern = sn->pattern;
            params.query = sn->query;
            params.limit = sn->limit;
            return new SortStage(params, ws, childStage);
        }
        else if (STAGE_PROJECTION == root->getType()) {
            const ProjectionNode* pn = static_cast<const ProjectionNode*>(root);
            PlanStage* childStage = buildStages(qsol, pn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new ProjectionStage(pn->projection, pn->fullExpression, ws, childStage);
        }
        else if (STAGE_LIMIT == root->getType()) {
            const LimitNode* ln = static_cast<const LimitNode*>(root);
            PlanStage* childStage = buildStages(qsol, ln->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new LimitStage(ln->limit, ws, childStage);
        }
        else if (STAGE_SKIP == root->getType()) {
            const SkipNode* sn = static_cast<const SkipNode*>(root);
            PlanStage* childStage = buildStages(qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new SkipStage(sn->skip, ws, childStage);
        }
        else if (STAGE_AND_HASH == root->getType()) {
            const AndHashNode* ahn = static_cast<const AndHashNode*>(root);
            auto_ptr<AndHashStage> ret(new AndHashStage(ws, ahn->filter.get()));
            for (size_t i = 0; i < ahn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, ahn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_OR == root->getType()) {
            const OrNode * orn = static_cast<const OrNode*>(root);
            auto_ptr<OrStage> ret(new OrStage(ws, orn->dedup, orn->filter.get()));
            for (size_t i = 0; i < orn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, orn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_AND_SORTED == root->getType()) {
            const AndSortedNode* asn = static_cast<const AndSortedNode*>(root);
            auto_ptr<AndSortedStage> ret(new AndSortedStage(ws, asn->filter.get()));
            for (size_t i = 0; i < asn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, asn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_SORT_MERGE == root->getType()) {
            const MergeSortNode* msn = static_cast<const MergeSortNode*>(root);
            MergeSortStageParams params;
            params.dedup = msn->dedup;
            params.pattern = msn->sort;
            auto_ptr<MergeSortStage> ret(new MergeSortStage(params, ws));
            for (size_t i = 0; i < msn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, msn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_GEO_2D == root->getType()) {
            const Geo2DNode* node = static_cast<const Geo2DNode*>(root);
            TwoDParams params;
            params.gq = node->gq;
            params.filter = node->filter.get();
            params.indexKeyPattern = node->indexKeyPattern;
            params.ns = qsol.ns;
            return new TwoD(params, ws);
        }
        else if (STAGE_GEO_NEAR_2D == root->getType()) {
            const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(root);
            TwoDNearParams params;
            params.nearQuery = node->nq;
            params.ns = qsol.ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.filter = node->filter.get();
            params.numWanted = node->numWanted;
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;
            return new TwoDNear(params, ws);
        }
        else if (STAGE_GEO_NEAR_2DSPHERE == root->getType()) {
            const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(root);
            S2NearParams params;
            params.ns = qsol.ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.nearQuery = node->nq;
            params.baseBounds = node->baseBounds;
            params.filter = node->filter.get();
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;
            return new S2NearStage(params, ws);
        }
        else if (STAGE_TEXT == root->getType()) {
            const TextNode* node = static_cast<const TextNode*>(root);

            Database* db = cc().database();
            Collection* collection = db ? db->getCollection(qsol.ns) : NULL;
            if (NULL == collection) {
                warning() << "null collection for text?";
                return NULL;
            }
            vector<int> idxMatches;
            collection->details()->findIndexByType("text", idxMatches);
            if (1 != idxMatches.size()) {
                warning() << "more than one text idx?";
                return NULL;
            }
            IndexDescriptor* index = collection->getIndexCatalog()->getDescriptor(idxMatches[0]);
            const FTSAccessMethod* fam =
                static_cast<FTSAccessMethod*>( collection->getIndexCatalog()->getIndex( index ) );
            TextStageParams params(fam->getSpec());

            params.ns = qsol.ns;
            params.index = index;
            params.spec = fam->getSpec();
            // XXX change getIndexPrefix to not look at BSONObj
            Status s = fam->getSpec().getIndexPrefix(qsol.filterData, &params.indexPrefix);
            if (!s.isOK()) {
                warning() << "can't get text index prefix??";
                return NULL;
            }

            string language = ("" == node->_language
                               ? fam->getSpec().defaultLanguage().str()
                               : node->_language);

            FTSQuery ftsq;
            Status parseStatus = ftsq.parse(node->_query, language);
            if (!parseStatus.isOK()) {
                warning() << "cant parse fts query";
                return NULL;
            }
            params.query = ftsq;

            return new TextStage(params, ws, node->filter.get());
        }
        else if (STAGE_SHARDING_FILTER == root->getType()) {
            const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(root);
            PlanStage* childStage = buildStages(qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new ShardFilterStage(shardingState.getCollectionMetadata(qsol.ns), ws, childStage);
        }
        else {
            stringstream ss;
            root->appendToString(&ss, 0);
            warning() << "Could not build exec tree for node " << ss.str() << endl;
            return NULL;
        }
    }
Exemple #5
0
    PlanStage* buildStages(const string& ns, const QuerySolutionNode* root, WorkingSet* ws) {
        if (STAGE_COLLSCAN == root->getType()) {
            const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(root);
            CollectionScanParams params;
            params.ns = csn->name;
            params.tailable = csn->tailable;
            params.direction = (csn->direction == 1) ? CollectionScanParams::FORWARD
                                                     : CollectionScanParams::BACKWARD;
            return new CollectionScan(params, ws, csn->filter.get());
        }
        else if (STAGE_IXSCAN == root->getType()) {
            const IndexScanNode* ixn = static_cast<const IndexScanNode*>(root);
            //
            // XXX XXX
            // Given that this grabs data from the catalog, we must do this inside of a lock.
            // We should change this to take a (ns, index key pattern) pair so that the params
            // don't involve any on-disk data, just descriptions thereof.
            // XXX XXX
            //
            IndexScanParams params;
            NamespaceDetails* nsd = nsdetails(ns.c_str());
            if (NULL == nsd) {
                warning() << "Can't ixscan null ns " << ns << endl;
                return NULL;
            }
            int idxNo = nsd->findIndexByKeyPattern(ixn->indexKeyPattern);
            if (-1 == idxNo) {
                warning() << "Can't find idx " << ixn->indexKeyPattern.toString()
                          << "in ns " << ns << endl;
                return NULL;
            }
            params.descriptor = CatalogHack::getDescriptor(nsd, idxNo);
            params.bounds = ixn->bounds;
            params.direction = ixn->direction;
            params.limit = ixn->limit;
            return new IndexScan(params, ws, ixn->filter.get());
        }
        else if (STAGE_FETCH == root->getType()) {
            const FetchNode* fn = static_cast<const FetchNode*>(root);
            PlanStage* childStage = buildStages(ns, fn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new FetchStage(ws, childStage, fn->filter.get());
        }
        else if (STAGE_SORT == root->getType()) {
            const SortNode* sn = static_cast<const SortNode*>(root);
            PlanStage* childStage = buildStages(ns, sn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            SortStageParams params;
            params.pattern = sn->pattern;
            return new SortStage(params, ws, childStage);
        }
        else if (STAGE_PROJECTION == root->getType()) {
            const ProjectionNode* pn = static_cast<const ProjectionNode*>(root);
            PlanStage* childStage = buildStages(ns, pn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new ProjectionStage(pn->projection, ws, childStage, NULL);
        }
        else if (STAGE_LIMIT == root->getType()) {
            const LimitNode* ln = static_cast<const LimitNode*>(root);
            PlanStage* childStage = buildStages(ns, ln->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new LimitStage(ln->limit, ws, childStage);
        }
        else if (STAGE_SKIP == root->getType()) {
            const SkipNode* sn = static_cast<const SkipNode*>(root);
            PlanStage* childStage = buildStages(ns, sn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new SkipStage(sn->skip, ws, childStage);
        }
        else if (STAGE_AND_HASH == root->getType()) {
            const AndHashNode* ahn = static_cast<const AndHashNode*>(root);
            auto_ptr<AndHashStage> ret(new AndHashStage(ws, ahn->filter.get()));
            for (size_t i = 0; i < ahn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, ahn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_OR == root->getType()) {
            const OrNode * orn = static_cast<const OrNode*>(root);
            auto_ptr<OrStage> ret(new OrStage(ws, orn->dedup, orn->filter.get()));
            for (size_t i = 0; i < orn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, orn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_AND_SORTED == root->getType()) {
            const AndSortedNode* asn = static_cast<const AndSortedNode*>(root);
            auto_ptr<AndSortedStage> ret(new AndSortedStage(ws, asn->filter.get()));
            for (size_t i = 0; i < asn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, asn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_SORT_MERGE == root->getType()) {
            const MergeSortNode* msn = static_cast<const MergeSortNode*>(root);
            MergeSortStageParams params;
            params.dedup = msn->dedup;
            params.pattern = msn->sort;
            auto_ptr<MergeSortStage> ret(new MergeSortStage(params, ws));
            for (size_t i = 0; i < msn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, msn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_GEO_2D == root->getType()) {
            const Geo2DNode* node = static_cast<const Geo2DNode*>(root);
            TwoDParams params;
            params.gq = node->gq;
            params.filter = node->filter.get();
            params.indexKeyPattern = node->indexKeyPattern;
            params.ns = ns;
            return new TwoD(params, ws);
        }
        else if (STAGE_GEO_NEAR_2D == root->getType()) {
            const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(root);
            TwoDNearParams params;
            params.nearQuery = node->nq;
            params.ns = ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.filter = node->filter.get();
            params.numWanted = node->numWanted;
            // XXX XXX where do we grab this from??  the near query...modify geo parser... :(
            params.uniqueDocs = false;
            // XXX XXX where do we grab this from??  the near query...modify geo parser... :(
            return new TwoDNear(params, ws);
        }
        else if (STAGE_GEO_NEAR_2DSPHERE == root->getType()) {
            const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(root);
            return new S2NearStage(ns, node->indexKeyPattern, node->nq, node->baseBounds,
                                   node->filter.get(), ws);
        }
        else if (STAGE_TEXT == root->getType()) {
            const TextNode* node = static_cast<const TextNode*>(root);

            NamespaceDetails* nsd = nsdetails(ns.c_str());
            if (NULL == nsd) { return NULL; }
            vector<int> idxMatches;
            nsd->findIndexByType("text", idxMatches);
            if (1 != idxMatches.size()) { return NULL; }
            IndexDescriptor* index = CatalogHack::getDescriptor(nsd, idxMatches[0]);
            auto_ptr<FTSAccessMethod> fam(new FTSAccessMethod(index));
            TextStageParams params(fam->getSpec());

            params.ns = ns;
            params.index = index;
            params.spec = fam->getSpec();
            params.limit = node->_numWanted;
            Status s = fam->getSpec().getIndexPrefix(BSONObj(), &params.indexPrefix);
            if (!s.isOK()) { return NULL; }

            string language = ("" == node->_language
                               ? fam->getSpec().defaultLanguage()
                               : node->_language);

            FTSQuery ftsq;
            Status parseStatus = ftsq.parse(node->_query, language);
            if (!parseStatus.isOK()) { return NULL; }
            params.query = ftsq;

            return new TextStage(params, ws, node->_filter.get());
        }
        else {
            stringstream ss;
            root->appendToString(&ss, 0);
            warning() << "Could not build exec tree for node " << ss.str() << endl;
            return NULL;
        }
    }