/** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #pragma once #include "../pch.h" #include "jsobj.h" #include "diskloc.h" #include "matcher.h" namespace mongo { class Record; class CoveredIndexMatcher; /* Query cursors, base class. This is for our internal cursors. "ClientCursor" is a separate concept and is for the user's cursor. WARNING concurrency: the vfunctions below are called back from within a ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. */ class Cursor : boost::noncopyable { public: virtual ~Cursor() {} virtual bool ok() = 0; bool eof() { return !ok(); } virtual Record* _current() = 0; virtual BSONObj current() = 0; virtual DiskLoc currLoc() = 0; virtual bool advance() = 0; /*true=ok*/ virtual BSONObj currKey() const { return BSONObj(); } // DiskLoc the cursor requires for continued operation. Before this // DiskLoc is deleted, the cursor must be incremented or destroyed. virtual DiskLoc refLoc() = 0; /* Implement these if you want the cursor to be "tailable" */ /* Request that the cursor starts tailing after advancing past last record. */ /* The implementation may or may not honor this request. */ virtual void setTailable() {} /* indicates if tailing is enabled. */ virtual bool tailable() { return false; } virtual void aboutToDeleteBucket(const DiskLoc& b) { } /* optional to implement. if implemented, means 'this' is a prototype */ virtual Cursor* clone() { return 0; } virtual BSONObj indexKeyPattern() { return BSONObj(); } /* called after every query block is iterated -- i.e. between getMore() blocks so you can note where we are, if necessary. */ virtual void noteLocation() { } /* called before query getmore block is iterated */ virtual void checkLocation() { } virtual bool supportGetMore() = 0; virtual bool supportYields() = 0; virtual string toString() { return "abstract?"; } /* used for multikey index traversal to avoid sending back dups. see Matcher::matches(). if a multikey index traversal: if loc has already been sent, returns true. otherwise, marks loc as sent. @param deep - match was against an array, so we know it is multikey. this is legacy and kept for backwards datafile compatibility. 'deep' can be eliminated next time we force a data file conversion. 7Jul09 */ virtual bool getsetdup(DiskLoc loc) = 0; virtual BSONObj prettyIndexBounds() const { return BSONArray(); } virtual bool capped() const { return false; } // The implementation may return different matchers depending on the // position of the cursor. If matcher() is nonzero at the start, // matcher() should be checked each time advance() is called. virtual CoveredIndexMatcher *matcher() const { return 0; } // A convenience function for setting the value of matcher() manually // so it may accessed later. Implementations which must generate // their own matcher() should assert here. virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { massert( 13285, "manual matcher config not allowed", false ); } }; // strategy object implementing direction of traversal. class AdvanceStrategy { public: virtual ~AdvanceStrategy() { } virtual DiskLoc next( const DiskLoc &prev ) const = 0; }; const AdvanceStrategy *forward(); const AdvanceStrategy *reverse(); /* table-scan style cursor */ class BasicCursor : public Cursor { protected: DiskLoc curr, last; const AdvanceStrategy *s; private: bool tailable_; shared_ptr< CoveredIndexMatcher > _matcher; void init() { tailable_ = false; } public: bool ok() { return !curr.isNull(); } Record* _current() { assert( ok() ); return curr.rec(); } BSONObj current() { Record *r = _current(); BSONObj j(r); return j; } virtual DiskLoc currLoc() { return curr; } virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; } bool advance(); BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : curr(dl), s( _s ) { init(); } BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ) { init(); } virtual string toString() { return "BasicCursor"; } virtual void setTailable() { if ( !curr.isNull() || !last.isNull() ) tailable_ = true; } virtual bool tailable() { return tailable_; } virtual bool getsetdup(DiskLoc loc) { return false; } virtual bool supportGetMore() { return true; } virtual bool supportYields() { return true; } virtual CoveredIndexMatcher *matcher() const { return _matcher.get(); } virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; } }; /* used for order { $natural: -1 } */ class ReverseCursor : public BasicCursor { public: ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } ReverseCursor() : BasicCursor( reverse() ) { } virtual string toString() { return "ReverseCursor"; } }; class NamespaceDetails; class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy { public: ForwardCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &startLoc = DiskLoc() ); virtual string toString() { return "ForwardCappedCursor"; } virtual DiskLoc next( const DiskLoc &prev ) const; virtual bool capped() const { return true; } private: NamespaceDetails *nsd; }; class ReverseCappedCursor : public BasicCursor, public AdvanceStrategy { public: ReverseCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &startLoc = DiskLoc() ); virtual string toString() { return "ReverseCappedCursor"; } virtual DiskLoc next( const DiskLoc &prev ) const; virtual bool capped() const { return true; } private: NamespaceDetails *nsd; }; } // namespace mongo