/// @brief sendToClient: for each row of the incoming AqlItemBlock use the /// attributes <shardKeys> of the Aql value <val> to determine to which shard /// the row should be sent and return its clientId size_t DistributeBlock::sendToClient(AqlItemBlock* cur) { DEBUG_BEGIN_BLOCK(); // inspect cur in row _pos and check to which shard it should be sent . . AqlValue val = cur->getValueReference(_pos, _regId); VPackSlice input = val.slice(); // will throw when wrong type bool usedAlternativeRegId = false; if (input.isNull() && _alternativeRegId != ExecutionNode::MaxRegisterId) { // value is set, but null // check if there is a second input register available (UPSERT makes use of // two input registers, // one for the search document, the other for the insert document) val = cur->getValueReference(_pos, _alternativeRegId); input = val.slice(); // will throw when wrong type usedAlternativeRegId = true; } VPackSlice value = input; VPackBuilder builder; VPackBuilder builder2; bool hasCreatedKeyAttribute = false; if (input.isString() && static_cast<DistributeNode const*>(_exeNode) ->_allowKeyConversionToObject) { builder.openObject(); builder.add(StaticStrings::KeyString, input); builder.close(); // clear the previous value cur->destroyValue(_pos, _regId); // overwrite with new value cur->setValue(_pos, _regId, AqlValue(builder)); value = builder.slice(); hasCreatedKeyAttribute = true; } else if (!input.isObject()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } TRI_ASSERT(value.isObject()); if (static_cast<DistributeNode const*>(_exeNode)->_createKeys) { // we are responsible for creating keys if none present if (_usesDefaultSharding) { // the collection is sharded by _key... if (!hasCreatedKeyAttribute && !value.hasKey(StaticStrings::KeyString)) { // there is no _key attribute present, so we are responsible for // creating one VPackBuilder temp; temp.openObject(); temp.add(StaticStrings::KeyString, VPackValue(createKey(value))); temp.close(); builder2 = VPackCollection::merge(input, temp.slice(), true); // clear the previous value and overwrite with new value: if (usedAlternativeRegId) { cur->destroyValue(_pos, _alternativeRegId); cur->setValue(_pos, _alternativeRegId, AqlValue(builder2)); } else { cur->destroyValue(_pos, _regId); cur->setValue(_pos, _regId, AqlValue(builder2)); } value = builder2.slice(); } } else { // the collection is not sharded by _key if (hasCreatedKeyAttribute || value.hasKey(StaticStrings::KeyString)) { // a _key was given, but user is not allowed to specify _key if (usedAlternativeRegId || !_allowSpecifiedKeys) { THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY); } } else { VPackBuilder temp; temp.openObject(); temp.add(StaticStrings::KeyString, VPackValue(createKey(value))); temp.close(); builder2 = VPackCollection::merge(input, temp.slice(), true); // clear the previous value and overwrite with new value: if (usedAlternativeRegId) { cur->destroyValue(_pos, _alternativeRegId); cur->setValue(_pos, _alternativeRegId, AqlValue(builder2.slice())); } else { cur->destroyValue(_pos, _regId); cur->setValue(_pos, _regId, AqlValue(builder2.slice())); } value = builder2.slice(); } } } std::string shardId; bool usesDefaultShardingAttributes; auto clusterInfo = arangodb::ClusterInfo::instance(); auto collInfo = _collection->getCollection(); int res = clusterInfo->getResponsibleShard(collInfo.get(), value, true, shardId, usesDefaultShardingAttributes); // std::cout << "SHARDID: " << shardId << "\n"; if (res != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(res); } TRI_ASSERT(!shardId.empty()); return getClientId(shardId); // cppcheck-suppress style DEBUG_END_BLOCK(); }
/// @brief 3-way comparison for AqlValue objects int AqlValue::Compare(arangodb::AqlTransaction* trx, AqlValue const& left, AqlValue const& right, bool compareUtf8) { VPackOptions* options = trx->transactionContext()->getVPackOptions(); AqlValue::AqlValueType const leftType = left.type(); AqlValue::AqlValueType const rightType = right.type(); if (leftType != rightType) { if (leftType == RANGE || rightType == RANGE || leftType == DOCVEC || rightType == DOCVEC) { // range|docvec against x VPackBuilder leftBuilder; left.toVelocyPack(trx, leftBuilder, false); VPackBuilder rightBuilder; right.toVelocyPack(trx, rightBuilder, false); return arangodb::basics::VelocyPackHelper::compare(leftBuilder.slice(), rightBuilder.slice(), compareUtf8, options); } // fall-through to other types intentional } // if we get here, types are equal or can be treated as being equal switch (leftType) { case VPACK_SLICE_POINTER: case VPACK_INLINE: case VPACK_MANAGED: { return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(), compareUtf8, options); } case DOCVEC: { // use lexicographic ordering of AqlValues regardless of block, // DOCVECs have a single register coming from ReturnNode. size_t lblock = 0; size_t litem = 0; size_t rblock = 0; size_t ritem = 0; size_t const lsize = left._data.docvec->size(); size_t const rsize = right._data.docvec->size(); if (lsize == 0 || rsize == 0) { if (lsize == rsize) { // both empty return 0; } return (lsize < rsize ? -1 : 1); } size_t lrows = left._data.docvec->at(0)->size(); size_t rrows = right._data.docvec->at(0)->size(); while (lblock < lsize && rblock < rsize) { AqlValue const& lval = left._data.docvec->at(lblock)->getValueReference(litem, 0); AqlValue const& rval = right._data.docvec->at(rblock)->getValueReference(ritem, 0); int cmp = Compare(trx, lval, rval, compareUtf8); if (cmp != 0) { return cmp; } if (++litem == lrows) { litem = 0; lblock++; if (lblock < lsize) { lrows = left._data.docvec->at(lblock)->size(); } } if (++ritem == rrows) { ritem = 0; rblock++; if (rblock < rsize) { rrows = right._data.docvec->at(rblock)->size(); } } } if (lblock == lsize && rblock == rsize) { // both blocks exhausted return 0; } return (lblock < lsize ? -1 : 1); } case RANGE: { if (left.range()->_low < right.range()->_low) { return -1; } if (left.range()->_low > right.range()->_low) { return 1; } if (left.range()->_high < right.range()->_high) { return -1; } if (left.range()->_high > right.range()->_high) { return 1; } return 0; } } return 0; }