//////////////////////////////////////////////////////////////////////////////// /// @brief SELECT result data structures and functionality /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 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 triAGENS GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2012, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "VocBase/query-result.h" #include "VocBase/query-base.h" //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief Initial size for result index (in number of rows) /// /// The index buffer will get an initial pre-allocation of this amount of rows /// to save multiple calls to malloc() for the first few additions of rows. /// re-allocation is postponed until we have collected a few rows already. /// This will save realloc overhead for smaller result sets. //////////////////////////////////////////////////////////////////////////////// #define INDEX_INIT_SIZE 32 //////////////////////////////////////////////////////////////////////////////// /// @brief Initial size for result data storage (in number of bytes) /// /// The result data buffer will get an initial pre-allocation of this amount of /// bytes to save multiple calls to malloc() for the first few additions of /// results. re-allocation is postponed until we have collected a few rows /// already. This will save realloc overhead for smaller result sets. //////////////////////////////////////////////////////////////////////////////// #define RESULT_INIT_SIZE 128 //////////////////////////////////////////////////////////////////////////////// /// @brief Growth factor for index memory allocation /// /// For each re-allocation, the memory size will be increased by at least this /// factor. //////////////////////////////////////////////////////////////////////////////// #define INDEX_GROWTH_FACTOR 1.5 //////////////////////////////////////////////////////////////////////////////// /// @brief Growth factor for result data memory allocation /// /// For each re-allocation, the memory size will be increased by at least this /// factor. //////////////////////////////////////////////////////////////////////////////// #define RESULT_GROWTH_FACTOR 1.5 //////////////////////////////////////////////////////////////////////////////// /// @brief Free memory allocated for dataparts //////////////////////////////////////////////////////////////////////////////// static void FreeDataPart (TRI_select_datapart_t* datapart) { if (datapart->_alias) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, datapart->_alias); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, datapart); } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a new select datapart instance //////////////////////////////////////////////////////////////////////////////// TRI_select_datapart_t* TRI_CreateDataPart(const char* alias, const TRI_doc_collection_t* collection, const TRI_select_part_e type, const size_t extraDataSize, const bool mustMaterializeSelect, const bool mustMaterializeOrder) { TRI_select_datapart_t* datapart; if (extraDataSize) { assert(!collection); } if (collection) { assert(extraDataSize == 0); } datapart = (TRI_select_datapart_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_select_datapart_t), false); if (!datapart) { return NULL; } datapart->_alias = TRI_DuplicateString(alias); datapart->_collection = (TRI_doc_collection_t*) collection; datapart->_type = type; datapart->_extraDataSize = extraDataSize; datapart->_mustMaterialize._select = mustMaterializeSelect; datapart->_mustMaterialize._order = mustMaterializeOrder; datapart->free = FreeDataPart; return datapart; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get document pointer for a specific row //////////////////////////////////////////////////////////////////////////////// static TRI_sr_documents_t* GetSelectResult (const TRI_select_result_t* result, const TRI_select_size_t rowNum) { TRI_sr_index_t* docPtr = (TRI_sr_index_t*) result->_index._start; return (TRI_sr_documents_t*) *(docPtr + rowNum); } //////////////////////////////////////////////////////////////////////////////// /// @brief Get pointer to document index start //////////////////////////////////////////////////////////////////////////////// static TRI_sr_index_t* FirstSelectResult (const TRI_select_result_t* result) { return result->_index._start; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get pointer to document index end //////////////////////////////////////////////////////////////////////////////// static TRI_sr_index_t* LastSelectResult (const TRI_select_result_t* result) { TRI_sr_index_t* docPtr = (TRI_sr_index_t*) result->_index._start; return (TRI_sr_index_t*) (docPtr + result->_index._numUsed); } //////////////////////////////////////////////////////////////////////////////// /// @brief Free memory allocated for select result //////////////////////////////////////////////////////////////////////////////// static void FreeSelectResult (TRI_select_result_t* result) { TRI_select_datapart_t* datapart; size_t i; if (result->_index._start) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, result->_index._start); } if (result->_documents._start) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, result->_documents._start); } for (i = 0; i < result->_dataParts->_length; i++) { datapart = (TRI_select_datapart_t*) result->_dataParts->_buffer[i]; datapart->free(datapart); } TRI_DestroyVectorPointer(result->_dataParts); TRI_Free(TRI_UNKNOWN_MEM_ZONE, result->_dataParts); TRI_Free(TRI_UNKNOWN_MEM_ZONE, result); } //////////////////////////////////////////////////////////////////////////////// /// @brief Initialise a select result /// /// This will also pre-allocate initial memory for the result set index and data //////////////////////////////////////////////////////////////////////////////// static void InitSelectResult (TRI_select_result_t* result, TRI_vector_pointer_t* dataparts) { assert(INDEX_INIT_SIZE > 0); assert(INDEX_GROWTH_FACTOR > 1.0); assert(RESULT_INIT_SIZE > 0); assert(RESULT_GROWTH_FACTOR > 1.0); result->_index._numAllocated = 0; result->_index._numUsed = 0; result->_index._start = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, INDEX_INIT_SIZE * sizeof(TRI_sr_index_t), false); result->_index._current = result->_index._start; if (result->_index._start) { result->_index._numAllocated = INDEX_INIT_SIZE; } result->_documents._bytesAllocated = 0; result->_documents._bytesUsed = 0; result->_documents._start = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, RESULT_INIT_SIZE, false); result->_documents._current = result->_documents._start; if (result->_documents._start) { result->_documents._bytesAllocated = RESULT_INIT_SIZE; } result->_dataParts = dataparts; result->_numRows = 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief Increase storage size for select result index //////////////////////////////////////////////////////////////////////////////// static bool IncreaseIndexStorageSelectResult (TRI_select_result_t* result, const size_t numNeeded) { TRI_sr_index_t* start; size_t newSize; // Extend by at least INDEX_GROWTH_FACTOR to save the cost of at least some // reallocations newSize = (size_t) result->_index._numAllocated + numNeeded; if (newSize < (size_t) (result->_index._numAllocated * INDEX_GROWTH_FACTOR)) { newSize = (size_t) (result->_index._numAllocated * INDEX_GROWTH_FACTOR); } assert(newSize > result->_index._numAllocated); start = TRI_Reallocate(TRI_UNKNOWN_MEM_ZONE, result->_index._start, newSize * sizeof(TRI_sr_index_t)); if (!start) { return false; } result->_index._numAllocated = newSize; // number of entries allocated // Index start pointer might have been moved by realloc, so save the new // position and calculate the end position result->_index._start = start; result->_index._current = ((TRI_sr_index_t*) start) + result->_index._numUsed; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Increase storage size for select documents data //////////////////////////////////////////////////////////////////////////////// static bool IncreaseDocumentsStorageSelectResult (TRI_select_result_t* result, const size_t bytesNeeded) { TRI_sr_documents_t* start; TRI_sr_index_t* indexStart; TRI_sr_index_t* indexEnd; TRI_sr_index_t value; size_t diff; size_t newSize; // Extend by at least RESULT_GROWTH_FACTOR to save the cost of at least some // reallocations newSize = (size_t) result->_documents._bytesAllocated + bytesNeeded; if (newSize < (size_t) (result->_documents._bytesAllocated * RESULT_GROWTH_FACTOR)) { newSize = (size_t) (result->_documents._bytesAllocated * RESULT_GROWTH_FACTOR); } assert(newSize > result->_documents._bytesAllocated); start = (TRI_sr_documents_t*) TRI_Reallocate(TRI_UNKNOWN_MEM_ZONE, result->_documents._start, newSize); if (!start) { return false; } // calc movement diff = start - (TRI_sr_documents_t*) result->_documents._start; // realloc might move the data. if it does, we need to adjust the index as well if ((start != result->_documents._start) && (result->_documents._start != 0)) { // data was moved, now adjust entries in index indexStart = (TRI_sr_index_t*) result->_index._start; indexEnd = (TRI_sr_index_t*) result->_index._current; while (indexStart < indexEnd) { value = *indexStart; *indexStart++ = (TRI_sr_index_t) (((TRI_sr_index_t*) value) + diff); } } result->_documents._bytesAllocated = newSize; result->_documents._start = start; result->_documents._current = result->_documents._current + diff; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get the required storage size for a row result of a join - DEPRECATED /// /// A result row of a join might contain data from multiple collections. Results /// might also be single documents or multiple documents, depending on the join /// type. /// This function will calculate the total required size to store all documents /// of all collections of the row result. //////////////////////////////////////////////////////////////////////////////// static size_t GetJoinDocumentSizeX (const TRI_select_join_t* join) { TRI_join_part_t* part; size_t i, n, total; total = 0; for (i = 0; i < join->_parts._length; i++) { part = (TRI_join_part_t*) join->_parts._buffer[i]; if (part->_type == JOIN_TYPE_LIST) { n = part->_listDocuments._length; // adjust for extra data total += part->_extraData._size * n; } else { n = 1; } // number of documents total += sizeof(TRI_select_size_t); // document pointers total += (size_t) (sizeof(TRI_sr_documents_t) * n); // adjust for extra data if (part->_extraData._size) { total += sizeof(TRI_select_size_t); total += part->_extraData._size; } } return total; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get the required storage size for a row result of a join /// /// A result row of a join might contain data from multiple collections. Results /// might also be single documents or multiple documents, depending on the join /// type. /// This function will calculate the total required size to store all documents /// of all collections of the row result. //////////////////////////////////////////////////////////////////////////////// static size_t GetJoinDocumentSize (const TRI_query_instance_t* const instance) { size_t i, total; total = 0; for (i = 0; i < instance->_join._length; i++) { TRI_join_part_t* part = (TRI_join_part_t*) instance->_join._buffer[i]; size_t n; if (!part->_mustMaterialize._select && !part->_mustMaterialize._order) { // no need to materialize this part continue; } if (part->_type == JOIN_TYPE_LIST) { n = part->_listDocuments._length; // adjust for extra data total += part->_extraData._size * n; } else { n = 1; } // number of documents total += sizeof(TRI_select_size_t); // document pointers total += (size_t) (sizeof(TRI_sr_documents_t) * n); // adjust for extra data if (part->_extraData._size) { total += sizeof(TRI_select_size_t); total += part->_extraData._size; } } return total; } //////////////////////////////////////////////////////////////////////////////// /// @brief Add documents from a join to the result set - DEPRECATED //////////////////////////////////////////////////////////////////////////////// bool TRI_AddJoinSelectResultX (TRI_select_result_t* result, TRI_select_join_t* join) { TRI_sr_index_t* indexPtr; TRI_sr_documents_t* docPtr; TRI_select_size_t* numPtr; TRI_join_part_t* part; TRI_doc_mptr_t* document; size_t numNeeded; size_t bytesNeeded; size_t i, j; // need space for one pointer numNeeded = 1; if (result->_index._numUsed + numNeeded > result->_index._numAllocated) { if (!IncreaseIndexStorageSelectResult(result, numNeeded)) { return false; } } bytesNeeded = GetJoinDocumentSizeX(join); if (result->_documents._bytesUsed + bytesNeeded > result->_documents._bytesAllocated) { if (!IncreaseDocumentsStorageSelectResult(result, bytesNeeded)) { return false; } } // store pointer to document in index docPtr = result->_documents._current; indexPtr = (TRI_sr_index_t*) result->_index._current; *indexPtr++ = (TRI_sr_index_t) docPtr; result->_index._current = (TRI_sr_index_t*) indexPtr; result->_index._numUsed++; // store document data numPtr = (TRI_select_size_t*) docPtr; for (i = 0; i < join->_parts._length; i++) { part = (TRI_join_part_t*) join->_parts._buffer[i]; if (part->_type == JOIN_TYPE_LIST) { // multiple documents *numPtr++ = part->_listDocuments._length; docPtr = (TRI_sr_documents_t*) numPtr; for (j = 0; j < part->_listDocuments._length; j++) { document = (TRI_doc_mptr_t*) part->_listDocuments._buffer[j]; *docPtr++ = (TRI_sr_documents_t) document->_data; } if (part->_extraData._size) { // copy extra data assert(part->_listDocuments._length == part->_extraData._listValues._length); numPtr = (TRI_select_size_t*) docPtr; *numPtr++ = part->_listDocuments._length; docPtr = (TRI_sr_documents_t*) numPtr; for (j = 0; j < part->_extraData._listValues._length; j++) { memcpy(docPtr, part->_extraData._listValues._buffer[j], part->_extraData._size); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraData._size); } } } else { // single document *numPtr++ = 1; docPtr = (TRI_sr_documents_t*) numPtr; document = (TRI_doc_mptr_t*) part->_singleDocument; if (document) { *docPtr++ = (TRI_sr_documents_t) document->_data; } else { // document is null *docPtr++ = 0; } if (part->_extraData._size) { // copy extra data numPtr = (TRI_select_size_t*) docPtr; *numPtr++ = 1; docPtr = (TRI_sr_documents_t*) numPtr; memcpy(docPtr, part->_extraData._singleValue, part->_extraData._size); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraData._size); } } numPtr = (TRI_select_size_t*) docPtr; } result->_documents._bytesUsed += bytesNeeded; result->_documents._current = (TRI_sr_documents_t*) numPtr; result->_numRows++; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Add documents from a join to the result set //////////////////////////////////////////////////////////////////////////////// bool TRI_AddJoinSelectResult (TRI_query_instance_t* const instance, TRI_select_result_t* result) { TRI_sr_index_t* indexPtr; TRI_sr_documents_t* docPtr; TRI_select_size_t* numPtr; TRI_doc_mptr_t* document; size_t numNeeded; size_t bytesNeeded; size_t i, j; // need space for one pointer numNeeded = 1; if (result->_index._numUsed + numNeeded > result->_index._numAllocated) { if (!IncreaseIndexStorageSelectResult(result, numNeeded)) { return false; } } bytesNeeded = GetJoinDocumentSize(instance); if (result->_documents._bytesUsed + bytesNeeded > result->_documents._bytesAllocated) { if (!IncreaseDocumentsStorageSelectResult(result, bytesNeeded)) { return false; } } // store pointer to document in index docPtr = result->_documents._current; indexPtr = (TRI_sr_index_t*) result->_index._current; *indexPtr++ = (TRI_sr_index_t) docPtr; result->_index._current = (TRI_sr_index_t*) indexPtr; result->_index._numUsed++; // store document data numPtr = (TRI_select_size_t*) docPtr; for (i = 0; i < instance->_join._length; i++) { TRI_join_part_t* part = (TRI_join_part_t*) instance->_join._buffer[i]; if (!part->_mustMaterialize._select && !part->_mustMaterialize._order) { // no need to materialize this part continue; } if (part->_type == JOIN_TYPE_LIST) { // multiple documents *numPtr++ = part->_listDocuments._length; docPtr = (TRI_sr_documents_t*) numPtr; for (j = 0; j < part->_listDocuments._length; j++) { document = (TRI_doc_mptr_t*) part->_listDocuments._buffer[j]; *docPtr++ = (TRI_sr_documents_t) document->_data; } if (part->_extraData._size) { // copy extra data assert(part->_listDocuments._length == part->_extraData._listValues._length); numPtr = (TRI_select_size_t*) docPtr; *numPtr++ = part->_listDocuments._length; docPtr = (TRI_sr_documents_t*) numPtr; for (j = 0; j < part->_extraData._listValues._length; j++) { memcpy(docPtr, part->_extraData._listValues._buffer[j], part->_extraData._size); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraData._size); } } } else { // single document *numPtr++ = 1; docPtr = (TRI_sr_documents_t*) numPtr; document = (TRI_doc_mptr_t*) part->_singleDocument; if (document) { *docPtr++ = (TRI_sr_documents_t) document->_data; } else { // document is null *docPtr++ = 0; } if (part->_extraData._size) { // copy extra data numPtr = (TRI_select_size_t*) docPtr; *numPtr++ = 1; docPtr = (TRI_sr_documents_t*) numPtr; memcpy(docPtr, part->_extraData._singleValue, part->_extraData._size); docPtr = (TRI_sr_documents_t*) ((uint8_t*) docPtr + part->_extraData._size); } } numPtr = (TRI_select_size_t*) docPtr; } result->_documents._bytesUsed += bytesNeeded; result->_documents._current = (TRI_sr_documents_t*) numPtr; result->_numRows++; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Slice a select result (apply skip/limit) //////////////////////////////////////////////////////////////////////////////// bool TRI_SliceSelectResult (TRI_select_result_t* result, const TRI_voc_size_t skip, const TRI_voc_ssize_t limit) { TRI_sr_index_t* oldIndex; TRI_sr_index_t* newIndex; size_t newSize; if (result->_numRows == 0 || !result->_index._start) { // no need to do anything return true; } // allocate new space for document index newSize = (size_t) limit; if (limit == 0) { newSize = 1; } newIndex = (TRI_sr_index_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, newSize * sizeof(TRI_sr_index_t), false); if (!newIndex) { // error: memory allocation failed return false; } oldIndex = (TRI_sr_index_t*) result->_index._start; memcpy(newIndex, oldIndex + skip, limit * sizeof(TRI_sr_index_t)); TRI_Free(TRI_UNKNOWN_MEM_ZONE, oldIndex); result->_numRows = limit; result->_index._start = newIndex; result->_index._current = newIndex + 1; result->_index._numAllocated = newSize; result->_index._numUsed = (size_t) limit; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a new select result //////////////////////////////////////////////////////////////////////////////// TRI_select_result_t* TRI_CreateSelectResult (TRI_vector_pointer_t *dataparts) { TRI_select_result_t* result; result = (TRI_select_result_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_select_result_t), false); if (!result) { return NULL; } InitSelectResult(result, dataparts); result->getAt = GetSelectResult; result->first = FirstSelectResult; result->last = LastSelectResult; result->free = FreeSelectResult; return result; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: