mirror of https://gitee.com/bigwinds/arangodb
467 lines
14 KiB
C++
467 lines
14 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 triAGENS 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 Michael Hackstein
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "PhysicalCollection.h"
|
|
|
|
#include "Basics/StaticStrings.h"
|
|
#include "Basics/ReadLocker.h"
|
|
#include "Basics/StringRef.h"
|
|
#include "Basics/VelocyPackHelper.h"
|
|
#include "Basics/WriteLocker.h"
|
|
#include "Basics/encoding.h"
|
|
#include "Indexes/Index.h"
|
|
#include "StorageEngine/TransactionState.h"
|
|
#include "Transaction/Methods.h"
|
|
#include "VocBase/KeyGenerator.h"
|
|
#include "VocBase/LogicalCollection.h"
|
|
#include "VocBase/ticks.h"
|
|
#include "VocBase/vocbase.h"
|
|
|
|
#include <velocypack/Builder.h>
|
|
#include <velocypack/Collection.h>
|
|
#include <velocypack/Iterator.h>
|
|
#include <velocypack/Slice.h>
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
using namespace arangodb;
|
|
|
|
PhysicalCollection::PhysicalCollection(LogicalCollection* collection,
|
|
VPackSlice const& info)
|
|
: _logicalCollection(collection),
|
|
_isDBServer(ServerState::instance()->isDBServer()),
|
|
_indexes() {}
|
|
|
|
void PhysicalCollection::figures(
|
|
std::shared_ptr<arangodb::velocypack::Builder>& builder) {
|
|
this->figuresSpecific(builder);
|
|
};
|
|
|
|
void PhysicalCollection::drop() {
|
|
{
|
|
WRITE_LOCKER(guard, _indexesLock);
|
|
_indexes.clear();
|
|
}
|
|
try {
|
|
// close collection. this will also invalidate the revisions cache
|
|
close();
|
|
} catch (...) {
|
|
// don't throw from here... dropping should succeed
|
|
}
|
|
}
|
|
|
|
bool PhysicalCollection::hasIndexOfType(arangodb::Index::IndexType type) const {
|
|
READ_LOCKER(guard, _indexesLock);
|
|
for (auto const& idx : _indexes) {
|
|
if (idx->type() == type) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::shared_ptr<Index> PhysicalCollection::lookupIndex(
|
|
TRI_idx_iid_t idxId) const {
|
|
READ_LOCKER(guard, _indexesLock);
|
|
for (auto const& idx : _indexes) {
|
|
if (idx->id() == idxId) {
|
|
return idx;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TRI_voc_rid_t PhysicalCollection::newRevisionId() const {
|
|
return TRI_HybridLogicalClock();
|
|
}
|
|
|
|
/// @brief merge two objects for update, oldValue must have correctly set
|
|
/// _key and _id attributes
|
|
void PhysicalCollection::mergeObjectsForUpdate(
|
|
transaction::Methods* trx, VPackSlice const& oldValue,
|
|
VPackSlice const& newValue, bool isEdgeCollection,
|
|
bool mergeObjects, bool keepNull, VPackBuilder& b, bool isRestore, TRI_voc_rid_t& revisionId) const {
|
|
b.openObject();
|
|
|
|
VPackSlice keySlice = oldValue.get(StaticStrings::KeyString);
|
|
VPackSlice idSlice = oldValue.get(StaticStrings::IdString);
|
|
TRI_ASSERT(!keySlice.isNone());
|
|
TRI_ASSERT(!idSlice.isNone());
|
|
|
|
// Find the attributes in the newValue object:
|
|
VPackSlice fromSlice;
|
|
VPackSlice toSlice;
|
|
|
|
std::unordered_map<StringRef, VPackSlice> newValues;
|
|
{
|
|
VPackObjectIterator it(newValue, true);
|
|
while (it.valid()) {
|
|
StringRef key(it.key());
|
|
if (!key.empty() && key[0] == '_' &&
|
|
(key == StaticStrings::KeyString || key == StaticStrings::IdString ||
|
|
key == StaticStrings::RevString ||
|
|
key == StaticStrings::FromString ||
|
|
key == StaticStrings::ToString)) {
|
|
// note _from and _to and ignore _id, _key and _rev
|
|
if (key == StaticStrings::FromString) {
|
|
fromSlice = it.value();
|
|
} else if (key == StaticStrings::ToString) {
|
|
toSlice = it.value();
|
|
} // else do nothing
|
|
} else {
|
|
// regular attribute
|
|
newValues.emplace(key, it.value());
|
|
}
|
|
|
|
it.next();
|
|
}
|
|
}
|
|
|
|
if (isEdgeCollection) {
|
|
if (fromSlice.isNone()) {
|
|
fromSlice = oldValue.get(StaticStrings::FromString);
|
|
}
|
|
if (toSlice.isNone()) {
|
|
toSlice = oldValue.get(StaticStrings::ToString);
|
|
}
|
|
}
|
|
|
|
// add system attributes first, in this order:
|
|
// _key, _id, _from, _to, _rev
|
|
|
|
// _key
|
|
b.add(StaticStrings::KeyString, keySlice);
|
|
|
|
// _id
|
|
b.add(StaticStrings::IdString, idSlice);
|
|
|
|
// _from, _to
|
|
if (isEdgeCollection) {
|
|
TRI_ASSERT(!fromSlice.isNone());
|
|
TRI_ASSERT(!toSlice.isNone());
|
|
b.add(StaticStrings::FromString, fromSlice);
|
|
b.add(StaticStrings::ToString, toSlice);
|
|
}
|
|
|
|
// _rev
|
|
bool handled = false;
|
|
if (isRestore) {
|
|
// copy revision id verbatim
|
|
VPackSlice s = newValue.get(StaticStrings::RevString);
|
|
if (s.isString()) {
|
|
b.add(StaticStrings::RevString, s);
|
|
VPackValueLength l;
|
|
char const* p = s.getString(l);
|
|
revisionId = TRI_StringToRid(p, l, false);
|
|
handled = true;
|
|
}
|
|
}
|
|
if (!handled) {
|
|
revisionId = newRevisionId();
|
|
b.add(StaticStrings::RevString, VPackValue(TRI_RidToString(revisionId)));
|
|
}
|
|
|
|
// add other attributes after the system attributes
|
|
{
|
|
VPackObjectIterator it(oldValue, true);
|
|
while (it.valid()) {
|
|
StringRef key(it.key());
|
|
// exclude system attributes in old value now
|
|
if (!key.empty() && key[0] == '_' &&
|
|
(key == StaticStrings::KeyString || key == StaticStrings::IdString ||
|
|
key == StaticStrings::RevString ||
|
|
key == StaticStrings::FromString ||
|
|
key == StaticStrings::ToString)) {
|
|
it.next();
|
|
continue;
|
|
}
|
|
|
|
auto found = newValues.find(key);
|
|
|
|
if (found == newValues.end()) {
|
|
// use old value
|
|
b.add(key.data(), key.size(), it.value());
|
|
} else if (mergeObjects && it.value().isObject() &&
|
|
(*found).second.isObject()) {
|
|
// merge both values
|
|
auto& value = (*found).second;
|
|
if (keepNull || (!value.isNone() && !value.isNull())) {
|
|
VPackBuilder sub =
|
|
VPackCollection::merge(it.value(), value, true, !keepNull);
|
|
b.add(key.data(), key.size(), sub.slice());
|
|
}
|
|
// clear the value in the map so its not added again
|
|
(*found).second = VPackSlice();
|
|
} else {
|
|
// use new value
|
|
auto& value = (*found).second;
|
|
if (keepNull || (!value.isNone() && !value.isNull())) {
|
|
b.add(key.data(), key.size(), value);
|
|
}
|
|
// clear the value in the map so its not added again
|
|
(*found).second = VPackSlice();
|
|
}
|
|
it.next();
|
|
}
|
|
}
|
|
|
|
// add remaining values that were only in new object
|
|
for (auto const& it : newValues) {
|
|
VPackSlice const& s = it.second;
|
|
if (s.isNone()) {
|
|
continue;
|
|
}
|
|
if (!keepNull && s.isNull()) {
|
|
continue;
|
|
}
|
|
b.add(it.first.data(), it.first.size(), s);
|
|
}
|
|
|
|
b.close();
|
|
}
|
|
|
|
/// @brief new object for insert, computes the hash of the key
|
|
int PhysicalCollection::newObjectForInsert(
|
|
transaction::Methods* trx, VPackSlice const& value,
|
|
VPackSlice const& fromSlice, VPackSlice const& toSlice,
|
|
bool isEdgeCollection, VPackBuilder& builder, bool isRestore,
|
|
TRI_voc_rid_t& revisionId) const {
|
|
builder.openObject();
|
|
|
|
// add system attributes first, in this order:
|
|
// _key, _id, _from, _to, _rev
|
|
|
|
// _key
|
|
VPackSlice s = value.get(StaticStrings::KeyString);
|
|
if (s.isNone()) {
|
|
TRI_ASSERT(!isRestore); // need key in case of restore
|
|
std::string keyString =
|
|
_logicalCollection->keyGenerator()->generate();
|
|
if (keyString.empty()) {
|
|
return TRI_ERROR_ARANGO_OUT_OF_KEYS;
|
|
}
|
|
builder.add(StaticStrings::KeyString, VPackValue(keyString));
|
|
} else if (!s.isString()) {
|
|
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
|
|
} else {
|
|
VPackValueLength l;
|
|
char const* p = s.getString(l);
|
|
// validate and track the key just used
|
|
int res =
|
|
_logicalCollection->keyGenerator()->validate(p, l, isRestore);
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return res;
|
|
}
|
|
builder.add(StaticStrings::KeyString, s);
|
|
}
|
|
|
|
// _id
|
|
uint8_t* p = builder.add(StaticStrings::IdString,
|
|
VPackValuePair(9ULL, VPackValueType::Custom));
|
|
|
|
*p++ = 0xf3; // custom type for _id
|
|
|
|
if (_isDBServer && !_logicalCollection->system()) {
|
|
// db server in cluster, note: the local collections _statistics,
|
|
// _statisticsRaw and _statistics15 (which are the only system
|
|
// collections)
|
|
// must not be treated as shards but as local collections
|
|
encoding::storeNumber<uint64_t>(p, _logicalCollection->planId(),
|
|
sizeof(uint64_t));
|
|
} else {
|
|
// local server
|
|
encoding::storeNumber<uint64_t>(
|
|
p, _logicalCollection->id(), sizeof(uint64_t)
|
|
);
|
|
}
|
|
|
|
// _from and _to
|
|
if (isEdgeCollection) {
|
|
TRI_ASSERT(!fromSlice.isNone());
|
|
TRI_ASSERT(!toSlice.isNone());
|
|
builder.add(StaticStrings::FromString, fromSlice);
|
|
builder.add(StaticStrings::ToString, toSlice);
|
|
}
|
|
|
|
// _rev
|
|
bool handled = false;
|
|
if (isRestore) {
|
|
// copy revision id verbatim
|
|
s = value.get(StaticStrings::RevString);
|
|
if (s.isString()) {
|
|
builder.add(StaticStrings::RevString, s);
|
|
VPackValueLength l;
|
|
char const* p = s.getString(l);
|
|
revisionId = TRI_StringToRid(p, l, false);
|
|
handled = true;
|
|
}
|
|
}
|
|
if (!handled) {
|
|
revisionId = newRevisionId();
|
|
builder.add(StaticStrings::RevString, VPackValue(TRI_RidToString(revisionId)));
|
|
}
|
|
|
|
// add other attributes after the system attributes
|
|
TRI_SanitizeObjectWithEdges(value, builder);
|
|
|
|
builder.close();
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
/// @brief new object for remove, must have _key set
|
|
void PhysicalCollection::newObjectForRemove(transaction::Methods* trx,
|
|
VPackSlice const& oldValue,
|
|
VPackBuilder& builder,
|
|
bool isRestore, TRI_voc_rid_t& revisionId) const {
|
|
// create an object consisting of _key and _rev (in this order)
|
|
builder.openObject();
|
|
if (oldValue.isString()) {
|
|
builder.add(StaticStrings::KeyString, oldValue);
|
|
} else {
|
|
VPackSlice s = oldValue.get(StaticStrings::KeyString);
|
|
TRI_ASSERT(s.isString());
|
|
builder.add(StaticStrings::KeyString, s);
|
|
}
|
|
revisionId = newRevisionId();
|
|
builder.add(StaticStrings::RevString, VPackValue(TRI_RidToString(revisionId)));
|
|
builder.close();
|
|
}
|
|
|
|
/// @brief new object for replace, oldValue must have _key and _id correctly
|
|
/// set
|
|
void PhysicalCollection::newObjectForReplace(
|
|
transaction::Methods* trx, VPackSlice const& oldValue,
|
|
VPackSlice const& newValue, VPackSlice const& fromSlice,
|
|
VPackSlice const& toSlice, bool isEdgeCollection,
|
|
VPackBuilder& builder, bool isRestore, TRI_voc_rid_t& revisionId) const {
|
|
builder.openObject();
|
|
|
|
// add system attributes first, in this order:
|
|
// _key, _id, _from, _to, _rev
|
|
|
|
// _key
|
|
VPackSlice s = oldValue.get(StaticStrings::KeyString);
|
|
TRI_ASSERT(!s.isNone());
|
|
builder.add(StaticStrings::KeyString, s);
|
|
|
|
// _id
|
|
s = oldValue.get(StaticStrings::IdString);
|
|
TRI_ASSERT(!s.isNone());
|
|
builder.add(StaticStrings::IdString, s);
|
|
|
|
// _from and _to here
|
|
if (isEdgeCollection) {
|
|
TRI_ASSERT(!fromSlice.isNone());
|
|
TRI_ASSERT(!toSlice.isNone());
|
|
builder.add(StaticStrings::FromString, fromSlice);
|
|
builder.add(StaticStrings::ToString, toSlice);
|
|
}
|
|
|
|
// _rev
|
|
bool handled = false;
|
|
if (isRestore) {
|
|
// copy revision id verbatim
|
|
s = newValue.get(StaticStrings::RevString);
|
|
if (s.isString()) {
|
|
builder.add(StaticStrings::RevString, s);
|
|
VPackValueLength l;
|
|
char const* p = s.getString(l);
|
|
revisionId = TRI_StringToRid(p, l, false);
|
|
handled = true;
|
|
}
|
|
}
|
|
if (!handled) {
|
|
revisionId = newRevisionId();
|
|
builder.add(StaticStrings::RevString, VPackValue(TRI_RidToString(revisionId)));
|
|
}
|
|
|
|
// add other attributes after the system attributes
|
|
TRI_SanitizeObjectWithEdges(newValue, builder);
|
|
|
|
builder.close();
|
|
}
|
|
|
|
/// @brief checks the revision of a document
|
|
int PhysicalCollection::checkRevision(transaction::Methods* trx,
|
|
TRI_voc_rid_t expected,
|
|
TRI_voc_rid_t found) const {
|
|
if (expected != 0 && found != expected) {
|
|
return TRI_ERROR_ARANGO_CONFLICT;
|
|
}
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
/// @brief hands out a list of indexes
|
|
std::vector<std::shared_ptr<arangodb::Index>>
|
|
PhysicalCollection::getIndexes() const {
|
|
READ_LOCKER(guard, _indexesLock);
|
|
return _indexes;
|
|
}
|
|
|
|
void PhysicalCollection::getIndexesVPack(VPackBuilder& result, bool withFigures, bool forPersistence,
|
|
std::function<bool(arangodb::Index const*)> const& filter) const {
|
|
READ_LOCKER(guard, _indexesLock);
|
|
result.openArray();
|
|
for (auto const& idx : _indexes) {
|
|
if (!filter(idx.get())) {
|
|
continue;
|
|
}
|
|
idx->toVelocyPack(result, withFigures, forPersistence);
|
|
}
|
|
result.close();
|
|
}
|
|
|
|
/// @brief return the figures for a collection
|
|
std::shared_ptr<arangodb::velocypack::Builder> PhysicalCollection::figures() {
|
|
auto builder = std::make_shared<VPackBuilder>();
|
|
builder->openObject();
|
|
|
|
// add index information
|
|
size_t sizeIndexes = memory();
|
|
size_t numIndexes = 0;
|
|
|
|
{
|
|
bool seenEdgeIndex = false;
|
|
READ_LOCKER(guard, _indexesLock);
|
|
for (auto const& idx : _indexes) {
|
|
// only count an edge index instance
|
|
if (idx->type() != Index::TRI_IDX_TYPE_EDGE_INDEX || !seenEdgeIndex) {
|
|
++numIndexes;
|
|
}
|
|
if (idx->type() == Index::TRI_IDX_TYPE_EDGE_INDEX) {
|
|
seenEdgeIndex = true;
|
|
}
|
|
sizeIndexes += static_cast<size_t>(idx->memory());
|
|
}
|
|
}
|
|
|
|
builder->add("indexes", VPackValue(VPackValueType::Object));
|
|
builder->add("count", VPackValue(numIndexes));
|
|
builder->add("size", VPackValue(sizeIndexes));
|
|
builder->close(); // indexes
|
|
|
|
// add engine-specific figures
|
|
figures(builder);
|
|
builder->close();
|
|
return builder;
|
|
}
|