bool MojDbIndex::canAnswer(const MojDbQuery& query) const { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojAssert(!m_propNames.empty()); // if this index is not ready yet, return false if (!m_ready) return false; // The goal here is to figure out whether the results for the query are contiguous in // the index. That will be the case if all the props referenced are contiguous in our // prop vector, any prop referenced by an inequality op comes at the end of the // range of referenced props, any unreferenced props in our vector are at the end, // and the order prop is either the last referenced prop or, if all ops are equality ops, // the prop following the last referenced prop. const MojDbQuery::WhereMap& map = query.where(); MojSize numQueryProps = map.size(); // if there are more props in the query than in our index, no can do if (numQueryProps > m_propNames.size()) return false; // skip the first prop if we have an incDel index and the query does not reference the del prop StringVec::ConstIterator propName = m_propNames.begin(); PropVec::ConstIterator prop = m_props.begin(); if (m_includeDeleted && !map.contains(MojDb::DelKey)) { ++propName; ++prop; } // if there are no props in query, but the order matches our first prop, we're good const MojString& orderProp = query.order(); if (numQueryProps == 0) { if (orderProp.empty()) { return isIdIndex(); } else { return *propName == orderProp; } } // check all remaining props for (; propName != m_propNames.end(); ++propName, ++prop) { --numQueryProps; // ensure that the current prop is referenced in the query (no gaps) MojDbQuery::WhereMap::ConstIterator mapIter = map.find(*propName); if (mapIter == map.end()) return false; // ensure that if it is an inequality op, it is at the end if (numQueryProps > 0 && mapIter->lowerOp() != MojDbQuery::OpEq) return false; // ensure that collation matches if (mapIter->collation() != (*prop)->collation()) return false; // hooray, we made it through all the props in the query without failing any tests. // there may still be unreferenced props at the end of the vector, but that's ok. if (numQueryProps == 0) { // make sure that we can satisfy the ordering. if there is no ordering, or we are // ordering on a referenced prop, we're golden if (!orderProp.empty() && !map.contains(orderProp)) { // ensure that the order prop is next in our vec StringVec::ConstIterator next = propName + 1; if (next == m_propNames.end() || *next != orderProp) return false; } // can do! return true; } } return false; }
MojErr MojDbQueryPlan::buildRanges(const MojDbIndex& index) { MojAssert(!index.props().empty()); MojErr err = MojErrNone; const MojDbQuery::WhereMap& where = m_query.where(); const StringVec& props = index.props(); const MojDbQuery::WhereClause* lastClause = NULL; MojDbKeyBuilder lowerBuilder; MojDbKeyBuilder upperBuilder; MojDbKeyBuilder::KeySet prefixKeys; err = pushVal(lowerBuilder, index.id(), NULL); MojErrCheck(err); err = pushVal(upperBuilder, index.id(), NULL); MojErrCheck(err); // push vals for all props in index prop order to create upper and lower bounds for (StringVec::ConstIterator i = props.begin(); i != props.end(); ++i) { // get clause for prop MojDbQuery::WhereMap::ConstIterator clause = where.find(*i); if (clause == where.end()) { MojAssert((MojSize)(i - props.begin()) == where.size()); break; } // create collator MojRefCountedPtr<MojDbTextCollator> collator; MojDbCollationStrength coll = clause->collation(); if (coll != MojDbCollationInvalid) { collator.reset(new MojDbTextCollator); MojAllocCheck(collator.get()); err = collator->init(m_locale, coll); MojErrCheck(err); } // push vals for clause MojDbQuery::CompOp lowerOp = clause->lowerOp(); if (lowerOp == MojDbQuery::OpSearch) { // tokenize search strings err = pushSearch(lowerBuilder, upperBuilder, clause->lowerVal(), collator.get()); MojErrCheck(err); } else { // copy off prefix before writing an inequality val if (lowerOp != MojDbQuery::OpEq && lowerOp != MojDbQuery::OpPrefix) { err = lowerBuilder.keys(prefixKeys); MojErrCheck(err); if (prefixKeys.empty()) { // if there is no prefix yet, put an empty key err = prefixKeys.put(MojDbKey()); MojErrCheck(err); } } // append lower-value to lower-key err = pushVal(lowerBuilder, clause->lowerVal(), collator.get()); MojErrCheck(err); // append appropriate value to upper-key if (clause->upperOp() == MojDbQuery::OpNone) { err = pushVal(upperBuilder, clause->lowerVal(), collator.get()); MojErrCheck(err); } else { err = pushVal(upperBuilder, clause->upperVal(), collator.get()); MojErrCheck(err); } } lastClause = &clause.value(); } // go from builders to ranges KeySet lowerKeys; err = lowerBuilder.keys(lowerKeys); MojErrCheck(err); KeySet upperKeys; err = upperBuilder.keys(upperKeys); MojErrCheck(err); if (prefixKeys.empty()) { prefixKeys = lowerKeys; } err = rangesFromKeySets(lowerKeys, upperKeys, prefixKeys, lastClause); MojErrCheck(err); return MojErrNone; }