//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2018 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Tobias Goedderz /// @author Michael Hackstein /// @author Heiko Kernbach /// @author Jan Christoph Uhde //////////////////////////////////////////////////////////////////////////////// #ifndef ARANGOD_AQL_INDEX_EXECUTOR_H #define ARANGOD_AQL_INDEX_EXECUTOR_H #include "Aql/ExecutionBlock.h" #include "Aql/ExecutionNode.h" #include "Aql/ExecutionState.h" #include "Aql/ExecutorInfos.h" #include "Aql/IndexNode.h" #include "Aql/OutputAqlItemRow.h" #include "Aql/Stats.h" #include "Aql/types.h" #include "DocumentProducingHelper.h" #include "Indexes/IndexIterator.h" #include "Utils/OperationCursor.h" #include namespace arangodb { namespace aql { class InputAqlItemRow; class ExecutorInfos; template class SingleRowFetcher; class IndexExecutorInfos : public ExecutorInfos { public: IndexExecutorInfos( RegisterId outputRegister, RegisterId nrInputRegisters, RegisterId nrOutputRegisters, std::unordered_set registersToClear, std::unordered_set registersToKeep, ExecutionEngine* engine, Collection const* collection, Variable const* outVariable, bool produceResult, std::vector const& projections, transaction::Methods* trxPtr, std::vector const& coveringIndexAttributePositions, bool useRawDocumentPointers, std::vector>&& nonConstExpression, std::vector&& expInVars, std::vector&& expInRegs, bool hasV8Expression, AstNode const* condition, std::vector indexes, Ast* ast, IndexIteratorOptions options); IndexExecutorInfos() = delete; IndexExecutorInfos(IndexExecutorInfos&&) = default; IndexExecutorInfos(IndexExecutorInfos const&) = delete; ~IndexExecutorInfos() = default; ExecutionEngine* getEngine() const { return _engine; } Collection const* getCollection() const { return _collection; } Variable const* getOutVariable() const { return _outVariable; } std::vector const& getProjections() const { return _projections; } transaction::Methods* getTrxPtr() const { return _trxPtr; } std::vector const& getCoveringIndexAttributePositions() const { return _coveringIndexAttributePositions; } bool getProduceResult() const { return _produceResult; } bool getUseRawDocumentPointers() const { return _useRawDocumentPointers; } std::vector const& getIndexes() { return _indexes; } AstNode const* getCondition() { return _condition; } bool getV8Expression() const { return _hasV8Expression; } RegisterId getOutputRegisterId() const { return _outputRegisterId; } std::vector> const& getNonConstExpressions() { return _nonConstExpression; } bool hasMultipleExpansions() const { return _hasMultipleExpansions; } /// @brief whether or not all indexes are accessed in reverse order IndexIteratorOptions getOptions() const { return _options; } bool isAscending() const { return _options.ascending; } Ast* getAst() const { return _ast; } std::vector const& getExpInVars() const { return _expInVars; } std::vector const& getExpInRegs() const { return _expInRegs; } // setter void setHasMultipleExpansions(bool flag) { _hasMultipleExpansions = flag; } bool hasNonConstParts() const { return !_nonConstExpression.empty(); } private: /// @brief _indexes holds all Indexes used in this block std::vector _indexes; /// @brief _inVars, a vector containing for each expression above /// a vector of Variable*, used to execute the expression std::vector> _inVars; /// @brief _condition: holds the complete condition this Block can serve for AstNode const* _condition; /// @brief _ast: holds the ast of the _plan Ast* _ast; /// @brief _inRegs, a vector containing for each expression above /// a vector of RegisterId, used to execute the expression std::vector> _inRegs; /// @brief true if one of the indexes uses more than one expanded attribute, /// e.g. the index is on values[*].name and values[*].type bool _hasMultipleExpansions; /// @brief the index sort order - this is the same order for all indexes IndexIteratorOptions _options; RegisterId _outputRegisterId; ExecutionEngine* _engine; Collection const* _collection; Variable const* _outVariable; std::vector const& _projections; transaction::Methods* _trxPtr; std::vector _expInVars; // input variables for expresseion std::vector _expInRegs; // input registers for expression std::vector const& _coveringIndexAttributePositions; bool _useRawDocumentPointers; std::vector> _nonConstExpression; bool _produceResult; /// @brief Counter how many documents have been returned/skipped /// during one call. Retained during WAITING situations. /// Needs to be 0 after we return a result. bool _hasV8Expression; }; /** * @brief Implementation of Index Node */ class IndexExecutor { private: struct CursorReader { public: CursorReader(IndexExecutorInfos const& infos, AstNode const* condition, transaction::Methods::IndexHandle const& index, DocumentProducingFunctionContext& context, bool checkUniqueness); bool readIndex(OutputAqlItemRow& output); size_t skipIndex(size_t toSkip); void reset(); bool hasMore() const; bool isCovering() const { return _type == Type::Covering; } CursorReader(const CursorReader&) = delete; CursorReader& operator=(const CursorReader&) = delete; CursorReader(CursorReader&& other) noexcept; private: enum Type { NoResult, Covering, Document }; IndexExecutorInfos const& _infos; AstNode const* _condition; transaction::Methods::IndexHandle const& _index; std::unique_ptr _cursor; Type const _type; union CallbackMethod { IndexIterator::LocalDocumentIdCallback noProduce; DocumentProducingFunction produce; CallbackMethod() : noProduce(nullptr) {} ~CallbackMethod() {} } _callback; }; public: struct Properties { static const bool preservesOrder = true; static const bool allowsBlockPassthrough = false; static const bool inputSizeRestrictsOutputSize = false; }; using Fetcher = SingleRowFetcher; using Infos = IndexExecutorInfos; using Stats = IndexStats; IndexExecutor() = delete; IndexExecutor(IndexExecutor&&) = default; IndexExecutor(IndexExecutor const&) = delete; IndexExecutor(Fetcher& fetcher, Infos&); ~IndexExecutor(); /** * @brief produce the next Row of Aql Values. * * @return ExecutionState, and if successful exactly one new Row of AqlItems. */ std::pair produceRows(OutputAqlItemRow& output); std::tuple skipRows(size_t toSkip); public: void initializeCursor(); private: bool advanceCursor(); void executeExpressions(InputAqlItemRow& input); void initIndexes(InputAqlItemRow& input); inline CursorReader& getCursor() { TRI_ASSERT(_currentIndex < _cursors.size()); return _cursors[_currentIndex]; } inline bool needsUniquenessCheck() const { return _infos.getIndexes().size() > 1 || _infos.hasMultipleExpansions(); } private: Infos& _infos; Fetcher& _fetcher; DocumentProducingFunctionContext _documentProducingFunctionContext; ExecutionState _state; InputAqlItemRow _input; /// @brief a vector of cursors for the index block /// cursors can be reused std::vector _cursors; /// @brief current position in _indexes size_t _currentIndex; /// @brief Count how many documents have been skipped during one call. /// Retained during WAITING situations. /// Needs to be 0 after we return a result. size_t _skipped; }; } // namespace aql } // namespace arangodb #endif