//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2017 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 Jan Steemann /// @author Daniel H. Larkin //////////////////////////////////////////////////////////////////////////////// #include "RocksDBEngine/RocksDBEntry.h" #include "Basics/Exceptions.h" using namespace arangodb; using namespace arangodb::velocypack; RocksDBEntry RocksDBEntry::Database(uint64_t id, VPackSlice const& data) { return RocksDBEntry(RocksDBEntryType::Database, id, 0, data); } RocksDBEntry RocksDBEntry::Collection(uint64_t id, VPackSlice const& data) { return RocksDBEntry(RocksDBEntryType::Collection, id, 0, data); } RocksDBEntry RocksDBEntry::Index(uint64_t id, VPackSlice const& data) { return RocksDBEntry(RocksDBEntryType::Index, id, 0, data); } RocksDBEntry RocksDBEntry::Document(uint64_t collectionId, uint64_t revisionId, VPackSlice const& data) { return RocksDBEntry(RocksDBEntryType::Document, collectionId, revisionId, data); } RocksDBEntry RocksDBEntry::IndexValue(uint64_t indexId, uint64_t revisionId, VPackSlice const& indexValues) { return RocksDBEntry(RocksDBEntryType::IndexValue, indexId, revisionId, indexValues); } RocksDBEntry RocksDBEntry::UniqueIndexValue(uint64_t indexId, uint64_t revisionId, VPackSlice const& indexValues) { return RocksDBEntry(RocksDBEntryType::UniqueIndexValue, indexId, revisionId, indexValues); } RocksDBEntry RocksDBEntry::View(uint64_t id, VPackSlice const& data) { return RocksDBEntry(RocksDBEntryType::View, id, 0, data); } RocksDBEntry RocksDBEntry::CrossReferenceCollection(uint64_t databaseId, uint64_t collectionId) { return RocksDBEntry(RocksDBEntryType::CrossReference, RocksDBEntryType::Collection, databaseId, collectionId); } RocksDBEntry RocksDBEntry::CrossReferenceIndex(uint64_t databaseId, uint64_t collectionId, uint64_t indexId) { return RocksDBEntry(RocksDBEntryType::CrossReference, RocksDBEntryType::Index, databaseId, collectionId, indexId); } RocksDBEntry RocksDBEntry::CrossReferenceView(uint64_t databaseId, uint64_t viewId) { return RocksDBEntry(RocksDBEntryType::CrossReference, RocksDBEntryType::View, databaseId, viewId); } RocksDBEntryType RocksDBEntry::type() const { return _type; } uint64_t RocksDBEntry::databaseId() const { switch (_type) { case RocksDBEntryType::Database: case RocksDBEntryType::CrossReference: { return *reinterpret_cast(_keyBuffer.data() + sizeof(char)); } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } uint64_t RocksDBEntry::collectionId() const { switch (_type) { case RocksDBEntryType::Collection: case RocksDBEntryType::Document: { return *reinterpret_cast(_keyBuffer.data() + sizeof(char)); } case RocksDBEntryType::CrossReference: { RocksDBEntryType subtype = *reinterpret_cast(_keyBuffer.data() + sizeof(char)); if ((subtype == RocksDBEntryType::Collection) || (subtype == RocksDBEntryType::Index)) { return *reinterpret_cast( _keyBuffer.data() + (2 * sizeof(char)) + sizeof(uint64_t)); } else { THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } uint64_t RocksDBEntry::indexId() const { switch (_type) { case RocksDBEntryType::Index: case RocksDBEntryType::IndexValue: case RocksDBEntryType::UniqueIndexValue: { return *reinterpret_cast(_keyBuffer.data() + sizeof(char)); } case RocksDBEntryType::CrossReference: { RocksDBEntryType subtype = *reinterpret_cast(_keyBuffer.data() + sizeof(char)); if (subtype == RocksDBEntryType::Index) { return *reinterpret_cast( _keyBuffer.data() + (2 * sizeof(char)) + (2 * sizeof(uint64_t))); } else { THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } uint64_t RocksDBEntry::viewId() const { switch (_type) { case RocksDBEntryType::View: { return *reinterpret_cast(_keyBuffer.data() + sizeof(char)); } case RocksDBEntryType::CrossReference: { RocksDBEntryType subtype = *reinterpret_cast(_keyBuffer.data() + sizeof(char)); if (subtype == RocksDBEntryType::View) { return *reinterpret_cast( _keyBuffer.data() + (2 * sizeof(char)) + sizeof(uint64_t)); } else { THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } uint64_t RocksDBEntry::revisionId() const { switch (_type) { case RocksDBEntryType::Document: { return *reinterpret_cast( _keyBuffer.data() + sizeof(char) + sizeof(uint64_t)); } case RocksDBEntryType::IndexValue: { return *reinterpret_cast( _keyBuffer.data() + (_keyBuffer.size() - sizeof(uint64_t))); } case RocksDBEntryType::UniqueIndexValue: { return *reinterpret_cast(_valueBuffer.data()); } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } VPackSlice RocksDBEntry::indexedValues() const { switch (_type) { case RocksDBEntryType::IndexValue: case RocksDBEntryType::UniqueIndexValue: { return VPackSlice(*reinterpret_cast( _keyBuffer.data() + sizeof(char) + sizeof(uint64_t))); } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } VPackSlice RocksDBEntry::data() const { switch (_type) { case RocksDBEntryType::Database: case RocksDBEntryType::Collection: case RocksDBEntryType::Index: case RocksDBEntryType::Document: case RocksDBEntryType::View: { return VPackSlice( *reinterpret_cast(_valueBuffer.data())); } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_TYPE_ERROR); } } std::string const& RocksDBEntry::key() const { return _keyBuffer; } std::string const& RocksDBEntry::value() const { return _valueBuffer; } std::string& RocksDBEntry::valueBuffer() { return _valueBuffer; } RocksDBEntry::RocksDBEntry(RocksDBEntryType type, RocksDBEntryType subtype, uint64_t first, uint64_t second, uint64_t third) : _type(type), _keyBuffer(), _valueBuffer() { TRI_ASSERT(_type == RocksDBEntryType::CrossReference); switch (subtype) { case RocksDBEntryType::Collection: case RocksDBEntryType::View: { size_t length = (2 * sizeof(char)) + (2 * sizeof(uint64_t)); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); _keyBuffer.push_back(static_cast(subtype)); uint64ToPersistent(_keyBuffer, first); uint64ToPersistent(_keyBuffer, second); break; } case RocksDBEntryType::Index: { size_t length = (2 * sizeof(char)) + (3 * sizeof(uint64_t)); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); _keyBuffer.push_back(static_cast(subtype)); uint64ToPersistent(_keyBuffer, first); uint64ToPersistent(_keyBuffer, second); uint64ToPersistent(_keyBuffer, third); break; } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER); } } RocksDBEntry::RocksDBEntry(RocksDBEntryType type, uint64_t first, uint64_t second, VPackSlice const& slice) : _type(type), _keyBuffer(), _valueBuffer() { TRI_ASSERT(_type != RocksDBEntryType::CrossReference); switch (_type) { case RocksDBEntryType::Database: case RocksDBEntryType::Collection: case RocksDBEntryType::Index: case RocksDBEntryType::View: { size_t length = sizeof(char) + sizeof(uint64_t); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); uint64ToPersistent(_keyBuffer, first); _valueBuffer.reserve(static_cast(slice.byteSize())); _valueBuffer.append(reinterpret_cast(slice.begin()), static_cast(slice.byteSize())); break; } case RocksDBEntryType::Document: { size_t length = sizeof(char) + (2 * sizeof(uint64_t)); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); uint64ToPersistent(_keyBuffer, first); uint64ToPersistent(_keyBuffer, second); _valueBuffer.reserve(static_cast(slice.byteSize())); _valueBuffer.append(reinterpret_cast(slice.begin()), static_cast(slice.byteSize())); break; } case RocksDBEntryType::IndexValue: { size_t length = sizeof(char) + static_cast(slice.byteSize()) + (2 * sizeof(uint64_t)); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); uint64ToPersistent(_keyBuffer, first); _keyBuffer.append(reinterpret_cast(slice.begin()), static_cast(slice.byteSize())); _keyBuffer.append(std::to_string(second)); break; } case RocksDBEntryType::UniqueIndexValue: { size_t length = sizeof(char) + static_cast(slice.byteSize()) + sizeof(uint64_t); _keyBuffer.reserve(length); _keyBuffer.push_back(static_cast(_type)); uint64ToPersistent(_keyBuffer, first); _keyBuffer.append(reinterpret_cast(slice.begin()), static_cast(slice.byteSize())); _valueBuffer.reserve(sizeof(uint64_t)); _valueBuffer.append(reinterpret_cast(&second), sizeof(uint64_t)); break; } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER); } } bool RocksDBEntry::isSameDatabase(RocksDBEntryType type, TRI_voc_tick_t id, rocksdb::Slice const& slice) { switch (type) { case RocksDBEntryType::Collection: case RocksDBEntryType::View: { TRI_ASSERT(slice.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); return id == uint64FromPersistent(slice.data() + sizeof(char)); } default: THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER); } } uint64_t RocksDBEntry::uint64FromPersistent(char const* p) { uint64_t value = 0; uint64_t x = 0; char const* end = p + sizeof(uint64_t); do { value += static_cast(*p++) << x; x += 8; } while (p < end); return value; } void RocksDBEntry::uint64ToPersistent(char* p, uint64_t value) { char* end = p + sizeof(uint64_t); do { *p++ = static_cast(value & 0xff); value >>= 8; } while (p < end); } void RocksDBEntry::uint64ToPersistent(std::string& p, uint64_t value) { size_t len = 0; do { p.push_back(static_cast(value & 0xff)); value >>= 8; } while (++len < sizeof(uint64_t)); }