1
0
Fork 0
arangodb/arangod/VocBase/VocShaper.cpp

1816 lines
63 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief json shaper used to compute the shape of a json object
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 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
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2006-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#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/logging.h"
#include "Basics/tri-strings.h"
#include "Basics/Utf8Helper.h"
#include "VocBase/document-collection.h"
#include "Wal/LogfileManager.h"
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @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<TRI_df_marker_t const*>(marker);
if (p != nullptr) {
if (p->_type == TRI_DF_MARKER_ATTRIBUTE) {
return reinterpret_cast<TRI_df_attribute_marker_t const*>(p)->_aid;
}
if (p->_type == TRI_WAL_MARKER_ATTRIBUTE) {
return reinterpret_cast<triagens::wal::attribute_marker_t const*>(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<TRI_df_marker_t const*>(marker);
if (p != nullptr) {
if (p->_type == TRI_DF_MARKER_ATTRIBUTE) {
return reinterpret_cast<char const*>(p) + sizeof(TRI_df_attribute_marker_t);
}
if (p->_type == TRI_WAL_MARKER_ATTRIBUTE) {
return reinterpret_cast<char const*>(p) + sizeof(triagens::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<TRI_shape_aid_t const*>(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<TRI_shape_aid_t const*>(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<TRI_shape_t const*>(element);
TRI_ASSERT(shape != nullptr);
char const* s = reinterpret_cast<char const*>(shape);
return TRI_FnvHashPointer(s + sizeof(TRI_shape_sid_t), static_cast<size_t>(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<TRI_shape_t const*>(left);
auto r = static_cast<TRI_shape_t const*>(right);
char const* ll = reinterpret_cast<char const*>(l);
char const* rr = reinterpret_cast<char const*>(r);
return (l->_size == r->_size)
&& memcmp(ll + sizeof(TRI_shape_sid_t),
rr + sizeof(TRI_shape_sid_t),
static_cast<size_t>(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<TRI_shape_sid_t const*>(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<TRI_shape_t const*>(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<TRI_shape_sid_t const*>(key);
auto shape = static_cast<TRI_shape_t const*>(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<TRI_shape_access_t const*>(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<TRI_shape_access_t const*>(left);
auto r = static_cast<TRI_shape_access_t const*>(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<TRI_shape_path_t const*>(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<TRI_shape_pid_t const*>(key);
auto e = static_cast<TRI_shape_path_t const*>(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<char const*>(key));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief hashs the attribute path
////////////////////////////////////////////////////////////////////////////////
static uint64_t HashNameElementAttributePath (TRI_associative_pointer_t*, void const* element) {
char const* e = static_cast<char const*>(element);
TRI_shape_path_t const* ee = static_cast<TRI_shape_path_t const*>(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<char const*>(key);
char const* e = static_cast<char const*>(element);
TRI_shape_path_t const* ee = static_cast<TRI_shape_path_t const*>(element);
return TRI_EqualString(k, e + sizeof(TRI_shape_path_t) + ee->_aidLength * sizeof(TRI_shape_aid_t));
}
// -----------------------------------------------------------------------------
// --SECTION-- VocShaper
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @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<TRI_shape_access_t*>(_accessors[i]._table[j]);
if (accessor != nullptr) {
TRI_FreeShapeAccessor(accessor);
}
}
TRI_DestroyAssociativePointer(&_accessors[i]);
}
}
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @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(_shapeIdsLock);
shape = static_cast<TRI_shape_t const*>(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(_attributeIdsLock);
auto element = static_cast<void const*>(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(_attributePathsByPidLock);
return static_cast<TRI_shape_path_t const*>(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);
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(_attributeNamesLock);
auto element = static_cast<void const*>(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 {
triagens::wal::AttributeMarker marker(document->_vocbase->_id, document->_info._cid, aid, std::string(name));
// lock the index and check that the element is still missing
{
MUTEX_LOCKER(_attributeCreateLock);
void const* p;
{
READ_LOCKER(_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);
}
// write marker into wal
triagens::wal::SlotInfoCopy slotInfo = triagens::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(_attributeIdsLock);
f = TRI_InsertKeyAssociativePointer(&_attributeIds, &aid, const_cast<void*>(slotInfo.mem), false);
}
TRI_ASSERT(f == nullptr);
// enter into the dictionaries
{
WRITE_LOCKER(_attributeNamesLock);
f = TRI_InsertKeyAssociativePointer(&_attributeNames, name, const_cast<void*>(slotInfo.mem), false);
}
TRI_ASSERT(f == nullptr);
}
return aid;
}
catch (triagens::basics::Exception const& ex) {
res = ex.code();
}
catch (...) {
res = TRI_ERROR_INTERNAL;
}
LOG_WARNING("could not save attribute marker in log: %s", 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(_shapeDictionaryLock);
found = static_cast<TRI_shape_t const*>(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 {
triagens::wal::ShapeMarker marker(document->_vocbase->_id, document->_info._cid, shape);
// lock the index and check the element is still missing
MUTEX_LOCKER(_shapeCreateLock);
{
READ_LOCKER(_shapeDictionaryLock);
found = static_cast<TRI_shape_t const*>(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);
}
// write marker into wal
triagens::wal::SlotInfoCopy slotInfo = triagens::wal::LogfileManager::instance()->allocateAndWrite(marker, false);
if (slotInfo.errorCode != TRI_ERROR_NO_ERROR) {
THROW_ARANGO_EXCEPTION(slotInfo.errorCode);
}
char const* m = static_cast<char const*>(slotInfo.mem) + sizeof(triagens::wal::shape_marker_t);
TRI_shape_t const* result = reinterpret_cast<TRI_shape_t const*>(m);
{
WRITE_LOCKER(_shapeIdsLock);
void* f = TRI_InsertKeyAssociativePointer(&_shapeIds, &sid, (void*) m, false);
if (f != nullptr) {
LOG_ERROR("logic error when inserting shape into id dictionary");
}
TRI_ASSERT(f == nullptr); // will abort here
}
{
WRITE_LOCKER(_shapeDictionaryLock);
void* f = TRI_InsertElementAssociativePointer(&_shapeDictionary, (void*) m, false);
if (f != nullptr) {
LOG_ERROR("logic error when inserting shape into dictionary");
}
TRI_ASSERT(f == nullptr); // will abort here
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, shape);
return result;
}
catch (triagens::basics::Exception const& ex) {
res = ex.code();
}
catch (...) {
res = TRI_ERROR_INTERNAL;
}
LOG_WARNING("could not save shape marker in log: %s", 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(_shapeCreateLock);
if (expectedOldPosition != nullptr) {
char* old = static_cast<char*>(expectedOldPosition);
void const* found;
{
READ_LOCKER(_shapeIdsLock);
found = TRI_LookupByKeyAssociativePointer(&_shapeIds, &l->_sid);
}
if (found != nullptr) {
if (old + sizeof(TRI_df_shape_marker_t) != found &&
old + sizeof(triagens::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(_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(_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(_attributeCreateLock);
if (expectedOldPosition != nullptr) {
void const* found;
{
READ_LOCKER(_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(_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(_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<char const*>(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(triagens::wal::shape_marker_t);
}
else {
return TRI_ERROR_INTERNAL;
}
TRI_shape_t* l = (TRI_shape_t*) p;
LOG_TRACE("found shape %lu", (unsigned long) l->_sid);
MUTEX_LOCKER(_shapeCreateLock);
void* f;
{
WRITE_LOCKER(_shapeDictionaryLock);
f = TRI_InsertElementAssociativePointer(&_shapeDictionary, l, false);
}
if (warnIfDuplicate && f != nullptr) {
char const* name = _collection->_info._name;
#ifdef TRI_ENABLE_MAINTAINER_MODE
LOG_ERROR("found duplicate shape in collection '%s'", name);
TRI_ASSERT(false);
#else
LOG_TRACE("found duplicate shape in collection '%s'", name);
#endif
}
{
WRITE_LOCKER(_shapeIdsLock);
f = TRI_InsertKeyAssociativePointer(&_shapeIds, &l->_sid, l, false);
}
if (warnIfDuplicate && f != nullptr) {
char const* name = _collection->_info._name;
#ifdef TRI_ENABLE_MAINTAINER_MODE
LOG_ERROR("found duplicate shape in collection '%s'", name);
TRI_ASSERT(false);
#else
LOG_TRACE("found duplicate shape in collection '%s'", name);
#endif
}
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<TRI_df_attribute_marker_t const*>(marker)->_aid;
}
else if (marker->_type == TRI_WAL_MARKER_ATTRIBUTE) {
name = ((char*) marker) + sizeof(triagens::wal::attribute_marker_t);
aid = reinterpret_cast<triagens::wal::attribute_marker_t const*>(marker)->_attributeId;
}
else {
return TRI_ERROR_INTERNAL;
}
TRI_ASSERT(aid != 0);
LOG_TRACE("found attribute '%s', aid: %lu", name, (unsigned long) aid);
// remove an existing temporary attribute if present
MUTEX_LOCKER(_attributeCreateLock);
void* found;
{
WRITE_LOCKER(_attributeNamesLock);
found = TRI_InsertKeyAssociativePointer(&_attributeNames, name, (void*) marker, false);
}
if (warnIfDuplicate && found != nullptr) {
char const* cname = _collection->_info._name;
#ifdef TRI_ENABLE_MAINTAINER_MODE
LOG_ERROR("found duplicate attribute name '%s' in collection '%s'", name, cname);
#else
LOG_TRACE("found duplicate attribute name '%s' in collection '%s'", name, cname);
#endif
}
{
WRITE_LOCKER(_attributeIdsLock);
found = TRI_InsertKeyAssociativePointer(&_attributeIds, &aid, (void*) marker, false);
}
if (warnIfDuplicate && found != nullptr) {
char const* cname = _collection->_info._name;
#ifdef TRI_ENABLE_MAINTAINER_MODE
LOG_ERROR("found duplicate attribute id '%llu' in collection '%s'", (unsigned long long) aid, cname);
#else
LOG_TRACE("found duplicate attribute id '%llu' in collection '%s'", (unsigned long long) aid, cname);
#endif
}
// no lock is necessary here as we are the only users of the shaper at this time
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<size_t>(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(_accessorLock[i]);
found = static_cast<TRI_shape_access_t const*>(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(_accessorLock[i]);
found = static_cast<TRI_shape_access_t const*>(TRI_InsertElementAssociativePointer(&_accessors[i], const_cast<void*>(static_cast<void const*>(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<TRI_shape_access_t const*>(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 %llu and path %llu",
(unsigned long long) document->_sid,
(unsigned long long) pid);
#endif
return false;
}
if (accessor->_resultSid == TRI_SHAPE_ILLEGAL) {
#ifdef TRI_ENABLE_MAINTAINER_MODE
LOG_TRACE("expecting any object for path %llu, got nothing",
(unsigned long long) pid);
#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 %llu, got unknown shape id %llu",
(unsigned long long) pid,
(unsigned long long) 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 %llu for path %llu, got sid %llu",
(unsigned long long) sid,
(unsigned long long) pid,
(unsigned long long) 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 %llu and path %llu",
(unsigned long long) document->_sid,
(unsigned long long) pid);
#endif
return false;
}
return true;
}
// -----------------------------------------------------------------------------
// --SECTION-- private methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @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(_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(_attributePathsCreateLock);
// if the element appeared, return the pid
{
READ_LOCKER(_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_shape_aid_t*>(TRI_Allocate(_memoryZone, len * sizeof(TRI_shape_aid_t), false));
if (aids == nullptr) {
LOG_ERROR("out of memory in shaper");
return nullptr;
}
buffer = ptr = TRI_DuplicateString2Z(_memoryZone, name, len);
if (buffer == nullptr) {
TRI_Free(_memoryZone, aids);
LOG_ERROR("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_shape_path_t*>(TRI_Allocate(_memoryZone, total, false));
if (result == nullptr) {
TRI_Free(_memoryZone, aids);
LOG_ERROR("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);
{
WRITE_LOCKER(_attributePathsByNameLock);
void const* f = TRI_InsertKeyAssociativePointer(&_attributePathsByName, name, result, false);
if (f != nullptr) {
LOG_WARNING("duplicate shape path %lu", (unsigned long) result->_pid);
}
TRI_ASSERT(f == nullptr); // will abort here
}
{
WRITE_LOCKER(_attributePathsByPidLock);
void const* f = TRI_InsertKeyAssociativePointer(&_attributePathsByPid, &result->_pid, result, false);
if (f != nullptr) {
LOG_WARNING("duplicate shape path %lu", (unsigned long)result->_pid);
}
TRI_ASSERT(f == nullptr); // will abort here
}
// return pid
return result;
}
// -----------------------------------------------------------------------------
// --SECTION-- non-class functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @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<attribute_entry_t const*>(lhs);
auto r = static_cast<attribute_entry_t const*>(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_DuplicateStringZ(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_DuplicateStringZ(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<attribute_entry_t*>(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) {
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) {
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) {
// identical collection and shape
rightShape = leftShape;
}
else {
// different shapes
rightShape = rightShaper->lookupShapeId(right._sid);
}
if (leftShape == nullptr || rightShape == nullptr) {
LOG_ERROR("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<attribute_entry_t const*>(TRI_AtVector(&leftSorted, i));
attribute_entry_t const* r = static_cast<attribute_entry_t const*>(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<char*>(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) {
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<uint32_t>(((char const*) shapedObject->_data.data) - ptr);
}
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: