//////////////////////////////////////////////////////////////////////////////// /// 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 Dr. Frank Celler /// @author Martin Schoenert //////////////////////////////////////////////////////////////////////////////// #include "VocShaper.h" #include "Basics/Exceptions.h" #include "Basics/fasthash.h" #include "Basics/Mutex.h" #include "Basics/MutexLocker.h" #include "Basics/ReadLocker.h" #include "Basics/ReadWriteLock.h" #include "Basics/WriteLocker.h" #include "Basics/associative.h" #include "Basics/hashes.h" #include "Basics/Logger.h" #include "Basics/tri-strings.h" #include "Basics/Utf8Helper.h" #include "VocBase/document-collection.h" #include "Wal/LogfileManager.h" //////////////////////////////////////////////////////////////////////////////// /// @brief extracts an attribute id from a marker //////////////////////////////////////////////////////////////////////////////// static inline TRI_shape_aid_t GetAttributeId(void const* marker) { TRI_df_marker_t const* p = static_cast(marker); if (p != nullptr) { if (p->_type == TRI_DF_MARKER_ATTRIBUTE) { return reinterpret_cast(p)->_aid; } if (p->_type == TRI_WAL_MARKER_ATTRIBUTE) { return reinterpret_cast(p) ->_attributeId; } } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief extracts an attribute name from a marker //////////////////////////////////////////////////////////////////////////////// static inline char const* GetAttributeName(void const* marker) { TRI_df_marker_t const* p = static_cast(marker); if (p != nullptr) { if (p->_type == TRI_DF_MARKER_ATTRIBUTE) { return reinterpret_cast(p) + sizeof(TRI_df_attribute_marker_t); } if (p->_type == TRI_WAL_MARKER_ATTRIBUTE) { return reinterpret_cast(p) + sizeof(arangodb::wal::attribute_marker_t); } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute name of a key //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyAttributeName(TRI_associative_pointer_t*, void const* key) { return TRI_FnvHashString((char const*)key); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute name of an element //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAttributeName(TRI_associative_pointer_t*, void const* element) { return TRI_FnvHashString(GetAttributeName(element)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute name and an attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyAttributeName(TRI_associative_pointer_t*, void const* key, void const* element) { return TRI_EqualString((char const*)key, GetAttributeName(element)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the attribute id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyAttributeId(TRI_associative_pointer_t*, void const* key) { TRI_shape_aid_t const* k = static_cast(key); return TRI_FnvHashPointer(k, sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the attribute //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAttributeId(TRI_associative_pointer_t*, void const* element) { TRI_shape_aid_t aid = GetAttributeId(element); return TRI_FnvHashPointer(&aid, sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute name and an attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyAttributeId(TRI_associative_pointer_t*, void const* key, void const* element) { TRI_shape_aid_t const* k = static_cast(key); TRI_shape_aid_t aid = GetAttributeId(element); return *k == aid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shapes //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementShape(TRI_associative_pointer_t*, void const* element) { auto shape = static_cast(element); TRI_ASSERT(shape != nullptr); char const* s = reinterpret_cast(shape); return TRI_FnvHashPointer( s + sizeof(TRI_shape_sid_t), static_cast(shape->_size - sizeof(TRI_shape_sid_t))); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares shapes //////////////////////////////////////////////////////////////////////////////// static bool EqualElementShape(TRI_associative_pointer_t*, void const* left, void const* right) { auto l = static_cast(left); auto r = static_cast(right); char const* ll = reinterpret_cast(l); char const* rr = reinterpret_cast(r); return (l->_size == r->_size) && memcmp(ll + sizeof(TRI_shape_sid_t), rr + sizeof(TRI_shape_sid_t), static_cast(l->_size) - sizeof(TRI_shape_sid_t)) == 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shape id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyShapeId(TRI_associative_pointer_t*, void const* key) { auto k = static_cast(key); return TRI_FnvHashPointer(k, sizeof(TRI_shape_sid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the shape //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementShapeId(TRI_associative_pointer_t*, void const* element) { auto shape = static_cast(element); TRI_ASSERT(shape != nullptr); return TRI_FnvHashPointer(&shape->_sid, sizeof(TRI_shape_sid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a shape id and a shape //////////////////////////////////////////////////////////////////////////////// static bool EqualKeyShapeId(TRI_associative_pointer_t*, void const* key, void const* element) { auto k = static_cast(key); auto shape = static_cast(element); TRI_ASSERT(shape != nullptr); return *k == shape->_sid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the accessor //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementAccessor(TRI_associative_pointer_t*, void const* element) { auto ee = static_cast(element); uint64_t v[2]; v[0] = ee->_sid; v[1] = ee->_pid; return TRI_FnvHashPointer(v, sizeof(v)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an accessor //////////////////////////////////////////////////////////////////////////////// static bool EqualElementAccessor(TRI_associative_pointer_t*, void const* left, void const* right) { auto l = static_cast(left); auto r = static_cast(right); return l->_sid == r->_sid && l->_pid == r->_pid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the attribute path identifier //////////////////////////////////////////////////////////////////////////////// static uint64_t HashPidKeyAttributePath(TRI_associative_pointer_t*, void const* key) { return TRI_FnvHashPointer(key, sizeof(TRI_shape_pid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute path //////////////////////////////////////////////////////////////////////////////// static uint64_t HashPidElementAttributePath(TRI_associative_pointer_t*, void const* element) { auto e = static_cast(element); return TRI_FnvHashPointer(&e->_pid, sizeof(TRI_shape_pid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute path identifier and an attribute path //////////////////////////////////////////////////////////////////////////////// static bool EqualPidKeyAttributePath(TRI_associative_pointer_t*, void const* key, void const* element) { auto k = static_cast(key); auto e = static_cast(element); return *k == e->_pid; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute path name //////////////////////////////////////////////////////////////////////////////// static uint64_t HashNameKeyAttributePath(TRI_associative_pointer_t*, void const* key) { return TRI_FnvHashString(static_cast(key)); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashs the attribute path //////////////////////////////////////////////////////////////////////////////// static uint64_t HashNameElementAttributePath(TRI_associative_pointer_t*, void const* element) { char const* e = static_cast(element); TRI_shape_path_t const* ee = static_cast(element); return TRI_FnvHashPointer( e + sizeof(TRI_shape_path_t) + ee->_aidLength * sizeof(TRI_shape_aid_t), ee->_nameLength - 1); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares an attribute name and an attribute //////////////////////////////////////////////////////////////////////////////// static bool EqualNameKeyAttributePath(TRI_associative_pointer_t*, void const* key, void const* element) { char const* k = static_cast(key); char const* e = static_cast(element); TRI_shape_path_t const* ee = static_cast(element); return TRI_EqualString(k, e + sizeof(TRI_shape_path_t) + ee->_aidLength * sizeof(TRI_shape_aid_t)); } //////////////////////////////////////////////////////////////////////////////// /// @brief create a shaper //////////////////////////////////////////////////////////////////////////////// VocShaper::VocShaper(TRI_memory_zone_t* memoryZone, TRI_document_collection_t* document) : Shaper(), _memoryZone(memoryZone), _collection(document), _nextPid(1), _nextAid(1), // id of next attribute to hand out _nextSid(Shaper::firstCustomShapeId()) { // id of next shape to hand out TRI_InitAssociativePointer(&_attributeNames, TRI_UNKNOWN_MEM_ZONE, HashKeyAttributeName, HashElementAttributeName, EqualKeyAttributeName, 0); TRI_InitAssociativePointer(&_attributeIds, TRI_UNKNOWN_MEM_ZONE, HashKeyAttributeId, HashElementAttributeId, EqualKeyAttributeId, 0); TRI_InitAssociativePointer(&_shapeDictionary, TRI_UNKNOWN_MEM_ZONE, 0, HashElementShape, 0, EqualElementShape); TRI_InitAssociativePointer(&_shapeIds, TRI_UNKNOWN_MEM_ZONE, HashKeyShapeId, HashElementShapeId, EqualKeyShapeId, 0); for (size_t i = 0; i < NUM_SHAPE_ACCESSORS; ++i) { TRI_InitAssociativePointer(&_accessors[i], TRI_UNKNOWN_MEM_ZONE, 0, HashElementAccessor, 0, EqualElementAccessor); } TRI_InitAssociativePointer( &_attributePathsByName, _memoryZone, HashNameKeyAttributePath, HashNameElementAttributePath, EqualNameKeyAttributePath, 0); TRI_InitAssociativePointer( &_attributePathsByPid, _memoryZone, HashPidKeyAttributePath, HashPidElementAttributePath, EqualPidKeyAttributePath, 0); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroy a shaper //////////////////////////////////////////////////////////////////////////////// VocShaper::~VocShaper() { size_t const n = _attributePathsByName._nrAlloc; // only free pointers in attributePathsByName // (attributePathsByPid contains the same pointers!) for (size_t i = 0; i < n; ++i) { void* data = _attributePathsByName._table[i]; if (data != nullptr) { TRI_Free(_memoryZone, data); } } TRI_DestroyAssociativePointer(&_attributePathsByName); TRI_DestroyAssociativePointer(&_attributePathsByPid); TRI_DestroyAssociativePointer(&_attributeNames); TRI_DestroyAssociativePointer(&_attributeIds); TRI_DestroyAssociativePointer(&_shapeDictionary); TRI_DestroyAssociativePointer(&_shapeIds); for (size_t i = 0; i < NUM_SHAPE_ACCESSORS; ++i) { for (size_t j = 0; j < _accessors[i]._nrAlloc; ++j) { auto accessor = static_cast(_accessors[i]._table[j]); if (accessor != nullptr) { TRI_FreeShapeAccessor(accessor); } } TRI_DestroyAssociativePointer(&_accessors[i]); } } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a shape by identifier //////////////////////////////////////////////////////////////////////////////// TRI_shape_t const* VocShaper::lookupShapeId(TRI_shape_sid_t sid) { TRI_shape_t const* shape = Shaper::lookupSidBasicShape(sid); if (shape == nullptr) { READ_LOCKER(readLocker, _shapeIdsLock); shape = static_cast( TRI_LookupByKeyAssociativePointer(&_shapeIds, &sid)); } return shape; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute name by identifier //////////////////////////////////////////////////////////////////////////////// char const* VocShaper::lookupAttributeId(TRI_shape_aid_t aid) { { READ_LOCKER(readLocker, _attributeIdsLock); auto element = static_cast( TRI_LookupByKeyAssociativePointer(&_attributeIds, &aid)); if (element != nullptr) { return GetAttributeName(element); } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute path by identifier //////////////////////////////////////////////////////////////////////////////// TRI_shape_path_t const* VocShaper::lookupAttributePathByPid( TRI_shape_pid_t pid) { READ_LOCKER(readLocker, _attributePathsByPidLock); return static_cast( TRI_LookupByKeyAssociativePointer(&_attributePathsByPid, &pid)); } //////////////////////////////////////////////////////////////////////////////// /// @brief finds an attribute path by identifier //////////////////////////////////////////////////////////////////////////////// TRI_shape_pid_t VocShaper::findOrCreateAttributePathByName(char const* name) { TRI_shape_path_t const* path = findShapePathByName(name, true); return path == nullptr ? 0 : path->_pid; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute path by identifier //////////////////////////////////////////////////////////////////////////////// TRI_shape_pid_t VocShaper::lookupAttributePathByName(char const* name) { TRI_shape_path_t const* path = findShapePathByName(name, false); return path == nullptr ? 0 : path->_pid; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the attribute name for an attribute path //////////////////////////////////////////////////////////////////////////////// char const* VocShaper::attributeNameShapePid(TRI_shape_pid_t pid) { TRI_shape_path_t const* path = lookupAttributePathByPid(pid); if (path == nullptr) { return nullptr; } char const* e = (char const*)path; return e + sizeof(TRI_shape_path_t) + path->_aidLength * sizeof(TRI_shape_aid_t); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an attribute identifier by name //////////////////////////////////////////////////////////////////////////////// TRI_shape_aid_t VocShaper::lookupAttributeByName(char const* name) { TRI_ASSERT(name != nullptr); { READ_LOCKER(readLocker, _attributeNamesLock); auto element = static_cast( TRI_LookupByKeyAssociativePointer(&_attributeNames, name)); if (element != nullptr) { return GetAttributeId(element); } } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds or creates an attribute identifier by name //////////////////////////////////////////////////////////////////////////////// TRI_shape_aid_t VocShaper::findOrCreateAttributeByName(char const* name) { // check if the attribute exists TRI_shape_aid_t aid = lookupAttributeByName(name); if (aid != 0) { // yes return aid; } // increase attribute id value aid = _nextAid++; int res = TRI_ERROR_NO_ERROR; TRI_document_collection_t* document = _collection; try { arangodb::wal::AttributeMarker marker( document->_vocbase->_id, document->_info.id(), aid, std::string(name)); // lock the index and check that the element is still missing { MUTEX_LOCKER(mutexLocker, _attributeCreateLock); void const* p; { READ_LOCKER(readLocker, _attributeNamesLock); p = TRI_LookupByKeyAssociativePointer(&_attributeNames, name); } // if the element appeared, return the aid if (p != nullptr) { return GetAttributeId(p); } TRI_IF_FAILURE("ShaperWriteAttributeMarker") { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } { // make room for one more element WRITE_LOCKER(writeLocker, _attributeIdsLock); if (!TRI_ReserveAssociativePointer(&_attributeIds, 1)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } } { // make room for one more element WRITE_LOCKER(writeLocker, _attributeNamesLock); if (!TRI_ReserveAssociativePointer(&_attributeNames, 1)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } } // write marker into wal arangodb::wal::SlotInfoCopy slotInfo = arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker, false); if (slotInfo.errorCode != TRI_ERROR_NO_ERROR) { // throw an exception which is caught at the end of this function THROW_ARANGO_EXCEPTION(slotInfo.errorCode); } void* TRI_UNUSED f; { WRITE_LOCKER(writeLocker, _attributeIdsLock); f = TRI_InsertKeyAssociativePointer( &_attributeIds, &aid, const_cast(slotInfo.mem), false); } TRI_ASSERT(f == nullptr); // enter into the dictionaries { WRITE_LOCKER(writeLocker, _attributeNamesLock); f = TRI_InsertKeyAssociativePointer( &_attributeNames, name, const_cast(slotInfo.mem), false); } TRI_ASSERT(f == nullptr); } return aid; } catch (arangodb::basics::Exception const& ex) { res = ex.code(); } catch (...) { res = TRI_ERROR_INTERNAL; } LOG(WARN) << "could not save attribute marker in log: " << TRI_errno_string(res); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds a shape /// if the function returns non-nullptr, the return value is a pointer to an /// already existing shape and the value must not be freed /// if the function returns nullptr, it has not found the shape and was not able /// to create it. The value must then be freed by the caller //////////////////////////////////////////////////////////////////////////////// TRI_shape_t const* VocShaper::findShape(TRI_shape_t* shape, bool create) { TRI_shape_t const* found = Shaper::lookupBasicShape(shape); if (found == nullptr) { READ_LOCKER(readLocker, _shapeDictionaryLock); found = static_cast( TRI_LookupByElementAssociativePointer(&_shapeDictionary, shape)); } // shape found, free argument and return if (found != nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); return found; } // not found if (!create) { return nullptr; } // get next shape id TRI_shape_sid_t const sid = _nextSid++; shape->_sid = sid; TRI_document_collection_t* document = _collection; int res = TRI_ERROR_NO_ERROR; try { arangodb::wal::ShapeMarker marker(document->_vocbase->_id, document->_info.id(), shape); // lock the index and check the element is still missing MUTEX_LOCKER(mutexLocker, _shapeCreateLock); { READ_LOCKER(readLocker, _shapeDictionaryLock); found = static_cast( TRI_LookupByElementAssociativePointer(&_shapeDictionary, shape)); } if (found != nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); return found; } TRI_IF_FAILURE("ShaperWriteShapeMarker") { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } { // make room for one more element WRITE_LOCKER(writeLocker, _shapeIdsLock); if (!TRI_ReserveAssociativePointer(&_shapeIds, 1)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } } { // make room for one more element WRITE_LOCKER(writeLocker, _shapeDictionaryLock); if (!TRI_ReserveAssociativePointer(&_shapeDictionary, 1)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } } // write marker into wal arangodb::wal::SlotInfoCopy slotInfo = arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker, false); if (slotInfo.errorCode != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(slotInfo.errorCode); } char const* m = static_cast(slotInfo.mem) + sizeof(arangodb::wal::shape_marker_t); TRI_ASSERT(m != nullptr); TRI_shape_t const* result = reinterpret_cast(m); { WRITE_LOCKER(writeLocker, _shapeIdsLock); void* f = TRI_InsertKeyAssociativePointer(&_shapeIds, &sid, (void*)m, false); if (f != nullptr) { LOG(ERR) << "logic error when inserting shape into id dictionary"; } TRI_ASSERT(f == nullptr); // will abort here } { WRITE_LOCKER(writeLocker, _shapeDictionaryLock); void* f = TRI_InsertElementAssociativePointer(&_shapeDictionary, (void*)m, false); if (f != nullptr) { LOG(ERR) << "logic error when inserting shape into dictionary"; } TRI_ASSERT(f == nullptr); // will abort here } TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape); return result; } catch (arangodb::basics::Exception const& ex) { res = ex.code(); } catch (...) { res = TRI_ERROR_INTERNAL; } LOG(WARN) << "could not save shape marker in log: " << TRI_errno_string(res); // must not free the shape here, as the caller is going to free it... return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief move a shape marker, called during compaction //////////////////////////////////////////////////////////////////////////////// int VocShaper::moveMarker(TRI_df_marker_t* marker, void* expectedOldPosition) { if (marker->_type == TRI_DF_MARKER_SHAPE) { char* p = ((char*)marker) + sizeof(TRI_df_shape_marker_t); TRI_shape_t* l = (TRI_shape_t*)p; MUTEX_LOCKER(mutexLocker, _shapeCreateLock); if (expectedOldPosition != nullptr) { char* old = static_cast(expectedOldPosition); void const* found; { READ_LOCKER(readLocker, _shapeIdsLock); found = TRI_LookupByKeyAssociativePointer(&_shapeIds, &l->_sid); } if (found != nullptr) { if (old + sizeof(TRI_df_shape_marker_t) != found && old + sizeof(arangodb::wal::shape_marker_t) != found) { LOG(TRACE) << "got unexpected shape position"; // do not insert if position doesn't match the expectation // this is done to ensure that the WAL collector doesn't insert a // shape pointer // that has already been garbage collected by the compactor thread return TRI_ERROR_NO_ERROR; } } } // remove the old marker // and re-insert the marker with the new pointer void* f; { WRITE_LOCKER(writeLocker, _shapeIdsLock); f = TRI_InsertKeyAssociativePointer(&_shapeIds, &l->_sid, l, true); } // note: this assertion is wrong if the recovery collects the shape in the // WAL and it has not been transferred // into the collection datafile yet // TRI_ASSERT(f != nullptr); if (f != nullptr) { LOG(TRACE) << "shape already existed in shape ids array"; } // same for the shape dictionary // delete and re-insert { WRITE_LOCKER(writeLocker, _shapeDictionaryLock); f = TRI_InsertElementAssociativePointer(&_shapeDictionary, l, true); } // note: this assertion is wrong if the recovery collects the shape in the // WAL and it has not been transferred // into the collection datafile yet // TRI_ASSERT(f != nullptr); if (f != nullptr) { LOG(TRACE) << "shape already existed in shape dictionary"; } } else if (marker->_type == TRI_DF_MARKER_ATTRIBUTE) { TRI_df_attribute_marker_t* m = (TRI_df_attribute_marker_t*)marker; char* p = ((char*)m) + sizeof(TRI_df_attribute_marker_t); MUTEX_LOCKER(mutexLocker, _attributeCreateLock); if (expectedOldPosition != nullptr) { void const* found; { READ_LOCKER(readLocker, _attributeNamesLock); found = TRI_LookupByKeyAssociativePointer(&_attributeNames, p); } if (found != nullptr && found != expectedOldPosition) { // do not insert if position doesn't match the expectation // this is done to ensure that the WAL collector doesn't insert a shape // pointer // that has already been garbage collected by the compactor thread LOG(TRACE) << "got unexpected attribute position"; return TRI_ERROR_NO_ERROR; } } // remove attribute by name (p points to new location of name, but names // are identical in old and new marker) // and re-insert same attribute with adjusted pointer void* f; { WRITE_LOCKER(writeLocker, _attributeNamesLock); f = TRI_InsertKeyAssociativePointer(&_attributeNames, p, m, true); } // note: this assertion is wrong if the recovery collects the attribute in // the WAL and it has not been transferred // into the collection datafile yet // TRI_ASSERT(f != nullptr); if (f != nullptr) { LOG(TRACE) << "attribute already existed in attribute names dictionary"; } // same for attribute ids // delete and re-insert same attribute with adjusted pointer { WRITE_LOCKER(writeLocker, _attributeIdsLock); f = TRI_InsertKeyAssociativePointer(&_attributeIds, &m->_aid, m, true); } // note: this assertion is wrong if the recovery collects the attribute in // the WAL and it has not been transferred // into the collection datafile yet // TRI_ASSERT(f != nullptr); if (f != nullptr) { LOG(TRACE) << "attribute already existed in attribute ids dictionary"; } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief insert a shape, called when opening a collection //////////////////////////////////////////////////////////////////////////////// int VocShaper::insertShape(TRI_df_marker_t const* marker, bool warnIfDuplicate) { char const* p = reinterpret_cast(marker); if (marker->_type == TRI_DF_MARKER_SHAPE) { p += sizeof(TRI_df_shape_marker_t); } else if (marker->_type == TRI_WAL_MARKER_SHAPE) { p += sizeof(arangodb::wal::shape_marker_t); } else { return TRI_ERROR_INTERNAL; } TRI_shape_t* l = (TRI_shape_t*)p; LOG(TRACE) << "found shape " << l->_sid; MUTEX_LOCKER(mutexLocker, _shapeCreateLock); void* f; { WRITE_LOCKER(writeLocker, _shapeDictionaryLock); f = TRI_InsertElementAssociativePointer(&_shapeDictionary, l, false); } if (warnIfDuplicate && f != nullptr) { std::string name = _collection->_info.name(); bool const isIdentical = EqualElementShape(nullptr, f, l); if (isIdentical) { // duplicate shape, but with identical content. simply ignore it LOG(TRACE) << "found duplicate shape markers for id " << l->_sid << " in collection '" << name.c_str() << "' in shape dictionary"; } else { LOG(ERR) << "found heterogenous shape markers for id " << l->_sid << " in collection '" << name.c_str() << "' in shape dictionary"; #ifdef TRI_ENABLE_MAINTAINER_MODE TRI_ASSERT(false); #endif } } { WRITE_LOCKER(writeLocker, _shapeIdsLock); f = TRI_InsertKeyAssociativePointer(&_shapeIds, &l->_sid, l, false); } if (warnIfDuplicate && f != nullptr) { std::string name = _collection->_info.name(); bool const isIdentical = EqualElementShape(nullptr, f, l); if (isIdentical) { // duplicate shape, but with identical content. simply ignore it LOG(TRACE) << "found duplicate shape markers for id " << l->_sid << " in collection '" << name.c_str() << "' in shape ids table"; } else { LOG(ERR) << "found heterogenous shape markers for id " << l->_sid << " in collection '" << name.c_str() << "' in shape ids table"; #ifdef TRI_ENABLE_MAINTAINER_MODE TRI_ASSERT(false); #endif } } // no lock is necessary here as we are the only users of the shaper at this // time (opening the collection) if (_nextSid <= l->_sid) { _nextSid = l->_sid + 1; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief insert an attribute, called when opening a collection //////////////////////////////////////////////////////////////////////////////// int VocShaper::insertAttribute(TRI_df_marker_t const* marker, bool warnIfDuplicate) { char* name = nullptr; TRI_shape_aid_t aid = 0; if (marker->_type == TRI_DF_MARKER_ATTRIBUTE) { name = ((char*)marker) + sizeof(TRI_df_attribute_marker_t); aid = reinterpret_cast(marker)->_aid; } else if (marker->_type == TRI_WAL_MARKER_ATTRIBUTE) { name = ((char*)marker) + sizeof(arangodb::wal::attribute_marker_t); aid = reinterpret_cast(marker) ->_attributeId; } else { return TRI_ERROR_INTERNAL; } TRI_ASSERT(aid != 0); LOG(TRACE) << "found attribute '" << name << "', aid: " << aid; // remove an existing temporary attribute if present MUTEX_LOCKER(mutexLocker, _attributeCreateLock); void* found; { WRITE_LOCKER(writeLocker, _attributeNamesLock); found = TRI_InsertKeyAssociativePointer(&_attributeNames, name, (void*)marker, false); } if (warnIfDuplicate && found != nullptr) { std::string cname = _collection->_info.name(); bool const isIdentical = (TRI_EqualString(name, GetAttributeName(found)) && aid == GetAttributeId(found)); if (isIdentical) { // duplicate attribute, but with identical content. simply ignore it LOG(TRACE) << "found duplicate attribute name '" << name << "' in collection '" << cname.c_str() << "'"; } else { LOG(ERR) << "found heterogenous attribute name '" << name << "' in collection '" << cname.c_str() << "'"; } } { WRITE_LOCKER(writeLocker, _attributeIdsLock); found = TRI_InsertKeyAssociativePointer(&_attributeIds, &aid, (void*)marker, false); } if (warnIfDuplicate && found != nullptr) { std::string cname = _collection->_info.name(); bool const isIdentical = TRI_EqualString(name, GetAttributeName(found)); if (isIdentical) { // duplicate attribute, but with identical content. simply ignore it LOG(TRACE) << "found duplicate attribute id '" << aid << "' in collection '" << cname.c_str() << "'"; } else { LOG(ERR) << "found heterogenous attribute id '" << aid << "' in collection '" << cname.c_str() << "'"; } } // no lock is necessary here as we are the only users of the shaper at this // time (opening the collection) if (_nextAid <= aid) { _nextAid = aid + 1; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds an accessor //////////////////////////////////////////////////////////////////////////////// TRI_shape_access_t const* VocShaper::findAccessor(TRI_shape_sid_t sid, TRI_shape_pid_t pid) { TRI_shape_access_t search = {sid, pid, 0, nullptr}; size_t const i = static_cast( fasthash64(&sid, sizeof(TRI_shape_sid_t), fasthash64(&pid, sizeof(TRI_shape_pid_t), 0x87654321)) % NUM_SHAPE_ACCESSORS); TRI_shape_access_t const* found = nullptr; { READ_LOCKER(readLocker, _accessorLock[i]); found = static_cast( TRI_LookupByElementAssociativePointer(&_accessors[i], &search)); if (found != nullptr) { return found; } } // not found... time for us to create the accessor ourselves! TRI_shape_access_t* accessor = TRI_ShapeAccessor(this, sid, pid); // TRI_ShapeAccessor can return a NULL pointer if (accessor == nullptr) { return nullptr; } // acquire the write-lock and try to insert our own accessor { WRITE_LOCKER(writeLocker, _accessorLock[i]); found = static_cast( TRI_InsertElementAssociativePointer( &_accessors[i], const_cast(static_cast(accessor)), false)); } if (found != nullptr) { // someone else inserted the same accessor in the period after we release // the read-lock // but before we acquired the write-lock // this is ok, and we can return the concurrently built accessor now TRI_FreeShapeAccessor(accessor); return found; } return const_cast(accessor); } //////////////////////////////////////////////////////////////////////////////// /// @brief extracts a sub-shape //////////////////////////////////////////////////////////////////////////////// bool VocShaper::extractShapedJson(TRI_shaped_json_t const* document, TRI_shape_sid_t sid, TRI_shape_pid_t pid, TRI_shaped_json_t* result, TRI_shape_t const** shape) { TRI_shape_access_t const* accessor = findAccessor(document->_sid, pid); if (accessor == nullptr) { #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(TRACE) << "failed to get accessor for sid " << document->_sid << " and path " << pid; #endif return false; } if (accessor->_resultSid == TRI_SHAPE_ILLEGAL) { #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(TRACE) << "expecting any object for path " << pid << ", got nothing"; #endif *shape = nullptr; return sid == TRI_SHAPE_ILLEGAL; } *shape = lookupShapeId(accessor->_resultSid); if (*shape == nullptr) { #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(TRACE) << "expecting any object for path " << pid << ", got unknown shape id " << accessor->_resultSid; #endif *shape = nullptr; return sid == TRI_SHAPE_ILLEGAL; } if (sid != 0 && sid != accessor->_resultSid) { #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(TRACE) << "expecting sid " << sid << " for path " << pid << ", got sid " << accessor->_resultSid; #endif return false; } bool ok = TRI_ExecuteShapeAccessor(accessor, document, result); if (!ok) { #ifdef TRI_ENABLE_MAINTAINER_MODE LOG(TRACE) << "failed to get accessor for sid " << document->_sid << " and path " << pid; #endif return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a shape path by identifier //////////////////////////////////////////////////////////////////////////////// TRI_shape_path_t const* VocShaper::findShapePathByName(char const* name, bool create) { char* buffer; char* end; char* prev; char* ptr; TRI_ASSERT(name != nullptr); void const* p; { READ_LOCKER(readLocker, _attributePathsByNameLock); p = TRI_LookupByKeyAssociativePointer(&_attributePathsByName, name); } if (p != nullptr) { return (TRI_shape_path_t const*)p; } // create an attribute path size_t len = strlen(name); // lock the index and check that the element is still missing MUTEX_LOCKER(mutexLocker, _attributePathsCreateLock); // if the element appeared, return the pid { READ_LOCKER(readLocker, _attributePathsByNameLock); p = TRI_LookupByKeyAssociativePointer(&_attributePathsByName, name); } if (p != nullptr) { return (TRI_shape_path_t const*)p; } // split path into attribute pieces size_t count = 0; TRI_shape_aid_t* aids = static_cast( TRI_Allocate(_memoryZone, len * sizeof(TRI_shape_aid_t), false)); if (aids == nullptr) { LOG(ERR) << "out of memory in shaper"; return nullptr; } buffer = ptr = TRI_DuplicateString(_memoryZone, name, len); if (buffer == nullptr) { TRI_Free(_memoryZone, aids); LOG(ERR) << "out of memory in shaper"; return nullptr; } end = buffer + len + 1; prev = buffer; for (; ptr < end; ++ptr) { if (*ptr == '.' || *ptr == '\0') { *ptr = '\0'; if (ptr != prev) { if (create) { aids[count++] = findOrCreateAttributeByName(prev); } else { aids[count] = lookupAttributeByName(prev); if (aids[count] == 0) { TRI_FreeString(_memoryZone, buffer); TRI_Free(_memoryZone, aids); return nullptr; } ++count; } } prev = ptr + 1; } } TRI_FreeString(_memoryZone, buffer); // create element size_t total = sizeof(TRI_shape_path_t) + (len + 1) + (count * sizeof(TRI_shape_aid_t)); TRI_shape_path_t* result = static_cast(TRI_Allocate(_memoryZone, total, false)); if (result == nullptr) { TRI_Free(_memoryZone, aids); LOG(ERR) << "out of memory in shaper"; return nullptr; } result->_pid = _nextPid++; result->_nameLength = (uint32_t)len + 1; result->_aidLength = count; memcpy(((char*)result) + sizeof(TRI_shape_path_t), aids, count * sizeof(TRI_shape_aid_t)); memcpy(((char*)result) + sizeof(TRI_shape_path_t) + count * sizeof(TRI_shape_aid_t), name, len + 1); TRI_Free(_memoryZone, aids); { // make room for one more element WRITE_LOCKER(writeLocker, _attributePathsByNameLock); if (!TRI_ReserveAssociativePointer(&_attributePathsByName, 1)) { TRI_Free(_memoryZone, result); return nullptr; } } { // make room for one more element WRITE_LOCKER(writeLocker, _attributePathsByPidLock); if (!TRI_ReserveAssociativePointer(&_attributePathsByPid, 1)) { TRI_Free(_memoryZone, result); return nullptr; } } { WRITE_LOCKER(writeLocker, _attributePathsByNameLock); void const* f = TRI_InsertKeyAssociativePointer(&_attributePathsByName, name, result, false); if (f != nullptr) { LOG(WARN) << "duplicate shape path " << result->_pid; } TRI_ASSERT(f == nullptr); // will abort here } { WRITE_LOCKER(writeLocker, _attributePathsByPidLock); void const* f = TRI_InsertKeyAssociativePointer( &_attributePathsByPid, &result->_pid, result, false); if (f != nullptr) { LOG(WARN) << "duplicate shape path " << result->_pid; } TRI_ASSERT(f == nullptr); // will abort here } // return pid return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief temporary structure for attributes //////////////////////////////////////////////////////////////////////////////// typedef struct attribute_entry_s { char* _attribute; TRI_shaped_json_t _value; } attribute_entry_t; //////////////////////////////////////////////////////////////////////////////// /// @brief sort attribure names //////////////////////////////////////////////////////////////////////////////// static int AttributeNameComparator(void const* lhs, void const* rhs) { auto l = static_cast(lhs); auto r = static_cast(rhs); if (l->_attribute == nullptr || r->_attribute == nullptr) { // error ! return -1; } return TRI_compare_utf8(l->_attribute, r->_attribute); } //////////////////////////////////////////////////////////////////////////////// /// @brief create a sorted vector of attributes //////////////////////////////////////////////////////////////////////////////// static int FillAttributesVector(TRI_vector_t* vector, TRI_shaped_json_t const* shapedJson, TRI_shape_t const* shape, VocShaper* shaper) { TRI_InitVector(vector, TRI_UNKNOWN_MEM_ZONE, sizeof(attribute_entry_t)); // ........................................................................... // Determine the number of fixed sized values // ........................................................................... char const* charShape = (char const*)shape; charShape = charShape + sizeof(TRI_shape_t); TRI_shape_size_t fixedEntries = *((TRI_shape_size_t*)(charShape)); // ........................................................................... // Determine the number of variable sized values // ........................................................................... charShape = charShape + sizeof(TRI_shape_size_t); TRI_shape_size_t variableEntries = *((TRI_shape_size_t*)(charShape)); // ........................................................................... // It may happen that the shaped_json_array is 'empty {}' // ........................................................................... if (fixedEntries + variableEntries == 0) { return TRI_ERROR_NO_ERROR; } // ........................................................................... // Determine the list of shape identifiers // ........................................................................... charShape = charShape + sizeof(TRI_shape_size_t); TRI_shape_sid_t const* sids = (TRI_shape_sid_t const*)charShape; charShape = charShape + (sizeof(TRI_shape_sid_t) * (fixedEntries + variableEntries)); TRI_shape_aid_t const* aids = (TRI_shape_aid_t const*)charShape; charShape = charShape + (sizeof(TRI_shape_aid_t) * (fixedEntries + variableEntries)); TRI_shape_size_t const* offsets = (TRI_shape_size_t const*)charShape; for (TRI_shape_size_t i = 0; i < fixedEntries; ++i) { char const* a = shaper->lookupAttributeId(aids[i]); if (a == nullptr) { return TRI_ERROR_INTERNAL; } char* copy = TRI_DuplicateString(TRI_UNKNOWN_MEM_ZONE, a); if (copy == nullptr) { return TRI_ERROR_OUT_OF_MEMORY; } attribute_entry_t attribute; attribute._attribute = copy; attribute._value._sid = sids[i]; attribute._value._data.data = shapedJson->_data.data + offsets[i]; attribute._value._data.length = (uint32_t)(offsets[i + 1] - offsets[i]); TRI_PushBackVector(vector, &attribute); } offsets = (TRI_shape_size_t const*)shapedJson->_data.data; for (TRI_shape_size_t i = 0; i < variableEntries; ++i) { char const* a = shaper->lookupAttributeId(aids[i + fixedEntries]); if (a == nullptr) { return TRI_ERROR_INTERNAL; } char* copy = TRI_DuplicateString(TRI_UNKNOWN_MEM_ZONE, a); if (copy == nullptr) { return TRI_ERROR_OUT_OF_MEMORY; } attribute_entry_t attribute; attribute._attribute = copy; attribute._value._sid = sids[i + fixedEntries]; attribute._value._data.data = shapedJson->_data.data + offsets[i]; attribute._value._data.length = (uint32_t)(offsets[i + 1] - offsets[i]); TRI_PushBackVector(vector, &attribute); } // sort the attributes by attribute name qsort(vector->_buffer, TRI_LengthVector(vector), sizeof(attribute_entry_t), AttributeNameComparator); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroy a vector of attributes //////////////////////////////////////////////////////////////////////////////// static void DestroyAttributesVector(TRI_vector_t* vector) { size_t const n = TRI_LengthVector(vector); for (size_t i = 0; i < n; ++i) { attribute_entry_t* entry = static_cast(TRI_AtVector(vector, i)); if (entry->_attribute != nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, entry->_attribute); } } TRI_DestroyVector(vector); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares two shapes /// /// You must either supply (leftDocument, leftObject) or leftShaped. /// You must either supply (rightDocument, rightObject) or rightShaped. //////////////////////////////////////////////////////////////////////////////// int TRI_CompareShapeTypes(char const* leftDocument, TRI_shaped_sub_t const* leftObject, TRI_shaped_json_t const* leftShaped, VocShaper* leftShaper, char const* rightDocument, TRI_shaped_sub_t const* rightObject, TRI_shaped_json_t const* rightShaped, VocShaper* rightShaper) { TRI_shape_t const* rightShape; TRI_shaped_json_t left; TRI_shaped_json_t leftElement; TRI_shaped_json_t right; TRI_shaped_json_t rightElement; // left is either a shaped json or a shaped sub object if (leftDocument != nullptr) { TRI_ASSERT(leftObject != nullptr); left._sid = leftObject->_sid; TRI_InspectShapedSub(leftObject, leftDocument, left); } else { left = *leftShaped; } // right is either a shaped json or a shaped sub object if (rightDocument != nullptr) { TRI_ASSERT(rightObject != nullptr); right._sid = rightObject->_sid; TRI_InspectShapedSub(rightObject, rightDocument, right); } else { right = *rightShaped; } // get left shape and type TRI_shape_t const* leftShape = leftShaper->lookupShapeId(left._sid); // get right shape and type if (leftShaper == rightShaper && left._sid == right._sid) { if (left._sid == BasicShapes::TRI_SHAPE_SID_ILLEGAL) { // Both sides have shape_sid illegal return 0; } // identical collection and shape rightShape = leftShape; } else { // different shapes rightShape = rightShaper->lookupShapeId(right._sid); } if (left._sid == BasicShapes::TRI_SHAPE_SID_ILLEGAL) { return -1; } if (right._sid == BasicShapes::TRI_SHAPE_SID_ILLEGAL) { return 1; } if (leftShape == nullptr || rightShape == nullptr) { LOG(ERR) << "shape not found"; TRI_ASSERT(false); return -1; } TRI_shape_type_t leftType = leftShape->_type; TRI_shape_type_t rightType = rightShape->_type; // ........................................................................... // check ALL combinations of leftType and rightType // ........................................................................... switch (leftType) { // ......................................................................... // illegal type // ......................................................................... case TRI_SHAPE_ILLEGAL: { switch (rightType) { case TRI_SHAPE_ILLEGAL: { return 0; } case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_ILLEGAL // ......................................................................... // nullptr // ......................................................................... case TRI_SHAPE_NULL: { switch (rightType) { case TRI_SHAPE_ILLEGAL: { return 1; } case TRI_SHAPE_NULL: { return 0; } case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_NULL // ......................................................................... // BOOLEAN // ......................................................................... case TRI_SHAPE_BOOLEAN: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: { return 1; } case TRI_SHAPE_BOOLEAN: { // check which is false and which is true! if (*((TRI_shape_boolean_t*)(left._data.data)) == *((TRI_shape_boolean_t*)(right._data.data))) { return 0; } if (*((TRI_shape_boolean_t*)(left._data.data)) < *((TRI_shape_boolean_t*)(right._data.data))) { return -1; } return 1; } case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_BOOLEAN // ......................................................................... // NUMBER // ......................................................................... case TRI_SHAPE_NUMBER: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: { return 1; } case TRI_SHAPE_NUMBER: { // compare the numbers if (*((TRI_shape_number_t*)(left._data.data)) == *((TRI_shape_number_t*)(right._data.data))) { return 0; } if (*((TRI_shape_number_t*)(left._data.data)) < *((TRI_shape_number_t*)(right._data.data))) { return -1; } return 1; } case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_NUMBER // ......................................................................... // STRING // ......................................................................... case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: { return 1; } case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { char* leftString; char* rightString; size_t leftLength; size_t rightLength; // compare strings // extract the strings if (leftType == TRI_SHAPE_SHORT_STRING) { leftString = (char*)(sizeof(TRI_shape_length_short_string_t) + left._data.data); leftLength = (size_t) * ((TRI_shape_length_short_string_t*)left._data.data) - 1; } else { leftString = (char*)(sizeof(TRI_shape_length_long_string_t) + left._data.data); leftLength = (size_t) * ((TRI_shape_length_long_string_t*)left._data.data) - 1; } if (rightType == TRI_SHAPE_SHORT_STRING) { rightString = (char*)(sizeof(TRI_shape_length_short_string_t) + right._data.data); rightLength = (size_t) * ((TRI_shape_length_short_string_t*)right._data.data) - 1; } else { rightString = (char*)(sizeof(TRI_shape_length_long_string_t) + right._data.data); rightLength = (size_t) * ((TRI_shape_length_long_string_t*)right._data.data) - 1; } return TRI_compare_utf8(leftString, leftLength, rightString, rightLength); } case TRI_SHAPE_ARRAY: case TRI_SHAPE_LIST: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_LONG/SHORT_STRING // ......................................................................... // HOMOGENEOUS LIST // ......................................................................... case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: { return 1; } case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { // unfortunately recursion: check the types of all the entries size_t leftListLength = *((TRI_shape_length_list_t*)left._data.data); size_t rightListLength = *((TRI_shape_length_list_t*)right._data.data); size_t listLength; // determine the smallest list if (leftListLength > rightListLength) { listLength = rightListLength; } else { listLength = leftListLength; } for (size_t j = 0; j < listLength; ++j) { if (leftType == TRI_SHAPE_HOMOGENEOUS_LIST) { TRI_AtHomogeneousListShapedJson( (const TRI_homogeneous_list_shape_t*)(leftShape), &left, j, &leftElement); } else if (leftType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { TRI_AtHomogeneousSizedListShapedJson( (const TRI_homogeneous_sized_list_shape_t*)(leftShape), &left, j, &leftElement); } else { TRI_AtListShapedJson((const TRI_list_shape_t*)(leftShape), &left, j, &leftElement); } if (rightType == TRI_SHAPE_HOMOGENEOUS_LIST) { TRI_AtHomogeneousListShapedJson( (const TRI_homogeneous_list_shape_t*)(rightShape), &right, j, &rightElement); } else if (rightType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { TRI_AtHomogeneousSizedListShapedJson( (const TRI_homogeneous_sized_list_shape_t*)(rightShape), &right, j, &rightElement); } else { TRI_AtListShapedJson((const TRI_list_shape_t*)(rightShape), &right, j, &rightElement); } int result = TRI_CompareShapeTypes(nullptr, nullptr, &leftElement, leftShaper, nullptr, nullptr, &rightElement, rightShaper); if (result != 0) { return result; } } // up to listLength everything matches if (leftListLength < rightListLength) { return -1; } else if (leftListLength > rightListLength) { return 1; } return 0; } case TRI_SHAPE_ARRAY: { return -1; } } // end of switch (rightType) } // end of case TRI_SHAPE_LIST ... // ......................................................................... // ARRAY // ......................................................................... case TRI_SHAPE_ARRAY: { switch (rightType) { case TRI_SHAPE_ILLEGAL: case TRI_SHAPE_NULL: case TRI_SHAPE_BOOLEAN: case TRI_SHAPE_NUMBER: case TRI_SHAPE_SHORT_STRING: case TRI_SHAPE_LONG_STRING: case TRI_SHAPE_HOMOGENEOUS_LIST: case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: case TRI_SHAPE_LIST: { return 1; } case TRI_SHAPE_ARRAY: { // ................................................................... // We are comparing a left JSON array with another JSON array on the // right // ................................................................... // ................................................................... // generate the left and right lists sorted by attribute names // ................................................................... TRI_vector_t leftSorted; TRI_vector_t rightSorted; bool error = false; if (FillAttributesVector(&leftSorted, &left, leftShape, leftShaper) != TRI_ERROR_NO_ERROR) { error = true; } if (FillAttributesVector(&rightSorted, &right, rightShape, rightShaper) != TRI_ERROR_NO_ERROR) { error = true; } size_t const leftLength = TRI_LengthVector(&leftSorted); size_t const rightLength = TRI_LengthVector(&rightSorted); size_t const numElements = (leftLength < rightLength ? leftLength : rightLength); int result = 0; for (size_t i = 0; i < numElements; ++i) { attribute_entry_t const* l = static_cast( TRI_AtVector(&leftSorted, i)); attribute_entry_t const* r = static_cast( TRI_AtVector(&rightSorted, i)); // a binary comparison is sufficient here as we're only interested // in if the attribute names are // identical. the attribute names are from ShapedJson, so they have // been normalized already result = strcmp(l->_attribute, r->_attribute); // result = TRI_compare_utf8(l->_attribute, r->_attribute); if (result != 0) { break; } result = TRI_CompareShapeTypes(nullptr, nullptr, &l->_value, leftShaper, nullptr, nullptr, &r->_value, rightShaper); if (result != 0) { break; } } if (result == 0) { // ................................................................. // The comparisions above indicate that the shaped_json_arrays are // equal, // however one more check to determine if the number of elements in // the arrays // are equal. // ................................................................. if (leftLength < rightLength) { result = -1; } else if (leftLength > rightLength) { result = 1; } } // clean up DestroyAttributesVector(&leftSorted); DestroyAttributesVector(&rightSorted); if (error) { return -1; } return result; } } // end of switch (rightType) } // end of case TRI_SHAPE_ARRAY } // end of switch (leftType) TRI_ASSERT(false); return 0; // shut the vc++ up } void TRI_InspectShapedSub(TRI_shaped_sub_t const* element, char const* shapedJson, TRI_shaped_json_t& shaped) { if (element->_sid <= BasicShapes::TRI_SHAPE_SID_SHORT_STRING) { shaped._data.data = (char*)&element->_value._data; shaped._data.length = BasicShapes::TypeLengths[element->_sid]; } else { shaped._data.data = const_cast(shapedJson) + element->_value._position._offset; // ONLY IN INDEX shaped._data.length = element->_value._position._length; } } void TRI_InspectShapedSub(TRI_shaped_sub_t const* element, TRI_doc_mptr_t const* mptr, char const*& ptr, size_t& length) { if (element->_sid <= BasicShapes::TRI_SHAPE_SID_SHORT_STRING) { ptr = (char const*)&element->_value._data; length = BasicShapes::TypeLengths[element->_sid]; } else { ptr = mptr->getShapedJsonPtr() + element->_value._position._offset; // ONLY IN INDEX length = element->_value._position._length; } } void TRI_FillShapedSub(TRI_shaped_sub_t* element, TRI_shaped_json_t const* shapedObject, char const* ptr) { element->_sid = shapedObject->_sid; if (element->_sid <= BasicShapes::TRI_SHAPE_SID_SHORT_STRING) { if (shapedObject->_data.data != nullptr) { memcpy((char*)&element->_value._data, shapedObject->_data.data, BasicShapes::TypeLengths[element->_sid]); } } else { element->_value._position._length = shapedObject->_data.length; element->_value._position._offset = static_cast(((char const*)shapedObject->_data.data) - ptr); } }