mirror of https://gitee.com/bigwinds/arangodb
Bug fix/aql speedup (#7379)
This commit is contained in:
parent
56289dcdbb
commit
bf2eeb16cd
|
@ -3554,7 +3554,16 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
|
|||
if (slice.isNumber()) {
|
||||
if (slice.isSmallInt() || slice.isInt()) {
|
||||
// integer value
|
||||
return createNodeValueInt(slice.getInt());
|
||||
return createNodeValueInt(slice.getIntUnchecked());
|
||||
}
|
||||
if (slice.isUInt()) {
|
||||
// check if we can safely convert the value from unsigned to signed
|
||||
// without data loss
|
||||
uint64_t v = slice.getUIntUnchecked();
|
||||
if (v <= uint64_t(INT64_MAX)) {
|
||||
return createNodeValueInt(static_cast<int64_t>(v));
|
||||
}
|
||||
// fall-through to floating point conversion
|
||||
}
|
||||
// floating point value
|
||||
return createNodeValueDouble(slice.getNumber<double>());
|
||||
|
@ -3562,7 +3571,7 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
|
|||
|
||||
if (slice.isString()) {
|
||||
VPackValueLength length;
|
||||
char const* p = slice.getString(length);
|
||||
char const* p = slice.getStringUnchecked(length);
|
||||
|
||||
if (copyStringValues) {
|
||||
// we must copy string values!
|
||||
|
|
|
@ -471,7 +471,7 @@ class Ast {
|
|||
static bool IsOrOperatorType(AstNodeType);
|
||||
|
||||
/// @brief create an AST node from vpack
|
||||
AstNode* nodeFromVPack(arangodb::velocypack::Slice const&, bool);
|
||||
AstNode* nodeFromVPack(arangodb::velocypack::Slice const&, bool copyStringValues);
|
||||
|
||||
/// @brief resolve an attribute access
|
||||
static AstNode const* resolveConstAttributeAccess(AstNode const*);
|
||||
|
|
|
@ -1362,7 +1362,7 @@ bool AstNode::isAttributeAccessForVariable(
|
|||
}
|
||||
auto node = this;
|
||||
|
||||
basics::StringBuffer indexBuff(false);
|
||||
basics::StringBuffer indexBuff;
|
||||
|
||||
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||
node->type == NODE_TYPE_INDEXED_ACCESS ||
|
||||
|
@ -2601,8 +2601,9 @@ void AstNode::markFinalized(AstNode* subtreeRoot) {
|
|||
}
|
||||
|
||||
subtreeRoot->setFlag(AstNodeFlagType::FLAG_FINALIZED);
|
||||
for (size_t i = 0; i < subtreeRoot->numMembers(); ++i) {
|
||||
markFinalized(subtreeRoot->getMember(i));
|
||||
size_t const n = subtreeRoot->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
markFinalized(subtreeRoot->getMemberUnchecked(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -544,7 +544,7 @@ struct AstNode {
|
|||
if (i >= members.size()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "member out of range");
|
||||
}
|
||||
members.at(i) = node;
|
||||
members[i] = node;
|
||||
}
|
||||
|
||||
/// @brief remove a member from the node
|
||||
|
|
|
@ -1115,45 +1115,54 @@ AqlValue Expression::executeSimpleExpressionOr(
|
|||
/// @brief execute an expression of type SIMPLE with AND or OR
|
||||
AqlValue Expression::executeSimpleExpressionNaryAndOr(
|
||||
AstNode const* node, transaction::Methods* trx, bool& mustDestroy) {
|
||||
mustDestroy = false;
|
||||
size_t count = node->numMembers();
|
||||
if (count == 0) {
|
||||
// There is nothing to evaluate. So this is always true
|
||||
mustDestroy = true;
|
||||
return AqlValue(AqlValueHintBool(true));
|
||||
}
|
||||
|
||||
// AND
|
||||
if (node->type == NODE_TYPE_OPERATOR_NARY_AND) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
bool localMustDestroy = false;
|
||||
AqlValue check =
|
||||
executeSimpleExpression(node->getMemberUnchecked(i), trx, mustDestroy, true);
|
||||
if (!check.toBoolean()) {
|
||||
// Check is false. Return it.
|
||||
return check;
|
||||
}
|
||||
executeSimpleExpression(node->getMemberUnchecked(i), trx, localMustDestroy, false);
|
||||
bool result = check.toBoolean();
|
||||
|
||||
if (mustDestroy) {
|
||||
if (localMustDestroy) {
|
||||
check.destroy();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
// we are allowed to return early here, because this is only called
|
||||
// in the context of index lookups
|
||||
return AqlValue(AqlValueHintBool(false));
|
||||
}
|
||||
}
|
||||
mustDestroy = true;
|
||||
return AqlValue(AqlValueHintBool(true));
|
||||
}
|
||||
|
||||
// OR
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
bool localMustDestroy = false;
|
||||
AqlValue check =
|
||||
executeSimpleExpression(node->getMemberUnchecked(i), trx, mustDestroy, true);
|
||||
if (check.toBoolean()) {
|
||||
// Check is true. Return it.
|
||||
return check;
|
||||
}
|
||||
executeSimpleExpression(node->getMemberUnchecked(i), trx, localMustDestroy, true);
|
||||
bool result = check.toBoolean();
|
||||
|
||||
if (mustDestroy) {
|
||||
if (localMustDestroy) {
|
||||
check.destroy();
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// we are allowed to return early here, because this is only called
|
||||
// in the context of index lookups
|
||||
return AqlValue(AqlValueHintBool(true));
|
||||
}
|
||||
}
|
||||
mustDestroy = true;
|
||||
|
||||
// anything else... we shouldn't get here
|
||||
TRI_ASSERT(false);
|
||||
return AqlValue(AqlValueHintBool(false));
|
||||
}
|
||||
|
||||
|
|
|
@ -182,10 +182,10 @@ void IndexBlock::executeExpressions() {
|
|||
AqlItemBlock* cur = _buffer.front();
|
||||
auto en = ExecutionNode::castTo<IndexNode const*>(getPlanNode());
|
||||
auto ast = en->_plan->getAst();
|
||||
AstNode const* oldCondition = _condition;
|
||||
AstNode* newCondition = ast->shallowCopyForModify(oldCondition);
|
||||
_condition = newCondition;
|
||||
TRI_DEFER(FINALIZE_SUBTREE(newCondition));
|
||||
AstNode* condition = const_cast<AstNode*>(_condition);
|
||||
|
||||
// modify the existing node in place
|
||||
TEMPORARILY_UNLOCK_NODE(condition);
|
||||
|
||||
Query* query = _engine->getQuery();
|
||||
|
||||
|
@ -204,15 +204,18 @@ void IndexBlock::executeExpressions() {
|
|||
VPackSlice slice = materializer.slice(a, false);
|
||||
AstNode* evaluatedNode = ast->nodeFromVPack(slice, true);
|
||||
|
||||
AstNode* tmp = newCondition;
|
||||
AstNode* tmp = condition;
|
||||
for (size_t x = 0; x < toReplace->indexPath.size(); x++) {
|
||||
size_t idx = toReplace->indexPath[x];
|
||||
AstNode* old = tmp->getMember(idx);
|
||||
// modify the node in place
|
||||
TEMPORARILY_UNLOCK_NODE(tmp);
|
||||
if (x + 1 < toReplace->indexPath.size()) {
|
||||
AstNode* cpy = ast->shallowCopyForModify(old);
|
||||
AstNode* cpy = old;
|
||||
tmp->changeMember(idx, cpy);
|
||||
tmp = cpy;
|
||||
} else {
|
||||
// insert the actual expression value
|
||||
tmp->changeMember(idx, evaluatedNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -981,7 +981,7 @@ void arangodb::aql::removeRedundantSortsRule(
|
|||
}
|
||||
|
||||
std::unordered_set<ExecutionNode*> toUnlink;
|
||||
arangodb::basics::StringBuffer buffer(false);
|
||||
arangodb::basics::StringBuffer buffer;
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
if (toUnlink.find(n) != toUnlink.end()) {
|
||||
|
@ -2523,7 +2523,7 @@ void arangodb::aql::removeRedundantCalculationsRule(
|
|||
return;
|
||||
}
|
||||
|
||||
arangodb::basics::StringBuffer buffer(false);
|
||||
arangodb::basics::StringBuffer buffer;
|
||||
std::unordered_map<VariableId, Variable const*> replacements;
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
|
|
|
@ -480,7 +480,7 @@ std::unique_ptr<IndexIterator> ClusterCollection::getAnyIterator(
|
|||
|
||||
void ClusterCollection::invokeOnAllElements(
|
||||
transaction::Methods* trx,
|
||||
std::function<bool(LocalDocumentId const&)> callback) {
|
||||
std::function<bool(LocalDocumentId const&)> /*callback*/) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
|
@ -532,7 +532,7 @@ Result ClusterCollection::update(
|
|||
OperationOptions& options, TRI_voc_tick_t& resultMarkerTick, bool,
|
||||
TRI_voc_rid_t& prevRev, ManagedDocumentResult& previous,
|
||||
arangodb::velocypack::Slice const key,
|
||||
std::function<Result(void)> callbackDuringLock) {
|
||||
std::function<Result(void)> /*callbackDuringLock*/) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
|
@ -541,7 +541,7 @@ Result ClusterCollection::replace(
|
|||
ManagedDocumentResult& mdr, OperationOptions& options,
|
||||
TRI_voc_tick_t& resultMarkerTick, bool, TRI_voc_rid_t& prevRev,
|
||||
ManagedDocumentResult& previous,
|
||||
std::function<Result(void)> callbackDuringLock) {
|
||||
std::function<Result(void)> /*callbackDuringLock*/) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
|
@ -549,7 +549,7 @@ Result ClusterCollection::remove(
|
|||
arangodb::transaction::Methods* trx, arangodb::velocypack::Slice slice,
|
||||
arangodb::ManagedDocumentResult& previous, OperationOptions& options,
|
||||
TRI_voc_tick_t& resultMarkerTick, bool, TRI_voc_rid_t& prevRev,
|
||||
TRI_voc_rid_t& revisionId, std::function<Result(void)> callbackDuringLock) {
|
||||
TRI_voc_rid_t& revisionId, std::function<Result(void)> /*callbackDuringLock*/) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
|
|
|
@ -285,6 +285,7 @@ bool MMFilesHashIndexIterator::nextDocument(DocumentCallback const& cb, size_t l
|
|||
|
||||
void MMFilesHashIndexIterator::reset() {
|
||||
_buffer.clear();
|
||||
_documentIds.clear();
|
||||
_posInBuffer = 0;
|
||||
_lookups.reset();
|
||||
_index->lookup(_trx, _lookups.lookup(), _buffer);
|
||||
|
|
|
@ -57,7 +57,60 @@ static std::vector<std::vector<arangodb::basics::AttributeName>> const
|
|||
IndexAttributes{{arangodb::basics::AttributeName("_id", false)},
|
||||
{arangodb::basics::AttributeName("_key", false)}};
|
||||
|
||||
MMFilesPrimaryIndexIterator::MMFilesPrimaryIndexIterator(
|
||||
MMFilesPrimaryIndexEqIterator::MMFilesPrimaryIndexEqIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
MMFilesPrimaryIndex const* index,
|
||||
std::unique_ptr<VPackBuilder> key)
|
||||
: IndexIterator(collection, trx),
|
||||
_index(index),
|
||||
_key(std::move(key)),
|
||||
_done(false) {
|
||||
TRI_ASSERT(_key->slice().isString());
|
||||
}
|
||||
|
||||
MMFilesPrimaryIndexEqIterator::~MMFilesPrimaryIndexEqIterator() {
|
||||
if (_key != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_key.release());
|
||||
}
|
||||
}
|
||||
|
||||
bool MMFilesPrimaryIndexEqIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(limit > 0);
|
||||
if (_done || limit == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
TRI_ASSERT(_key->slice().isString());
|
||||
MMFilesSimpleIndexElement result =
|
||||
_index->lookupKey(_trx, _key->slice());
|
||||
if (result) {
|
||||
cb(LocalDocumentId{result.localDocumentId()});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MMFilesPrimaryIndexEqIterator::nextDocument(DocumentCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(limit > 0);
|
||||
if (_done || limit == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
ManagedDocumentResult mdr;
|
||||
TRI_ASSERT(_key->slice().isString());
|
||||
MMFilesSimpleIndexElement result =
|
||||
_index->lookupKey(_trx, _key->slice(), mdr);
|
||||
if (result) {
|
||||
cb(result.localDocumentId(), VPackSlice(mdr.vpack()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MMFilesPrimaryIndexEqIterator::reset() { _done = false; }
|
||||
|
||||
MMFilesPrimaryIndexInIterator::MMFilesPrimaryIndexInIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
MMFilesPrimaryIndex const* index,
|
||||
std::unique_ptr<VPackBuilder> keys)
|
||||
|
@ -68,14 +121,14 @@ MMFilesPrimaryIndexIterator::MMFilesPrimaryIndexIterator(
|
|||
TRI_ASSERT(_keys->slice().isArray());
|
||||
}
|
||||
|
||||
MMFilesPrimaryIndexIterator::~MMFilesPrimaryIndexIterator() {
|
||||
MMFilesPrimaryIndexInIterator::~MMFilesPrimaryIndexInIterator() {
|
||||
if (_keys != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_keys.release());
|
||||
}
|
||||
}
|
||||
|
||||
bool MMFilesPrimaryIndexIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
bool MMFilesPrimaryIndexInIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(limit > 0);
|
||||
if (!_iterator.valid() || limit == 0) {
|
||||
return false;
|
||||
|
@ -93,7 +146,7 @@ bool MMFilesPrimaryIndexIterator::next(LocalDocumentIdCallback const& cb, size_t
|
|||
return _iterator.valid();
|
||||
}
|
||||
|
||||
void MMFilesPrimaryIndexIterator::reset() { _iterator.reset(); }
|
||||
void MMFilesPrimaryIndexInIterator::reset() { _iterator.reset(); }
|
||||
|
||||
MMFilesAllIndexIterator::MMFilesAllIndexIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
|
@ -275,10 +328,7 @@ void MMFilesPrimaryIndex::unload() {
|
|||
MMFilesSimpleIndexElement MMFilesPrimaryIndex::lookupKey(
|
||||
transaction::Methods* trx, VPackSlice const& key) const {
|
||||
ManagedDocumentResult mdr;
|
||||
IndexLookupContext context(trx, &_collection, &mdr, 1);
|
||||
TRI_ASSERT(key.isString());
|
||||
|
||||
return _primaryIndex->findByKey(&context, key.begin());
|
||||
return lookupKey(trx, key, mdr);
|
||||
}
|
||||
|
||||
/// @brief looks up an element given a key
|
||||
|
@ -467,11 +517,12 @@ IndexIterator* MMFilesPrimaryIndex::iteratorForCondition(
|
|||
arangodb::aql::Variable const* reference,
|
||||
IndexIteratorOptions const& opts) {
|
||||
TRI_ASSERT(!isSorted() || opts.sorted);
|
||||
TRI_ASSERT(node->type == aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
|
||||
auto comp = node->getMember(0);
|
||||
auto comp = node;
|
||||
if (node->type == aql::NODE_TYPE_OPERATOR_NARY_AND) {
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
comp = node->getMember(0);
|
||||
}
|
||||
|
||||
// assume a.b == value
|
||||
auto attrNode = comp->getMember(0);
|
||||
|
@ -488,17 +539,17 @@ IndexIterator* MMFilesPrimaryIndex::iteratorForCondition(
|
|||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||
// a.b == value
|
||||
return createEqIterator(trx, attrNode, valNode);
|
||||
} else if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (!valNode->isArray()) {
|
||||
// a.b IN non-array
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
return createInIterator(trx, attrNode, valNode);
|
||||
}
|
||||
|
||||
// operator type unsupported
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (valNode->isArray()) {
|
||||
// a.b IN array
|
||||
return createInIterator(trx, attrNode, valNode);
|
||||
}
|
||||
}
|
||||
|
||||
// operator type unsupported or IN used on non-array
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
|
@ -541,7 +592,7 @@ IndexIterator* MMFilesPrimaryIndex::createInIterator(
|
|||
|
||||
keys->close();
|
||||
|
||||
return new MMFilesPrimaryIndexIterator(
|
||||
return new MMFilesPrimaryIndexInIterator(
|
||||
&_collection, trx, this, std::move(keys)
|
||||
);
|
||||
}
|
||||
|
@ -556,21 +607,22 @@ IndexIterator* MMFilesPrimaryIndex::createEqIterator(
|
|||
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
transaction::BuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->openArray();
|
||||
std::unique_ptr<VPackBuilder> key(builder.steal());
|
||||
|
||||
// handle the sole element
|
||||
handleValNode(trx, keys.get(), valNode, isId);
|
||||
handleValNode(trx, key.get(), valNode, isId);
|
||||
|
||||
TRI_IF_FAILURE("PrimaryIndex::noIterator") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
keys->close();
|
||||
if (!key->isEmpty()) {
|
||||
return new MMFilesPrimaryIndexEqIterator(
|
||||
&_collection, trx, this, std::move(key)
|
||||
);
|
||||
}
|
||||
|
||||
return new MMFilesPrimaryIndexIterator(
|
||||
&_collection, trx, this, std::move(keys)
|
||||
);
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
/// @brief add a single value node to the iterator's keys
|
||||
|
|
|
@ -97,16 +97,39 @@ struct MMFilesPrimaryIndexHelper {
|
|||
typedef arangodb::basics::AssocUnique<uint8_t, MMFilesSimpleIndexElement, MMFilesPrimaryIndexHelper>
|
||||
MMFilesPrimaryIndexImpl;
|
||||
|
||||
class MMFilesPrimaryIndexIterator final : public IndexIterator {
|
||||
class MMFilesPrimaryIndexEqIterator final : public IndexIterator {
|
||||
public:
|
||||
MMFilesPrimaryIndexIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
MMFilesPrimaryIndex const* index,
|
||||
std::unique_ptr<VPackBuilder> keys);
|
||||
MMFilesPrimaryIndexEqIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
MMFilesPrimaryIndex const* index,
|
||||
std::unique_ptr<VPackBuilder> keys);
|
||||
|
||||
~MMFilesPrimaryIndexIterator();
|
||||
~MMFilesPrimaryIndexEqIterator();
|
||||
|
||||
char const* typeName() const override { return "primary-index-iterator"; }
|
||||
char const* typeName() const override { return "primary-index-eq-iterator"; }
|
||||
|
||||
bool next(LocalDocumentIdCallback const& cb, size_t limit) override;
|
||||
|
||||
bool nextDocument(DocumentCallback const& cb, size_t limit) override;
|
||||
|
||||
void reset() override;
|
||||
|
||||
private:
|
||||
MMFilesPrimaryIndex const* _index;
|
||||
std::unique_ptr<VPackBuilder> _key;
|
||||
bool _done;
|
||||
};
|
||||
|
||||
class MMFilesPrimaryIndexInIterator final : public IndexIterator {
|
||||
public:
|
||||
MMFilesPrimaryIndexInIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
MMFilesPrimaryIndex const* index,
|
||||
std::unique_ptr<VPackBuilder> keys);
|
||||
|
||||
~MMFilesPrimaryIndexInIterator();
|
||||
|
||||
char const* typeName() const override { return "primary-index-in-iterator"; }
|
||||
|
||||
bool next(LocalDocumentIdCallback const& cb, size_t limit) override;
|
||||
|
||||
|
|
|
@ -73,7 +73,62 @@ static std::vector<std::vector<arangodb::basics::AttributeName>> const
|
|||
IndexAttributes{{arangodb::basics::AttributeName("_id", false)},
|
||||
{arangodb::basics::AttributeName("_key", false)}};
|
||||
|
||||
RocksDBPrimaryIndexIterator::RocksDBPrimaryIndexIterator(
|
||||
RocksDBPrimaryIndexEqIterator::RocksDBPrimaryIndexEqIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
RocksDBPrimaryIndex* index,
|
||||
std::unique_ptr<VPackBuilder> key,
|
||||
bool allowCoveringIndexOptimization)
|
||||
: IndexIterator(collection, trx),
|
||||
_index(index),
|
||||
_key(std::move(key)),
|
||||
_done(false),
|
||||
_allowCoveringIndexOptimization(allowCoveringIndexOptimization) {
|
||||
TRI_ASSERT(_key->slice().isString());
|
||||
}
|
||||
|
||||
RocksDBPrimaryIndexEqIterator::~RocksDBPrimaryIndexEqIterator() {
|
||||
if (_key != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_key.release());
|
||||
}
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexEqIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
if (limit == 0 || _done) {
|
||||
// No limit no data, or we are actually done. The last call should have
|
||||
// returned false
|
||||
TRI_ASSERT(limit > 0); // Someone called with limit == 0. Api broken
|
||||
return false;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
LocalDocumentId documentId = _index->lookupKey(_trx, StringRef(_key->slice()));
|
||||
if (documentId.isSet()) {
|
||||
cb(documentId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexEqIterator::nextCovering(DocumentCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(_allowCoveringIndexOptimization);
|
||||
if (limit == 0 || _done) {
|
||||
// No limit no data, or we are actually done. The last call should have
|
||||
// returned false
|
||||
TRI_ASSERT(limit > 0); // Someone called with limit == 0. Api broken
|
||||
return false;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
LocalDocumentId documentId = _index->lookupKey(_trx, StringRef(_key->slice()));
|
||||
if (documentId.isSet()) {
|
||||
cb(documentId, _key->slice());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RocksDBPrimaryIndexEqIterator::reset() { _done = false; }
|
||||
|
||||
RocksDBPrimaryIndexInIterator::RocksDBPrimaryIndexInIterator(
|
||||
LogicalCollection* collection, transaction::Methods* trx,
|
||||
RocksDBPrimaryIndex* index,
|
||||
std::unique_ptr<VPackBuilder> keys,
|
||||
|
@ -86,14 +141,14 @@ RocksDBPrimaryIndexIterator::RocksDBPrimaryIndexIterator(
|
|||
TRI_ASSERT(_keys->slice().isArray());
|
||||
}
|
||||
|
||||
RocksDBPrimaryIndexIterator::~RocksDBPrimaryIndexIterator() {
|
||||
RocksDBPrimaryIndexInIterator::~RocksDBPrimaryIndexInIterator() {
|
||||
if (_keys != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_keys.release());
|
||||
}
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
bool RocksDBPrimaryIndexInIterator::next(LocalDocumentIdCallback const& cb, size_t limit) {
|
||||
if (limit == 0 || !_iterator.valid()) {
|
||||
// No limit no data, or we are actually done. The last call should have
|
||||
// returned false
|
||||
|
@ -102,7 +157,6 @@ bool RocksDBPrimaryIndexIterator::next(LocalDocumentIdCallback const& cb, size_t
|
|||
}
|
||||
|
||||
while (limit > 0) {
|
||||
// TODO: prevent copying of the value into result, as we don't need it here!
|
||||
LocalDocumentId documentId = _index->lookupKey(_trx, StringRef(*_iterator));
|
||||
if (documentId.isSet()) {
|
||||
cb(documentId);
|
||||
|
@ -117,7 +171,8 @@ bool RocksDBPrimaryIndexIterator::next(LocalDocumentIdCallback const& cb, size_t
|
|||
return true;
|
||||
}
|
||||
|
||||
bool RocksDBPrimaryIndexIterator::nextCovering(DocumentCallback const& cb, size_t limit) {
|
||||
bool RocksDBPrimaryIndexInIterator::nextCovering(DocumentCallback const& cb, size_t limit) {
|
||||
TRI_ASSERT(_allowCoveringIndexOptimization);
|
||||
if (limit == 0 || !_iterator.valid()) {
|
||||
// No limit no data, or we are actually done. The last call should have
|
||||
// returned false
|
||||
|
@ -141,7 +196,7 @@ bool RocksDBPrimaryIndexIterator::nextCovering(DocumentCallback const& cb, size_
|
|||
return true;
|
||||
}
|
||||
|
||||
void RocksDBPrimaryIndexIterator::reset() { _iterator.reset(); }
|
||||
void RocksDBPrimaryIndexInIterator::reset() { _iterator.reset(); }
|
||||
|
||||
// ================ PrimaryIndex ================
|
||||
|
||||
|
@ -385,17 +440,17 @@ IndexIterator* RocksDBPrimaryIndex::iteratorForCondition(
|
|||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||
// a.b == value
|
||||
return createEqIterator(trx, attrNode, valNode);
|
||||
} else if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (!valNode->isArray()) {
|
||||
// a.b IN non-array
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
return createInIterator(trx, attrNode, valNode);
|
||||
}
|
||||
|
||||
// operator type unsupported
|
||||
if (comp->type == aql::NODE_TYPE_OPERATOR_BINARY_IN) {
|
||||
// a.b IN values
|
||||
if (valNode->isArray()) {
|
||||
// a.b IN array
|
||||
return createInIterator(trx, attrNode, valNode);
|
||||
}
|
||||
}
|
||||
|
||||
// operator type unsupported or IN used on non-array
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
|
@ -438,7 +493,7 @@ IndexIterator* RocksDBPrimaryIndex::createInIterator(
|
|||
|
||||
keys->close();
|
||||
|
||||
return new RocksDBPrimaryIndexIterator(
|
||||
return new RocksDBPrimaryIndexInIterator(
|
||||
&_collection, trx, this, std::move(keys), !isId
|
||||
);
|
||||
}
|
||||
|
@ -453,21 +508,22 @@ IndexIterator* RocksDBPrimaryIndex::createEqIterator(
|
|||
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
transaction::BuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->openArray();
|
||||
std::unique_ptr<VPackBuilder> key(builder.steal());
|
||||
|
||||
// handle the sole element
|
||||
handleValNode(trx, keys.get(), valNode, isId);
|
||||
handleValNode(trx, key.get(), valNode, isId);
|
||||
|
||||
TRI_IF_FAILURE("PrimaryIndex::noIterator") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
keys->close();
|
||||
if (!key->isEmpty()) {
|
||||
return new RocksDBPrimaryIndexEqIterator(
|
||||
&_collection, trx, this, std::move(key), !isId
|
||||
);
|
||||
}
|
||||
|
||||
return new RocksDBPrimaryIndexIterator(
|
||||
&_collection, trx, this, std::move(keys), !isId
|
||||
);
|
||||
return new EmptyIndexIterator(&_collection, trx);
|
||||
}
|
||||
|
||||
/// @brief add a single value node to the iterator's keys
|
||||
|
|
|
@ -47,17 +47,46 @@ namespace transaction {
|
|||
class Methods;
|
||||
}
|
||||
|
||||
class RocksDBPrimaryIndexIterator final : public IndexIterator {
|
||||
class RocksDBPrimaryIndexEqIterator final : public IndexIterator {
|
||||
public:
|
||||
RocksDBPrimaryIndexIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
RocksDBPrimaryIndex* index,
|
||||
std::unique_ptr<VPackBuilder> keys,
|
||||
bool allowCoveringIndexOptimization);
|
||||
RocksDBPrimaryIndexEqIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
RocksDBPrimaryIndex* index,
|
||||
std::unique_ptr<VPackBuilder> key,
|
||||
bool allowCoveringIndexOptimization);
|
||||
|
||||
~RocksDBPrimaryIndexIterator();
|
||||
~RocksDBPrimaryIndexEqIterator();
|
||||
|
||||
char const* typeName() const override { return "primary-index-iterator"; }
|
||||
char const* typeName() const override { return "primary-index-eq-iterator"; }
|
||||
|
||||
bool next(LocalDocumentIdCallback const& cb, size_t limit) override;
|
||||
|
||||
bool nextCovering(DocumentCallback const& cb, size_t limit) override;
|
||||
|
||||
void reset() override;
|
||||
|
||||
/// @brief we provide a method to provide the index attribute values
|
||||
/// while scanning the index
|
||||
bool hasCovering() const override { return _allowCoveringIndexOptimization; }
|
||||
|
||||
private:
|
||||
RocksDBPrimaryIndex* _index;
|
||||
std::unique_ptr<VPackBuilder> _key;
|
||||
bool _done;
|
||||
bool const _allowCoveringIndexOptimization;
|
||||
};
|
||||
|
||||
class RocksDBPrimaryIndexInIterator final : public IndexIterator {
|
||||
public:
|
||||
RocksDBPrimaryIndexInIterator(LogicalCollection* collection,
|
||||
transaction::Methods* trx,
|
||||
RocksDBPrimaryIndex* index,
|
||||
std::unique_ptr<VPackBuilder> keys,
|
||||
bool allowCoveringIndexOptimization);
|
||||
|
||||
~RocksDBPrimaryIndexInIterator();
|
||||
|
||||
char const* typeName() const override { return "primary-index-in-iterator"; }
|
||||
|
||||
bool next(LocalDocumentIdCallback const& cb, size_t limit) override;
|
||||
|
||||
|
@ -77,7 +106,8 @@ class RocksDBPrimaryIndexIterator final : public IndexIterator {
|
|||
};
|
||||
|
||||
class RocksDBPrimaryIndex final : public RocksDBIndex {
|
||||
friend class RocksDBPrimaryIndexIterator;
|
||||
friend class RocksDBPrimaryIndexEqIterator;
|
||||
friend class RocksDBPrimaryIndexInIterator;
|
||||
friend class RocksDBAllIndexIterator;
|
||||
friend class RocksDBAnyIndexIterator;
|
||||
|
||||
|
|
|
@ -241,11 +241,18 @@ namespace basics {
|
|||
|
||||
/// @brief string buffer with formatting routines
|
||||
class StringBuffer {
|
||||
StringBuffer() = delete;
|
||||
StringBuffer(StringBuffer const&) = delete;
|
||||
StringBuffer& operator=(StringBuffer const&) = delete;
|
||||
|
||||
public:
|
||||
/// @brief creates an uninitialized string buffer
|
||||
StringBuffer() {
|
||||
_buffer._buffer = nullptr;
|
||||
_buffer._current = nullptr;
|
||||
_buffer._len = 0;
|
||||
_buffer._initializeMemory = false;
|
||||
}
|
||||
|
||||
/// @brief initializes the string buffer
|
||||
explicit StringBuffer(bool initializeMemory) {
|
||||
TRI_InitStringBuffer(&_buffer, initializeMemory);
|
||||
|
|
Loading…
Reference in New Issue